package tim.prune.function;
+import java.util.Arrays;
+
import javax.swing.JOptionPane;
import tim.prune.App;
-import tim.prune.GenericFunction;
import tim.prune.I18nManager;
+import tim.prune.data.Checker;
+import tim.prune.data.DataPoint;
import tim.prune.data.Track;
+import tim.prune.data.sort.SortMode;
+import tim.prune.data.sort.WaypointComparer;
import tim.prune.undo.UndoRearrangeWaypoints;
/**
* Class to provide the function for rearranging waypoints
*/
-public class RearrangeWaypointsFunction extends GenericFunction
+public class RearrangeWaypointsFunction extends RearrangeFunction
{
- /** Enumeration for rearrange commands */
- public enum Rearrange
- {
- /** Rearrange all waypoints to start */
- TO_START,
- /** Rearrange all waypoints to end */
- TO_END,
- /** Rearrange each waypoint to nearest track point */
- TO_NEAREST
- }
-
/**
* Constructor
* @param inApp app object
*/
public RearrangeWaypointsFunction(App inApp)
{
- super(inApp);
+ super(inApp, true);
+ }
+
+ /** Get the name key */
+ public String getNameKey() {
+ return "function.rearrangewaypoints";
}
- /** Begin the rearrange (not needed) */
- public void begin() {
+ /** Get whether sorting by time is allowed or not */
+ protected boolean isSortByTimeAllowed() {
+ return Checker.haveWaypointsGotTimestamps(_app.getTrackInfo().getTrack());
}
- /** Get the name key (not needed) */
- public String getNameKey() {
- return null;
+ /** Get the description key */
+ public String getDescriptionKey() {
+ return "dialog.rearrangewaypoints.desc";
+ }
+
+ /** Sort by name key */
+ protected String getSortNameKey() {
+ return "sortbyname";
}
/**
- * Rearrange the waypoints into track order
- * @param inFunction nearest point, all to end or all to start
+ * Perform the rearrange and sort according to the radio buttons
*/
- public void rearrangeWaypoints(Rearrange inFunction)
+ protected void finish()
{
Track track = _app.getTrackInfo().getTrack();
+ // Figure out what is required from the radio buttons
+ Rearrange rearrangeOption = getRearrangeOption();
+ SortMode sortOption = getSortMode();
+
UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(track);
boolean success = false;
- if (inFunction == Rearrange.TO_START || inFunction == Rearrange.TO_END)
+ if (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END)
{
// Collect the waypoints to the start or end of the track
- success = track.collectWaypoints(inFunction == Rearrange.TO_START);
+ success = collectWaypoints(rearrangeOption, sortOption);
}
else
{
}
}
+
+ /**
+ * Do the collection and sorting of the waypoints
+ * @param inRearrangeOption beginning or end
+ * @param inSortOption optional sort criterion
+ * @return true on success
+ */
+ private boolean collectWaypoints(Rearrange inRearrangeOption, SortMode inSortOption)
+ {
+ // Check for mixed data, numbers of waypoints & nons
+ int numWaypoints = 0, numNonWaypoints = 0;
+ boolean wayAfterNon = false, nonAfterWay = false;
+ Track track = _app.getTrackInfo().getTrack();
+ final int numPoints = track.getNumPoints();
+ DataPoint[] waypoints = new DataPoint[numPoints];
+ DataPoint[] nonWaypoints = new DataPoint[numPoints];
+ DataPoint point = null;
+ for (int i=0; i<numPoints; i++)
+ {
+ point = track.getPoint(i);
+ if (point.isWaypoint())
+ {
+ waypoints[numWaypoints] = point;
+ numWaypoints++;
+ wayAfterNon |= (numNonWaypoints > 0);
+ }
+ else
+ {
+ nonWaypoints[numNonWaypoints] = point;
+ numNonWaypoints++;
+ nonAfterWay |= (numWaypoints > 0);
+ }
+ }
+
+ // Exit if the data is already in the specified order
+ final boolean wpsToStart = (inRearrangeOption == Rearrange.TO_START);
+ final boolean doSort = (inSortOption != SortMode.DONT_SORT);
+ if (numWaypoints == 0 || numNonWaypoints == 0
+ || (wpsToStart && !wayAfterNon && nonAfterWay && !doSort)
+ || (!wpsToStart && wayAfterNon && !nonAfterWay && !doSort)
+ || inRearrangeOption == Rearrange.TO_NEAREST)
+ {
+ return false;
+ }
+ // Note: it could still be that the rearrange and sort has no effect, but we don't know yet
+ // Make a copy of the waypoints array first so we can compare it with after the sort
+ DataPoint[] origWaypoints = new DataPoint[numPoints];
+ System.arraycopy(waypoints, 0, origWaypoints, 0, numPoints);
+
+ if (doSort && numWaypoints > 1)
+ {
+ // Sort the waypoints array
+ WaypointComparer comparer = new WaypointComparer(inSortOption);
+ Arrays.sort(waypoints, comparer);
+ final boolean sortDidNothing = areArraysSame(origWaypoints, waypoints);
+ if (sortDidNothing && (numNonWaypoints == 0
+ || (wpsToStart && !wayAfterNon && nonAfterWay)
+ || (!wpsToStart && wayAfterNon && !nonAfterWay)))
+ {
+ return false;
+ }
+ }
+
+ // Copy the arrays into an array in the specified order
+ DataPoint[] neworder = new DataPoint[numPoints];
+ if (wpsToStart)
+ {
+ System.arraycopy(waypoints, 0, neworder, 0, numWaypoints);
+ System.arraycopy(nonWaypoints, 0, neworder, numWaypoints, numNonWaypoints);
+ }
+ else
+ {
+ System.arraycopy(nonWaypoints, 0, neworder, 0, numNonWaypoints);
+ System.arraycopy(waypoints, 0, neworder, numNonWaypoints, numWaypoints);
+ }
+ // Give track the new point order
+ return track.replaceContents(neworder);
+ }
+
+ /**
+ * Compare two arrays of DataPoints and see if they're identical or not
+ * @param inOriginal original array of points
+ * @param inSorted array of points after sorting
+ * @return true if the two arrays have the same points in the same order
+ */
+ private static boolean areArraysSame(DataPoint[] inOriginal, DataPoint[] inSorted)
+ {
+ if (inOriginal == null && inSorted == null) return true; // both null
+ if (inOriginal == null || inSorted == null) return false; // only one of them null
+ if (inOriginal.length != inSorted.length) return false;
+ // Loop over all points
+ for (int i=0; i<inOriginal.length; i++)
+ {
+ DataPoint origPoint = inOriginal[i];
+ DataPoint sortedPoint = inSorted[i];
+ if ((origPoint != null || sortedPoint != null)
+ && (origPoint != sortedPoint))
+ {
+ return false; // points different
+ }
+ }
+ // Must be all the same
+ return true;
+ }
}