+package tim.prune.function;
+
+import java.util.Arrays;
+
+import javax.swing.JOptionPane;
+
+import tim.prune.App;
+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 RearrangeFunction
+{
+
+ /**
+ * Constructor
+ * @param inApp app object
+ */
+ public RearrangeWaypointsFunction(App inApp)
+ {
+ super(inApp, true);
+ }
+
+ /** Get the name key */
+ public String getNameKey() {
+ return "function.rearrangewaypoints";
+ }
+
+ /** Get whether sorting by time is allowed or not */
+ protected boolean isSortByTimeAllowed() {
+ return Checker.haveWaypointsGotTimestamps(_app.getTrackInfo().getTrack());
+ }
+
+ /** Get the description key */
+ public String getDescriptionKey() {
+ return "dialog.rearrangewaypoints.desc";
+ }
+
+ /** Sort by name key */
+ protected String getSortNameKey() {
+ return "sortbyname";
+ }
+
+ /**
+ * Perform the rearrange and sort according to the radio buttons
+ */
+ 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 (rearrangeOption == Rearrange.TO_START || rearrangeOption == Rearrange.TO_END)
+ {
+ // Collect the waypoints to the start or end of the track
+ success = collectWaypoints(rearrangeOption, sortOption);
+ }
+ else
+ {
+ // Interleave the waypoints into track order
+ success = track.interleaveWaypoints();
+ }
+ if (success)
+ {
+ _app.getTrackInfo().getSelection().clearAll(); // clear selected point and range
+ _app.completeFunction(undo, I18nManager.getText("confirm.rearrangewaypoints"));
+ }
+ else
+ {
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.rearrange.noop"),
+ I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
+ }
+ }
+
+
+ /**
+ * 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;
+ }
+}