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.waypointtype", point.getFieldValue(Field.WAYPT_TYPE));
244 // Speed can come from either timestamps and distances, or speed values in data
245 SpeedValue speedValue = new SpeedValue();
246 SpeedCalculator.calculateSpeed(inTrack, inPointIndex, speedValue);
247 UnitSet unitSet = Config.getUnitSet();
248 if (speedValue.isValid())
250 final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
251 String speed = DisplayUtils.roundedNumber(speedValue.getValue());
252 addTextPair(result, "fieldname.speed", speed, speedUnitsStr);
255 // Now do the vertical speed in the same way
256 SpeedCalculator.calculateVerticalSpeed(inTrack, inPointIndex, speedValue);
257 if (speedValue.isValid())
259 final String vSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
260 String speed = DisplayUtils.roundedNumber(speedValue.getValue());
261 addTextPair(result, "fieldname.verticalspeed", speed, vSpeedUnitsStr);
264 Photo currentPhoto = point.getPhoto();
265 if (currentPhoto != null)
267 addTextPair(result, "details.photofile", currentPhoto.getName());
268 addTextPair(result, "details.media.fullpath", currentPhoto.getFullPath());
271 AudioClip currentAudio = point.getAudio();
272 if (currentAudio != null)
274 addTextPair(result, "details.audio.file", currentAudio.getName());
275 addTextPair(result, "details.media.fullpath", currentAudio.getFullPath());
278 return result.toString();
282 * Make the range description text
283 * @param inStats stats object
284 * @param inMaxSpeed maximum speed info
285 * @return string describing range
287 private static String makeRangeDescription(RangeStatsWithGradients inStats, SpeedValue inMaxSpeed)
289 StringBuffer result = new StringBuffer();
290 addTextPair(result, "details.track.points", "" + inStats.getNumPoints());
291 addTextPair(result, "details.range.numsegments", "" + inStats.getNumSegments());
292 final boolean hasMultipleSegments = (inStats.getNumSegments() > 1);
294 UnitSet unitSet = Config.getUnitSet();
295 final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
296 if (inMaxSpeed.isValid())
298 final String maxSpeedStr = DisplayUtils.roundedNumber(inMaxSpeed.getValue()) + " " + speedUnitsStr;
299 addTextPair(result, "details.range.maxspeed", maxSpeedStr);
302 addHeading(result, "dialog.fullrangedetails.colsegments");
303 final Unit distUnit = Config.getUnitSet().getDistanceUnit();
304 final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
305 final double movingDist = inStats.getMovingDistance();
306 addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(movingDist),
308 long numSecs = inStats.getMovingDurationInSeconds();
309 addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(numSecs));
311 if (numSecs > 0 && movingDist > 0.0)
313 addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(movingDist/numSecs*3600.0),
315 addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (numSecs/movingDist)),
316 "/ " + distUnitsStr);
318 final Unit altUnit = unitSet.getAltitudeUnit();
319 final String altUnitsStr = I18nManager.getText(altUnit.getShortnameKey());
320 if (inStats.getMovingAltitudeRange().hasRange())
322 AltitudeRange altRange = inStats.getMovingAltitudeRange();
323 addTextPair(result, "fieldname.altitude", "" + altRange.getMinimum(altUnit) + altUnitsStr + " "
324 + I18nManager.getText("details.altitude.to") + " "
325 + altRange.getMaximum(altUnit) + altUnitsStr);
326 addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
327 addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
328 addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getMovingGradient()), "%");
331 final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
332 final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getMovingVerticalSpeed());
333 addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
337 if (hasMultipleSegments)
339 addHeading(result, "dialog.fullrangedetails.coltotal");
340 final double totalDist = inStats.getTotalDistance();
341 addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(totalDist), distUnitsStr);
342 long totalSecs = inStats.getTotalDurationInSeconds();
343 addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(totalSecs));
344 if (totalSecs > 0 && totalDist > 0.0)
346 addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(totalDist/totalSecs*3600.0),
348 addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (totalSecs/totalDist)),
349 "/ " + distUnitsStr);
351 if (inStats.getTotalAltitudeRange().hasRange())
353 AltitudeRange altRange = inStats.getTotalAltitudeRange();
354 addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
355 addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
356 addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getTotalGradient()), "%");
359 final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
360 final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getTotalVerticalSpeed());
361 addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
365 return result.toString();
369 * Add the label and value to the buffer
370 * @param inBuffer buffer to append to
371 * @param inLabelKey label key
372 * @param inValue value text
374 private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue)
376 addTextPair(inBuffer, inLabelKey, inValue, null);
380 * Add the label and value to the buffer
381 * @param inBuffer buffer to append to
382 * @param inLabelKey label key
383 * @param inValue value text
384 * @param inUnits optional units string
386 private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue, String inUnits)
388 if (inValue != null && !inValue.equals(""))
390 inBuffer.append(I18nManager.getText(inLabelKey));
391 inBuffer.append(": ");
392 inBuffer.append(inValue);
393 if (inUnits != null && !inUnits.equals(""))
395 inBuffer.append(' ');
396 inBuffer.append(inUnits);
398 inBuffer.append("\n");
403 * Add a heading to the buffer
404 * @param inBuffer buffer to append to
405 * @param inLabelKey key for heading
407 private static void addHeading(StringBuffer inBuffer, String inLabelKey)
409 final String heading = I18nManager.getText(inLabelKey);
410 inBuffer.append('\n').append(heading).append('\n');
411 for (int i=0; i<heading.length(); i++)
413 inBuffer.append('=');
415 inBuffer.append('\n');