]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/function/DistanceTimeLimitFunction.java
Version 19, May 2018
[GpsPrune.git] / tim / prune / function / DistanceTimeLimitFunction.java
diff --git a/tim/prune/function/DistanceTimeLimitFunction.java b/tim/prune/function/DistanceTimeLimitFunction.java
new file mode 100644 (file)
index 0000000..eaa7b7d
--- /dev/null
@@ -0,0 +1,275 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.Distance;
+import tim.prune.data.Field;
+import tim.prune.data.TimeDifference;
+import tim.prune.data.Unit;
+import tim.prune.data.UnitSetLibrary;
+import tim.prune.gui.WholeNumberField;
+
+/**
+ * Superclass for functions which act on a defined
+ * distance limit or time limit
+ */
+public abstract class DistanceTimeLimitFunction extends GenericFunction
+{
+       /** Dialog */
+       protected JDialog _dialog = null;
+       /** Radio buttons for splitting by distance and time */
+       private JRadioButton _distLimitRadio = null, _timeLimitRadio = null;
+       /** Dropdown for selecting distance units */
+       private JComboBox<String> _distUnitsDropdown = null;
+       /** Text field for entering distance */
+       private WholeNumberField _distanceField = null;
+       /** Text fields for entering distance */
+       private WholeNumberField _limitHourField = null, _limitMinField = null;
+       /** Ok and cancel buttons */
+       private JButton _okButton = null;
+       private JButton _cancelButton = null;
+
+
+       /**
+        * React to item changes and key presses
+        */
+       private abstract class ChangeListener extends KeyAdapter implements ItemListener
+       {
+               /** Method to be implemented */
+               public abstract void optionsChanged();
+
+               /** Item changed in ItemListener */
+               public void itemStateChanged(ItemEvent arg0) {
+                       optionsChanged();
+               }
+
+               /** Key released in KeyListener */
+               public void keyReleased(KeyEvent arg0) {
+                       optionsChanged();
+               }
+       }
+
+       /**
+        * Constructor
+        */
+       public DistanceTimeLimitFunction(App inApp) {
+               super(inApp);
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               enableOkButton();
+               // TODO: Maybe set distance units according to current Config setting?
+               final boolean hasTimestamps = _app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP);
+               _timeLimitRadio.setEnabled(hasTimestamps);
+               if (!hasTimestamps)
+               {
+                       _distLimitRadio.setSelected(true);
+               }
+               // set focus to Cancel button so that pressing "Esc" works
+               _cancelButton.requestFocus();
+               _dialog.setVisible(true);
+       }
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout(5, 5));
+
+               // Make radio buttons for three different options
+               _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit") + ": ");
+               _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + ": ");
+               ButtonGroup radioGroup = new ButtonGroup();
+               radioGroup.add(_distLimitRadio);
+               radioGroup.add(_timeLimitRadio);
+
+               // central panel for limits
+               JPanel limitsPanel = new JPanel();
+               limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS));
+               limitsPanel.add(Box.createVerticalStrut(8));
+               ChangeListener optionsChangedListener = new ChangeListener() {
+                       public void optionsChanged() {
+                               enableOkButton();
+                       }
+               };
+               // distance limits
+               JPanel distLimitPanel = new JPanel();
+               distLimitPanel.setLayout(new FlowLayout());
+               _distLimitRadio.setSelected(true);
+               _distLimitRadio.addItemListener(optionsChangedListener);
+               distLimitPanel.add(_distLimitRadio);
+               _distanceField = new WholeNumberField(3);
+               _distanceField.addKeyListener(optionsChangedListener);
+               distLimitPanel.add(_distanceField);
+               String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
+                       I18nManager.getText("units.miles")};
+               _distUnitsDropdown = new JComboBox<String>(distUnitsOptions);
+               _distUnitsDropdown.addItemListener(optionsChangedListener);
+               distLimitPanel.add(_distUnitsDropdown);
+               distLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               limitsPanel.add(distLimitPanel);
+
+               // time limit panel
+               JPanel timeLimitPanel = new JPanel();
+               timeLimitPanel.setLayout(new FlowLayout());
+               _timeLimitRadio.addItemListener(optionsChangedListener);
+               timeLimitPanel.add(_timeLimitRadio);
+               _limitHourField = new WholeNumberField(2);
+               _limitHourField.addKeyListener(optionsChangedListener);
+               timeLimitPanel.add(_limitHourField);
+               timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours")));
+               _limitMinField = new WholeNumberField(3);
+               _limitMinField.addKeyListener(optionsChangedListener);
+               timeLimitPanel.add(_limitMinField);
+               timeLimitPanel.add(new JLabel(I18nManager.getText("units.minutes")));
+               timeLimitPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               limitsPanel.add(timeLimitPanel);
+
+               dialogPanel.add(limitsPanel, BorderLayout.NORTH);
+
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               // OK button
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               _okButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               performFunction();
+                       }
+               });
+               buttonPanel.add(_okButton);
+               // Cancel button
+               _cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               _cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _dialog.dispose();
+                       }
+               });
+               _cancelButton.addKeyListener(new KeyAdapter() {
+                       public void keyPressed(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+                       }
+               });
+               buttonPanel.add(_cancelButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return dialogPanel;
+       }
+
+       /**
+        * Enable or disable the OK button according to the inputs
+        */
+       private void enableOkButton()
+       {
+               boolean enabled = false;
+               if (_distLimitRadio.isSelected()) {
+                       enabled = _distanceField.getValue() > 0;
+               }
+               else if (_timeLimitRadio.isSelected()) {
+                       enabled = _limitHourField.getValue() > 0 || _limitMinField.getValue() > 0;
+               }
+               _okButton.setEnabled(enabled);
+
+               // Also enable/disable the other fields
+               _distanceField.setEnabled(_distLimitRadio.isSelected());
+               _distUnitsDropdown.setEnabled(_distLimitRadio.isSelected());
+               _limitHourField.setEnabled(_timeLimitRadio.isSelected());
+               _limitMinField.setEnabled(_timeLimitRadio.isSelected());
+       }
+
+       /**
+        * @return selected time limit in seconds, or 0
+        */
+       protected int getTimeLimitInSeconds()
+       {
+               if (_timeLimitRadio.isSelected()
+                       && (_limitHourField.getValue() > 0 || _limitMinField.getValue() > 0))
+               {
+                       int timeLimitSeconds = _limitHourField.getValue() * 60 * 60
+                               + _limitMinField.getValue() * 60;
+                       if (timeLimitSeconds > 0)
+                               return timeLimitSeconds;
+               }
+               return 0;
+       }
+
+       /**
+        * @return selected distance limit in radians, or 0.0
+        */
+       protected double getDistanceLimitRadians()
+       {
+               if (_distLimitRadio.isSelected()
+                       && _distanceField.getValue() > 0)
+               {
+                       final Unit[] distUnits = {UnitSetLibrary.UNITS_KILOMETRES,
+                               UnitSetLibrary.UNITS_METRES, UnitSetLibrary.UNITS_MILES};
+                       Unit distUnit = distUnits[_distUnitsDropdown.getSelectedIndex()];
+                       double distLimitRadians = Distance.convertDistanceToRadians(_distanceField.getValue(), distUnit);
+                       return distLimitRadians;
+               }
+               return 0.0;
+       }
+
+       /**
+        * The dialog has been completed and OK pressed, so do the corresponding function
+        */
+       protected abstract void performFunction();
+
+       /**
+        * Create a description for the given multiple of the configured limit
+        * @param inMultiple multiple of selected limit
+        * @return language-specific String description for waypoint names
+        */
+       protected String createLimitDescription(int inMultiple)
+       {
+               if (_distLimitRadio.isSelected()
+                       && _distanceField.getValue() > 0)
+               {
+                       final int distLimit = inMultiple * _distanceField.getValue();
+                       final String[] distUnits = {"kilometres", "metres", "miles"};
+                       final String unitKey = "units." + distUnits[_distUnitsDropdown.getSelectedIndex()] + ".short";
+                       return "" + distLimit + " " + I18nManager.getText(unitKey);
+               }
+               else if (_timeLimitRadio.isSelected())
+               {
+                       final long timeLimitSecs = (long) getTimeLimitInSeconds() * inMultiple;
+                       return new TimeDifference(timeLimitSecs).getDescription();
+               }
+               return null;
+       }
+}