1 package tim.prune.function;
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.FlowLayout;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.awt.event.ItemEvent;
9 import java.awt.event.ItemListener;
10 import java.awt.event.KeyAdapter;
11 import java.awt.event.KeyEvent;
13 import javax.swing.Box;
14 import javax.swing.BoxLayout;
15 import javax.swing.ButtonGroup;
16 import javax.swing.JButton;
17 import javax.swing.JComboBox;
18 import javax.swing.JDialog;
19 import javax.swing.JLabel;
20 import javax.swing.JPanel;
21 import javax.swing.JRadioButton;
24 import tim.prune.GenericFunction;
25 import tim.prune.I18nManager;
26 import tim.prune.data.Distance;
27 import tim.prune.data.Field;
28 import tim.prune.data.TimeDifference;
29 import tim.prune.data.Unit;
30 import tim.prune.data.UnitSetLibrary;
31 import tim.prune.gui.WholeNumberField;
34 * Superclass for functions which act on a defined
35 * distance limit or time limit
37 public abstract class DistanceTimeLimitFunction extends GenericFunction
40 protected JDialog _dialog = null;
41 /** Radio buttons for splitting by distance and time */
42 private JRadioButton _distLimitRadio = null, _timeLimitRadio = null;
43 /** Dropdown for selecting distance units */
44 private JComboBox<String> _distUnitsDropdown = null;
45 /** Text field for entering distance */
46 private WholeNumberField _distanceField = null;
47 /** Text fields for entering distance */
48 private WholeNumberField _limitHourField = null, _limitMinField = null;
49 /** Ok and cancel buttons */
50 private JButton _okButton = null;
51 private JButton _cancelButton = null;
55 * React to item changes and key presses
57 private abstract class ChangeListener extends KeyAdapter implements ItemListener
59 /** Method to be implemented */
60 public abstract void optionsChanged();
62 /** Item changed in ItemListener */
63 public void itemStateChanged(ItemEvent arg0) {
67 /** Key released in KeyListener */
68 public void keyReleased(KeyEvent arg0) {
76 public DistanceTimeLimitFunction(App inApp) {
87 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
88 _dialog.setLocationRelativeTo(_parentFrame);
89 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
90 _dialog.getContentPane().add(makeDialogComponents());
94 // TODO: Maybe set distance units according to current Config setting?
95 final boolean hasTimestamps = _app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP);
96 _timeLimitRadio.setEnabled(hasTimestamps);
99 _distLimitRadio.setSelected(true);
101 // set focus to Cancel button so that pressing "Esc" works
102 _cancelButton.requestFocus();
103 _dialog.setVisible(true);
107 * Create dialog components
108 * @return Panel containing all gui elements in dialog
110 private Component makeDialogComponents()
112 JPanel dialogPanel = new JPanel();
113 dialogPanel.setLayout(new BorderLayout(5, 5));
115 // Make radio buttons for three different options
116 _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": ");
117 _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": ");
118 ButtonGroup radioGroup = new ButtonGroup();
119 radioGroup.add(_distLimitRadio);
120 radioGroup.add(_timeLimitRadio);
122 // central panel for limits
123 JPanel limitsPanel = new JPanel();
124 limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS));
125 limitsPanel.add(Box.createVerticalStrut(8));
126 ChangeListener optionsChangedListener = new ChangeListener() {
127 public void optionsChanged() {
132 JPanel distLimitPanel = new JPanel();
133 distLimitPanel.setLayout(new FlowLayout());
134 _distLimitRadio.setSelected(true);
135 _distLimitRadio.addItemListener(optionsChangedListener);
136 distLimitPanel.add(_distLimitRadio);
137 _distanceField = new WholeNumberField(3);
138 _distanceField.addKeyListener(optionsChangedListener);
139 distLimitPanel.add(_distanceField);
140 String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
141 I18nManager.getText("units.miles")};
142 _distUnitsDropdown = new JComboBox<String>(distUnitsOptions);
143 _distUnitsDropdown.addItemListener(optionsChangedListener);
144 distLimitPanel.add(_distUnitsDropdown);
145 distLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
146 limitsPanel.add(distLimitPanel);
149 JPanel timeLimitPanel = new JPanel();
150 timeLimitPanel.setLayout(new FlowLayout());
151 _timeLimitRadio.addItemListener(optionsChangedListener);
152 timeLimitPanel.add(_timeLimitRadio);
153 _limitHourField = new WholeNumberField(2);
154 _limitHourField.addKeyListener(optionsChangedListener);
155 timeLimitPanel.add(_limitHourField);
156 timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours")));
157 _limitMinField = new WholeNumberField(3);
158 _limitMinField.addKeyListener(optionsChangedListener);
159 timeLimitPanel.add(_limitMinField);
160 timeLimitPanel.add(new JLabel(I18nManager.getText("units.minutes")));
161 timeLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
162 limitsPanel.add(timeLimitPanel);
164 dialogPanel.add(limitsPanel, BorderLayout.NORTH);
166 // button panel at bottom
167 JPanel buttonPanel = new JPanel();
168 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
170 _okButton = new JButton(I18nManager.getText("button.ok"));
171 _okButton.addActionListener(new ActionListener() {
172 public void actionPerformed(ActionEvent e) {
176 buttonPanel.add(_okButton);
178 _cancelButton = new JButton(I18nManager.getText("button.cancel"));
179 _cancelButton.addActionListener(new ActionListener() {
180 public void actionPerformed(ActionEvent e) {
184 _cancelButton.addKeyListener(new KeyAdapter() {
185 public void keyPressed(KeyEvent inE) {
186 if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
189 buttonPanel.add(_cancelButton);
190 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
195 * Enable or disable the OK button according to the inputs
197 private void enableOkButton()
199 boolean enabled = false;
200 if (_distLimitRadio.isSelected()) {
201 enabled = _distanceField.getValue() > 0;
203 else if (_timeLimitRadio.isSelected()) {
204 enabled = _limitHourField.getValue() > 0 || _limitMinField.getValue() > 0;
206 _okButton.setEnabled(enabled);
208 // Also enable/disable the other fields
209 _distanceField.setEnabled(_distLimitRadio.isSelected());
210 _distUnitsDropdown.setEnabled(_distLimitRadio.isSelected());
211 _limitHourField.setEnabled(_timeLimitRadio.isSelected());
212 _limitMinField.setEnabled(_timeLimitRadio.isSelected());
216 * @return selected time limit in seconds, or 0
218 protected int getTimeLimitInSeconds()
220 if (_timeLimitRadio.isSelected()
221 && (_limitHourField.getValue() > 0 || _limitMinField.getValue() > 0))
223 int timeLimitSeconds = _limitHourField.getValue() * 60 * 60
224 + _limitMinField.getValue() * 60;
225 if (timeLimitSeconds > 0)
226 return timeLimitSeconds;
232 * @return selected distance limit in radians, or 0.0
234 protected double getDistanceLimitRadians()
236 if (_distLimitRadio.isSelected()
237 && _distanceField.getValue() > 0)
239 final Unit[] distUnits = {UnitSetLibrary.UNITS_KILOMETRES,
240 UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_MILES};
241 Unit distUnit = distUnits[_distUnitsDropdown.getSelectedIndex()];
242 double distLimitRadians = Distance.convertDistanceToRadians(_distanceField.getValue(), distUnit);
243 return distLimitRadians;
249 * The dialog has been completed and OK pressed, so do the corresponding function
251 protected abstract void performFunction();
254 * Create a description for the given multiple of the configured limit
255 * @param inMultiple multiple of selected limit
256 * @return language-specific String description for waypoint names
258 protected String createLimitDescription(int inMultiple)
260 if (_distLimitRadio.isSelected()
261 && _distanceField.getValue() > 0)
263 final int distLimit = inMultiple * _distanceField.getValue();
264 final String[] distUnits = {"kilometres", "metres", "miles"};
265 final String unitKey = "units." + distUnits[_distUnitsDropdown.getSelectedIndex()] + ".short";
266 return "" + distLimit + " " + I18nManager.getText(unitKey);
268 else if (_timeLimitRadio.isSelected())
270 final long timeLimitSecs = (long) getTimeLimitInSeconds() * inMultiple;
271 return new TimeDifference(timeLimitSecs).getDescription();