diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractRaceBoardMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractRaceBoardMode.java
index c99675b1315..a43881ad14c 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractRaceBoardMode.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractRaceBoardMode.java
@@ -1,27 +1,49 @@
package com.sap.sailing.gwt.ui.raceboard;
+import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
+import com.google.gwt.core.shared.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
+import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction;
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener;
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener;
+import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceCompetitorSet.CompetitorsForRaceDefinedListener;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sse.gwt.client.player.Timer;
/**
- * Abstract base class for implementing a {@link PlayerMode}. The {@link #applyTo(RaceBoardPanel)} method
- * registers this object as a leaderboard update listener and as a race times info provider listener. The
- * default implementations of the corresponding callback methods will simply unregister this object as a
- * listener. If subclasses want to stick with this behavior they can call the {@code super} implementation
- * in their overrides.
+ * Abstract base class for implementing a {@link RaceBoardMode}. The {@link #applyTo(RaceBoardPanel)} method registers
+ * this object as a leaderboard update listener and as a race times info provider listener, also as a
+ * {@link CompetitorsForRaceDefinedListener} in order to learn when the set of competitors for the race displayed by the
+ * player is known. The default implementations of the corresponding callback methods will simply unregister this object
+ * as a listener for the respective callback, enters the knowledge gained in the object's fields and calls the
+ * {@link #trigger} method each time (abstract in this class). Subclasses can and should do the following:
+ *
+ *
+ * - Redefine specific callback methods in case more processing is required or canceling the listener is desired only
+ * when certain preconditions are met
+ * - Implement the {@link #trigger} method to check if all information required has been received by now and then use
+ * it to configure the {@link RaceBoard} and its components accordingly
+ *
+ *
+ * Note that the {@link #updatedLeaderboard(LeaderboardDTO)} method will typically be called several times in more or
+ * less unpredictable order, asynchronously. Calling {@link Timer#setTime(long)} will trigger
+ * {@link #updatedLeaderboard(LeaderboardDTO)} eventually, but in between, other callbacks to the same method
+ * may occur, triggered by other, automatic adjustments or initialization of the {@link Timer}. To solve this
+ * problem, subclasses may decide to use a separate {@link GetLeaderboardByNameAction} action and execute
+ * it using the {@link #getLeaderboardPanel()}.{@link LeaderboardPanel#getExecutor() getExecutor()}. In the
+ * dedicated callback the subclass can then be sure that the {@link LeaderboardDTO} received matches the
+ * time previously set on the {@link Timer}.
*
* @author Axel Uhl (d043530)
*
@@ -33,6 +55,13 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
private LeaderboardPanel leaderboardPanel;
private RaceBoardPanel raceBoardPanel;
+ private Map raceTimesInfo;
+ private RaceTimesInfoDTO raceTimesInfoForRace;
+ private LeaderboardDTO leaderboard;
+ private LeaderboardDTO leaderboardForSpecificTimePoint;
+ private Iterable competitorsInRace;
+ private RaceColumnDTO raceColumn;
+
@Override
public void applyTo(RaceBoardPanel raceBoardPanel) {
this.raceBoardPanel = raceBoardPanel;
@@ -45,6 +74,12 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
raceBoardPanel.getMap().addCompetitorsForRaceDefinedListener(this);
}
+ /**
+ * Called whenever new information of any sort has become available. Subclasses can use this
+ * to decide whether everything they need has been received in order to carry out their actions.
+ */
+ protected abstract void trigger();
+
protected Timer getTimer() {
return timer;
}
@@ -66,13 +101,21 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
}
/**
- * Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has been
- * received, and it should be clear by now whether we're talking about a live or a replay race. In case of a replay race
- * the timer is set to
+ * Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has
+ * been received, and it should be clear by now whether we're talking about a live or a replay race. The
+ * {@link #raceTimesInfo} field is set to the {@code raceTimesInfo} object received as parameter, the
+ * {@link #raceTimesInfoForRace} field is set to the race times info specific to the race identified by
+ * {@link #getRaceIdentifier()}.
*/
@Override
public void raceTimesInfosReceived(Map raceTimesInfo,
long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
+ this.raceTimesInfo = raceTimesInfo;
+ this.raceTimesInfoForRace = raceTimesInfo.get(getRaceIdentifier());
+ trigger();
+ }
+
+ protected void stopReceivingRaceTimesInfos() {
raceTimePanel.removeRaceTimesInfoProviderListener(this);
}
@@ -81,16 +124,83 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
*/
@Override
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
+ this.leaderboard = leaderboard;
+ trigger();
+ }
+
+ protected void stopReceivingLeaderboard() {
leaderboardPanel.removeLeaderboardUpdateListener(this);
}
+ /**
+ * Fetches the leaderboard named {@code leaderboardName} for {@code timePoint} with details for race column
+ * {@code raceColumnName} and stores it in {@link #leaderboardForSpecificTimePoint}, then invokes {@link #trigger()}.
+ * If a {@link #getLeaderboard()} has been received already, it is passed on in the call in order to reduce the
+ * bandwidth required, based on a differential approach.
+ */
+ protected void loadLeaderboardForSpecificTimePoint(String leaderboardName,
+ String raceColumnName, Date timePoint) {
+ final AsyncCallback callback = new AsyncCallback() {
+ @Override
+ public void onFailure(Throwable caught) {
+ GWT.log("Error trying to load leaderboard", caught);
+ }
+
+ @Override
+ public void onSuccess(LeaderboardDTO result) {
+ leaderboardForSpecificTimePoint = result;
+ trigger();
+ }
+ };
+ final ArrayList raceColumnNameAsList = new ArrayList<>();
+ if (getRaceColumn() != null) {
+ raceColumnNameAsList.add(getRaceColumn().getName());
+ }
+ final GetLeaderboardByNameAction getLeaderboardByNameAction = new GetLeaderboardByNameAction(
+ getLeaderboardPanel().getSailingService(), getLeaderboardPanel().getLeaderboard().name,
+ getTimer().getTime(), raceColumnNameAsList, /* addOverallDetails */ false,
+ getLeaderboard(), /* fillTotalPointsUncorrected */ false,
+ /* timerToAdjustOffsetIn */ getTimer(), /* errorReporter */ null, StringMessages.INSTANCE);
+ getLeaderboardPanel().getExecutor().execute(getLeaderboardByNameAction, LeaderboardPanel.LOAD_LEADERBOARD_DATA_CATEGORY, callback);
+ }
+
@Override
public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) {
- // nothing to do
+ this.raceColumn = raceColumn;
+ trigger();
}
@Override
public void competitorsForRaceDefined(Iterable competitorsInRace) {
- // nothing to do here
+ this.competitorsInRace = competitorsInRace;
+ trigger();
+ }
+
+ protected void stopReceivingCompetitorsInRace() {
+ raceBoardPanel.getMap().removeCompetitorsForRaceDefinedListener(this);
+ }
+
+ protected Map getRaceTimesInfo() {
+ return raceTimesInfo;
+ }
+
+ protected RaceTimesInfoDTO getRaceTimesInfoForRace() {
+ return raceTimesInfoForRace;
+ }
+
+ protected LeaderboardDTO getLeaderboard() {
+ return leaderboard;
+ }
+
+ protected LeaderboardDTO getLeaderboardForSpecificTimePoint() {
+ return leaderboardForSpecificTimePoint;
+ }
+
+ protected Iterable getCompetitorsInRace() {
+ return competitorsInRace;
+ }
+
+ protected RaceColumnDTO getRaceColumn() {
+ return raceColumn;
}
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/FullAnalysisMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/FullAnalysisMode.java
index 599cef15f87..b74ab873c3d 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/FullAnalysisMode.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/FullAnalysisMode.java
@@ -1,16 +1,10 @@
package com.sap.sailing.gwt.ui.raceboard;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
-import java.util.Map;
import com.sap.sailing.domain.common.DetailType;
-import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
-import com.sap.sailing.domain.common.dto.LeaderboardDTO;
-import com.sap.sailing.gwt.ui.client.RaceTimePanel;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
-import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sse.gwt.client.player.Timer.PlayModes;
import com.sap.sse.gwt.client.player.Timer.PlayStates;
@@ -22,30 +16,33 @@ import com.sap.sse.gwt.client.player.Timer.PlayStates;
*
*/
public class FullAnalysisMode extends AbstractRaceBoardMode {
-
+ private boolean leaderboardSettingsAdjusted;
+ private boolean timerAdjusted;
+
/**
- * Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has been
- * received, and it should be clear by now whether we're talking about a live or a replay race. In case of a replay race
- * the timer is set to
+ * Listening for race times info continues until information about the end of the race has been received. The leaderboard
+ * settings are adjusted as soon as a valid leaderboard has been received.
*/
@Override
- public void raceTimesInfosReceived(Map raceTimesInfo,
- long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
- if (getTimer().getPlayMode() != PlayModes.Live && !raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier())) {
- final RaceTimesInfoDTO times = raceTimesInfo.get(getRaceIdentifier());
- if (times.endOfRace != null) {
- getTimer().setTime(times.endOfRace.getTime());
- // we've done our adjustments; remove listener and let go
- super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
+ protected void trigger() {
+ if (!leaderboardSettingsAdjusted && getLeaderboard() != null) {
+ leaderboardSettingsAdjusted = true;
+ // it's important to first unregister the listener before updateSettings is called because
+ // updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
+ stopReceivingLeaderboard();
+ adjustLeaderboardSettings();
+ }
+ if (!timerAdjusted && getRaceTimesInfoForRace() != null && getRaceTimesInfoForRace().endOfRace != null) {
+ timerAdjusted = true;
+ stopReceivingRaceTimesInfos();
+ if (getTimer().getPlayMode() == PlayModes.Live) {
+ getTimer().setPlayMode(PlayModes.Replay);
}
+ getTimer().setTime(getRaceTimesInfoForRace().endOfRace.getTime());
}
}
- @Override
- public void updatedLeaderboard(LeaderboardDTO leaderboard) {
- // it's important to first unregister the listener before updateSettings is called because
- // updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
- super.updatedLeaderboard(leaderboard);
+ private void adjustLeaderboardSettings() {
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
final List raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
raceDetailsToShow.add(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS);
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/PlayerMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/PlayerMode.java
index 43954dcd879..0e707f9ec88 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/PlayerMode.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/PlayerMode.java
@@ -1,16 +1,10 @@
package com.sap.sailing.gwt.ui.raceboard;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
-import java.util.Map;
import com.sap.sailing.domain.common.DetailType;
-import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
-import com.sap.sailing.domain.common.dto.LeaderboardDTO;
-import com.sap.sailing.gwt.ui.client.RaceTimePanel;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
-import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.gwt.client.player.Timer.PlayModes;
@@ -26,30 +20,11 @@ import com.sap.sse.gwt.client.player.Timer.PlayStates;
public class PlayerMode extends AbstractRaceBoardMode {
private final Duration DURATION_BEFORE_START_TO_SET_TIMER_TO_FOR_REPLAY_RACES = Duration.ONE_SECOND.times(10);
- /**
- * Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has been
- * received, and it should be clear by now whether we're talking about a live or a replay race. In case of a replay race
- * the timer is set to
- */
- @Override
- public void raceTimesInfosReceived(Map raceTimesInfo,
- long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
- if (getTimer().getPlayMode() != PlayModes.Live && !raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier())) {
- final RaceTimesInfoDTO times = raceTimesInfo.get(getRaceIdentifier());
- if (times.startOfRace != null) {
- getTimer().setTime(new MillisecondsTimePoint(times.startOfRace).minus(DURATION_BEFORE_START_TO_SET_TIMER_TO_FOR_REPLAY_RACES).asMillis());
- getTimer().play();
- // we've done our adjustments; remove listener and let go
- super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
- }
- }
- }
+ private boolean adjustedLeaderboardSettings;
- @Override
- public void updatedLeaderboard(LeaderboardDTO leaderboard) {
- // it's important to first unregister the listener before updateSettings is called because
- // updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
- super.updatedLeaderboard(leaderboard);
+ private boolean timerAdjusted;
+
+ private void adjustLeaderboardSettings() {
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
final List raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
raceDetailsToShow.add(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
@@ -69,4 +44,22 @@ public class PlayerMode extends AbstractRaceBoardMode {
existingSettings.isShowCompetitorFullNameColumn());
getLeaderboardPanel().updateSettings(newSettings);
}
+
+ @Override
+ protected void trigger() {
+ if (!adjustedLeaderboardSettings && getLeaderboard() != null) {
+ adjustedLeaderboardSettings = true;
+ // it's important to first unregister the listener before updateSettings is called because
+ // updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
+ stopReceivingLeaderboard();
+ adjustLeaderboardSettings();
+ }
+ if (!timerAdjusted && getTimer().getPlayMode() != PlayModes.Live && getRaceTimesInfoForRace() != null && getRaceTimesInfoForRace().startOfRace != null) {
+ timerAdjusted = true;
+ getTimer().setTime(new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).minus(DURATION_BEFORE_START_TO_SET_TIMER_TO_FOR_REPLAY_RACES).asMillis());
+ getTimer().play();
+ // we've done our adjustments; remove listener and let go
+ stopReceivingRaceTimesInfos();
+ }
+ }
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardModeWithPerRaceCompetitors.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardModeWithPerRaceCompetitors.java
index 8fac0c85e68..4532853ca9d 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardModeWithPerRaceCompetitors.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardModeWithPerRaceCompetitors.java
@@ -20,7 +20,6 @@ public abstract class RaceBoardModeWithPerRaceCompetitors extends AbstractRaceBo
@Override
public void competitorsForRaceDefined(Iterable competitorsInRace) {
this.competitorsInRace = competitorsInRace;
- updateCompetitorSelection();
}
protected Iterable getCompetitorsInRace() {
@@ -31,28 +30,19 @@ public abstract class RaceBoardModeWithPerRaceCompetitors extends AbstractRaceBo
return leaderboard;
}
- protected void setLeaderboard(LeaderboardDTO leaderboard) {
- this.leaderboard = leaderboard;
- }
-
- @Override
- public void updatedLeaderboard(LeaderboardDTO leaderboard) {
- super.updatedLeaderboard(leaderboard);
- setLeaderboard(leaderboard);
- }
-
@Override
public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) {
this.raceColumn = raceColumn;
+ trigger();
}
protected RaceColumnDTO getRaceColumn() {
return raceColumn;
}
- protected void updateCompetitorSelection(final int howManyTopCompetitorsInRaceToSelect) {
- if (getLeaderboard() != null) {
- final List competitorsFromBestToWorstInColumn = getLeaderboard().getCompetitorsFromBestToWorst(getRaceColumn());
+ protected void updateCompetitorSelection(final int howManyTopCompetitorsInRaceToSelect, LeaderboardDTO leaderboard) {
+ if (leaderboard != null) {
+ final List competitorsFromBestToWorstInColumn = leaderboard.getCompetitorsFromBestToWorst(getRaceColumn());
final Set competitorsToSelect = new HashSet<>();
int numberOfSelectedCompetitors = 0;
for (int i=0; numberOfSelectedCompetitors raceTimesInfo,
- long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
- if (!raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier())) {
- final RaceTimesInfoDTO times = raceTimesInfo.get(getRaceIdentifier());
- if (times.startOfRace != null) {
- // make sure we're no longer in live/playing mode/state; the start analysis is supposed to be a "frozen" display at first
- if (getTimer().getPlayMode() == PlayModes.Live) {
- getTimer().setPlayMode(PlayModes.Replay);
- }
- // the following call will always trigger a leaderboard load and therefore a callback
- // to updatedLeaderboard; therefore, updatedLeaderboard can decide when it's time to
- // trigger the chart display and the map settings application
- getTimer().setTime(new MillisecondsTimePoint(times.startOfRace).plus(DURATION_AFTER_START_TO_SET_TIMER_TO_FOR_START_ANALYSIS).asMillis());
- // we've done our adjustments; remove listener and let go
- super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
- this.raceTimesInfo = times;
- raceTimesInfoReceived = true;
+ protected void updateCompetitorSelection() {
+ updateCompetitorSelection(/* howManyTopCompetitorsInRaceToSelect */ 3, getLeaderboard());
+ }
+
+ private void adjustMapSettings() {
+ final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
+ final RaceMapSettings newMapSettings = new RaceMapSettings(
+ new RaceMapZoomSettings(Collections.singleton(ZoomTypes.BOATS), /* zoomToSelected */ false),
+ existingMapSettings.getHelpLinesSettings(),
+ existingMapSettings.getTransparentHoverlines(),
+ existingMapSettings.getHoverlineStrokeWeight(),
+ existingMapSettings.getTailLengthInMilliseconds(),
+ /* existingMapSettings.isWindUp() */ true,
+ existingMapSettings.getBuoyZoneRadiusInMeters(),
+ existingMapSettings.isShowOnlySelectedCompetitors(),
+ existingMapSettings.isShowSelectedCompetitorsInfo(),
+ existingMapSettings.isShowWindStreamletColors(),
+ existingMapSettings.isShowWindStreamletOverlay(),
+ existingMapSettings.isShowSimulationOverlay(),
+ existingMapSettings.isShowMapControls(),
+ existingMapSettings.getManeuverTypesToShow(),
+ existingMapSettings.isShowDouglasPeuckerPoints());
+ // try to update the settings once; the problem is the "wind up" display; it changes pan/zoom
+ // which is a major source of instability for the map. Going twice in the map idle event handler
+ // to ensure the settings are really applied.
+ final Object[] registration = new Object[1];
+ registration[0] = getRaceBoardPanel().getMap().getMap().addIdleHandler(new IdleMapHandler() {
+ @Override
+ public void onEvent(IdleMapEvent event) {
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ getRaceBoardPanel().getMap().updateSettings(newMapSettings);
+ }
+ });
+ ((HandlerRegistration) registration[0]).removeHandler();
+ }
+ });
+ getRaceBoardPanel().getMap().updateSettings(newMapSettings);
+ }
+
+ @Override
+ protected void trigger() {
+ if (!leaderboardSettingsAdjusted && getLeaderboard() != null) {
+ leaderboardSettingsAdjusted = true;
+ stopReceivingLeaderboard();
+ adjustLeaderboardSettings();
+ }
+ if (!timerAdjusted && getRaceTimesInfoForRace() != null && getRaceTimesInfoForRace().startOfRace != null) {
+ timerAdjusted = true;
+ // we've done our adjustments; remove listener and let go
+ stopReceivingRaceTimesInfos();
+ // make sure we're no longer in live/playing mode/state; the start analysis is supposed to be a "frozen" display at first
+ if (getTimer().getPlayMode() == PlayModes.Live) {
+ getTimer().setPlayMode(PlayModes.Replay);
+ }
+ // the following call will always trigger a leaderboard load and therefore a callback
+ // to updatedLeaderboard; therefore, updatedLeaderboard can decide when it's time to
+ // trigger the chart display and the map settings application
+ getTimer().setTime(new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).plus(DURATION_AFTER_START_TO_SET_TIMER_TO_FOR_START_ANALYSIS).asMillis());
+ // no need to do anything if we don't have the timing info yet; after obtaining the timing
+ // info we can start waiting for the simulation overlay and then adjust the map settings and,
+ // if available, show the competitor chart
+ if (!mapSettingsAndTimeZoomAdjusted) {
+ mapSettingsAndTimeZoomAdjusted = true;
+ Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
+ @Override
+ public boolean execute() {
+ // keep trying in 500ms steps until the simulation overlay is found; this way, changing the competitor chart
+ // settings is largely more successful
+ final boolean simulationOverlayFound = getRaceBoardPanel().getMap().getSimulationOverlay() != null;
+ if (simulationOverlayFound) {
+ if (isCompetitorChartEnabled()) {
+ final MultiCompetitorRaceChartSettings newCompetitorChartSettings = new MultiCompetitorRaceChartSettings(
+ new ChartSettings(/* stepSizeInMillis */ 1000), DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
+ /* no second series */ null);
+ competitorChart.updateSettings(newCompetitorChartSettings);
+ getRaceTimePanel().getTimeRangeProvider().setTimeZoom(
+ new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).minus(DURATION_BEFORE_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate(),
+ new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).plus(DURATION_AFTER_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate());
+ }
+ adjustMapSettings();
+ }
+ return !simulationOverlayFound;
+ }
+ }, /* delay in milliseconds */ 500);
+ }
+ }
+ if (getLeaderboardForSpecificTimePoint() == null &&
+ getRaceColumn() != null && getLeaderboard() != null
+ && timerAdjusted) {
+ loadLeaderboardForSpecificTimePoint(getLeaderboard().name, getRaceColumn().getName(), getTimer().getTime());
+ }
+ if (!competitorSelectionUpdated && getLeaderboardForSpecificTimePoint() != null && getCompetitorsInRace() != null) {
+ competitorSelectionUpdated = true;
+ stopReceivingCompetitorsInRace();
+ updateCompetitorSelection();
+ if (isCompetitorChartEnabled()) {
+ getRaceBoardPanel().setCompetitorChartVisible(true);
}
}
}
-
- @Override
- protected void updateCompetitorSelection() {
- updateCompetitorSelection(/* howManyTopCompetitorsInRaceToSelect */ 3);
- }
- private void showCompetitorChartIfAllDataReceived() {
- if (getRaceColumn() != null && leaderboardUpdateReveiced && raceTimesInfoReceived && getLeaderboard() != null
- && getLeaderboard().getCompetitorsFromBestToWorst(getRaceColumn()) != null
- && raceTimesInfo != null && raceTimesInfo.startOfRace != null) {
- ArrayList raceColumnName = new ArrayList<>();
- raceColumnName.add(getRaceColumn().getName());
- // The problem with the leaderboard received in updatedLeaderboard(...) is that we don't know which request
- // has caused it to be delivered; there is an early request for "now" and maybe even another request for start of race;
- // therefore, we need to fire our own request for the time point desired and make sure we get the right
- // LeaderboardDTO to determine the competitor ordering for the current race.
- final GetLeaderboardByNameAction getLeaderboardByNameAction = new GetLeaderboardByNameAction(
- getLeaderboardPanel().getSailingService(), getLeaderboardPanel().getLeaderboard().name,
- getTimer().getTime(), raceColumnName, /* addOverallDetails */ false,
- getLeaderboard(), /* fillTotalPointsUncorrected */ false,
- /* timerToAdjustOffsetIn */ getTimer(), /* errorReporter */ null, StringMessages.INSTANCE);
- getLeaderboardPanel().getExecutor().execute(getLeaderboardByNameAction, LeaderboardPanel.LOAD_LEADERBOARD_DATA_CATEGORY,
- new AsyncCallback() {
- @Override
- public void onFailure(Throwable caught) {
- GWT.log("Error trying to load leaderboard", caught);
- }
-
- @Override
- public void onSuccess(LeaderboardDTO result) {
- setLeaderboard(result);
- getLeaderboardPanel().updateLeaderboard(result);
- updateCompetitorSelection();
- if (isCompetitorChartEnabled()) {
- getRaceBoardPanel().setCompetitorChartVisible(true);
- }
- }
- });
- Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
- @Override
- public boolean execute() {
- // keep trying in 500ms steps until the simulation overlay is found; this way, changing the competitor chart
- // settings is largely more successful because
- boolean result = getRaceBoardPanel().getMap().getSimulationOverlay() == null;
- if (!result) {
- if (isCompetitorChartEnabled()) {
- final MultiCompetitorRaceChartSettings newCompetitorChartSettings = new MultiCompetitorRaceChartSettings(
- new ChartSettings(/* stepSizeInMillis */ 1000), DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
- /* no second series */ null);
- competitorChart.updateSettings(newCompetitorChartSettings);
- getRaceTimePanel().getTimeRangeProvider().setTimeZoom(
- new MillisecondsTimePoint(raceTimesInfo.startOfRace).minus(DURATION_BEFORE_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate(),
- new MillisecondsTimePoint(raceTimesInfo.startOfRace).plus(DURATION_AFTER_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate());
- }
- final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
- final RaceMapSettings newMapSettings = new RaceMapSettings(
- new RaceMapZoomSettings(Collections.singleton(ZoomTypes.BOATS), /* zoomToSelected */ false),
- existingMapSettings.getHelpLinesSettings(),
- existingMapSettings.getTransparentHoverlines(),
- existingMapSettings.getHoverlineStrokeWeight(),
- existingMapSettings.getTailLengthInMilliseconds(),
- /* existingMapSettings.isWindUp() */ true,
- existingMapSettings.getBuoyZoneRadiusInMeters(),
- existingMapSettings.isShowOnlySelectedCompetitors(),
- existingMapSettings.isShowSelectedCompetitorsInfo(),
- existingMapSettings.isShowWindStreamletColors(),
- existingMapSettings.isShowWindStreamletOverlay(),
- existingMapSettings.isShowSimulationOverlay(),
- existingMapSettings.isShowMapControls(),
- existingMapSettings.getManeuverTypesToShow(),
- existingMapSettings.isShowDouglasPeuckerPoints());
- // try to update the settings once; the problem is the "wind up" display; it changes pan/zoom
- // which is a major source of instability for the map. Going twice in the map idle event handler
- // to ensure the settings are really applied.
- final Object[] registration = new Object[1];
- registration[0] = getRaceBoardPanel().getMap().getMap().addIdleHandler(new IdleMapHandler() {
- @Override
- public void onEvent(IdleMapEvent event) {
- Scheduler.get().scheduleDeferred(new ScheduledCommand() {
- @Override
- public void execute() {
- getRaceBoardPanel().getMap().updateSettings(newMapSettings);
- }
- });
- ((HandlerRegistration) registration[0]).removeHandler();
- }
- });
- getRaceBoardPanel().getMap().updateSettings(newMapSettings);
- }
- return result;
- }
- }, /* delay in milliseconds */ 500);
- }
- }
-
- @Override
- public void updatedLeaderboard(LeaderboardDTO leaderboard) {
- // no need to do anything if we don't have the timing info yet; after obtaining the timing
- // info there will always be at least one more leaderboard requested, triggering a callback
- // to this method
- if (raceTimesInfoReceived) {
- // it's important to first unregister the listener before updateSettings is called because
- // updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
- super.updatedLeaderboard(leaderboard);
- final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
- final List raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
- raceDetailsToShow.add(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START);
- raceDetailsToShow.add(DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START);
- raceDetailsToShow.add(DetailType.DISTANCE_TO_START_AT_RACE_START);
- raceDetailsToShow.add(DetailType.DISTANCE_TO_START_LINE);
- raceDetailsToShow.add(DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS);
- raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_AT_RACE_START);
- raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START);
- raceDetailsToShow.add(DetailType.START_TACK);
- raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
- final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
- existingSettings.getLegDetailsToShow(),
- raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
- existingSettings.getNamesOfRacesToShow(),
- existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
- existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
- existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
- existingSettings.isUpdateUponPlayStateChange(),
- existingSettings.getActiveRaceColumnSelectionStrategy(),
- existingSettings.isShowAddedScores(),
- existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
- existingSettings.isShowCompetitorSailIdColumn(),
- existingSettings.isShowCompetitorFullNameColumn());
- getLeaderboardPanel().updateSettings(newSettings);
- leaderboardUpdateReveiced = true;
- showCompetitorChartIfAllDataReceived();
- }
+ private void adjustLeaderboardSettings() {
+ final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
+ final List raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
+ raceDetailsToShow.add(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START);
+ raceDetailsToShow.add(DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START);
+ raceDetailsToShow.add(DetailType.DISTANCE_TO_START_AT_RACE_START);
+ raceDetailsToShow.add(DetailType.DISTANCE_TO_START_LINE);
+ raceDetailsToShow.add(DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS);
+ raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_AT_RACE_START);
+ raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START);
+ raceDetailsToShow.add(DetailType.START_TACK);
+ raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
+ final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
+ existingSettings.getLegDetailsToShow(),
+ raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
+ existingSettings.getNamesOfRacesToShow(),
+ existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
+ existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
+ existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
+ existingSettings.isUpdateUponPlayStateChange(),
+ existingSettings.getActiveRaceColumnSelectionStrategy(),
+ existingSettings.isShowAddedScores(),
+ existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
+ existingSettings.isShowCompetitorSailIdColumn(),
+ existingSettings.isShowCompetitorFullNameColumn());
+ getLeaderboardPanel().updateSettings(newSettings);
}
private boolean isCompetitorChartEnabled() {
return competitorChart != null;
}
-
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java
index 82ccbf67e3f..3f6689ae76c 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java
@@ -3,16 +3,12 @@ package com.sap.sailing.gwt.ui.raceboard;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import java.util.Map;
import com.sap.sailing.domain.common.DetailType;
-import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
-import com.sap.sailing.domain.common.dto.LeaderboardDTO;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO;
-import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -30,93 +26,58 @@ import com.sap.sse.gwt.client.player.Timer.PlayModes;
public class WinningLanesMode extends RaceBoardModeWithPerRaceCompetitors {
private Duration tailLength;
- /**
- * When set to {@code true}, when {@link #updatedLeaderboard(LeaderboardDTO)} is called the next time it will
- * stop the leaderboard update notifications and will adjust the tail length to {@link #tailLength}. Must only
- * be set to {@code true} if {@link #tailLength} has been set to a valid duration before.
- */
- private boolean stopReceivingLeaderboardUpdatesAndAdjustTailLength;
-
- @Override
- public void raceTimesInfosReceived(Map raceTimesInfo,
- long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
- final Date startOfRace;
- final RaceTimesInfoDTO raceTimesInfoForRace;
- if (!raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier()) &&
- (startOfRace=(raceTimesInfoForRace=raceTimesInfo.get(getRaceIdentifier())).startOfRace) != null) {
- if (getTimer().getPlayMode() == PlayModes.Live) {
- // adjust tail length such that for the leading boat the tail is shown since the start time point
- tailLength = new MillisecondsTimePoint(startOfRace).until(getTimer().getLiveTimePoint());
- stopReceivingLeaderboardUpdatesAndAdjustTailLength = true;
- } else {
- final List markPassingTimes = raceTimesInfoForRace.getMarkPassingTimes();
- final Date firstPassingOfLastWaypointPassed = markPassingTimes == null || markPassingTimes.isEmpty() ? null :
- markPassingTimes.get(markPassingTimes.size()-1).firstPassingDate;
- final Date endOfRace = raceTimesInfoForRace.endOfRace;
- final Date end = firstPassingOfLastWaypointPassed != null ? firstPassingOfLastWaypointPassed :
- endOfRace != null ? endOfRace : raceTimesInfoForRace.endOfTracking;
- if (end != null) {
- tailLength = new MillisecondsTimePoint(startOfRace).until(new MillisecondsTimePoint(end));
- stopReceivingLeaderboardUpdatesAndAdjustTailLength = true;
- getTimer().setTime(end.getTime());
- }
- // we've done our adjustments; remove listener and let go
- super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
- }
- }
- }
+ private boolean adjustedLeaderboardSettings;
+
+ private boolean adjustedCompetitorSelection;
@Override
protected void updateCompetitorSelection() {
final int howManyCompetitorsToSelect = getHowManyCompetitorsToSelect(getLeaderboardPanel().getCompetitors(getRaceIdentifier()));
- updateCompetitorSelection(howManyCompetitorsToSelect);
+ updateCompetitorSelection(howManyCompetitorsToSelect, getLeaderboard());
}
- @Override
- public void updatedLeaderboard(LeaderboardDTO leaderboard) {
- if (stopReceivingLeaderboardUpdatesAndAdjustTailLength) {
- // it's important to first unregister the listener before updateSettings is called because
- // updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
- super.updatedLeaderboard(leaderboard);
- final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
- final List raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
- raceDetailsToShow.add(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS);
- raceDetailsToShow.add(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS);
- raceDetailsToShow.add(DetailType.RACE_DISTANCE_TRAVELED);
- raceDetailsToShow.add(DetailType.RACE_TIME_TRAVELED);
- raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
- final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
- existingSettings.getLegDetailsToShow(),
- raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
- existingSettings.getNamesOfRacesToShow(),
- existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
- existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
- existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
- existingSettings.isUpdateUponPlayStateChange(),
- existingSettings.getActiveRaceColumnSelectionStrategy(),
- existingSettings.isShowAddedScores(),
- existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
- existingSettings.isShowCompetitorSailIdColumn(),
- existingSettings.isShowCompetitorFullNameColumn());
- getLeaderboardPanel().updateSettings(newSettings);
- final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
- final RaceMapSettings newMapSettings = new RaceMapSettings(existingMapSettings.getZoomSettings(),
- existingMapSettings.getHelpLinesSettings(),
- existingMapSettings.getTransparentHoverlines(),
- existingMapSettings.getHoverlineStrokeWeight(),
- tailLength.asMillis(),
- /* existingMapSettings.isWindUp() */ true,
- existingMapSettings.getBuoyZoneRadiusInMeters(),
- /* existingMapSettings.isShowOnlySelectedCompetitors() */ true, // show the top n competitors and their tails quickly
- existingMapSettings.isShowSelectedCompetitorsInfo(),
- existingMapSettings.isShowWindStreamletColors(),
- existingMapSettings.isShowWindStreamletOverlay(),
- existingMapSettings.isShowSimulationOverlay(),
- existingMapSettings.isShowMapControls(),
- existingMapSettings.getManeuverTypesToShow(),
- existingMapSettings.isShowDouglasPeuckerPoints());
- getRaceBoardPanel().getMap().updateSettings(newMapSettings);
- }
+ private void adjustLeaderboardSettings() {
+ final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
+ final List raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
+ raceDetailsToShow.add(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS);
+ raceDetailsToShow.add(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS);
+ raceDetailsToShow.add(DetailType.RACE_DISTANCE_TRAVELED);
+ raceDetailsToShow.add(DetailType.RACE_TIME_TRAVELED);
+ raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
+ final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
+ existingSettings.getLegDetailsToShow(),
+ raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
+ existingSettings.getNamesOfRacesToShow(),
+ existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
+ existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
+ existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
+ existingSettings.isUpdateUponPlayStateChange(),
+ existingSettings.getActiveRaceColumnSelectionStrategy(),
+ existingSettings.isShowAddedScores(),
+ existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
+ existingSettings.isShowCompetitorSailIdColumn(),
+ existingSettings.isShowCompetitorFullNameColumn());
+ getLeaderboardPanel().updateSettings(newSettings);
+ }
+
+ private void adjustMapSettings() {
+ final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
+ final RaceMapSettings newMapSettings = new RaceMapSettings(existingMapSettings.getZoomSettings(),
+ existingMapSettings.getHelpLinesSettings(),
+ existingMapSettings.getTransparentHoverlines(),
+ existingMapSettings.getHoverlineStrokeWeight(),
+ tailLength.asMillis(),
+ /* existingMapSettings.isWindUp() */ true,
+ existingMapSettings.getBuoyZoneRadiusInMeters(),
+ /* existingMapSettings.isShowOnlySelectedCompetitors() */ true, // show the top n competitors and their tails quickly
+ existingMapSettings.isShowSelectedCompetitorsInfo(),
+ existingMapSettings.isShowWindStreamletColors(),
+ existingMapSettings.isShowWindStreamletOverlay(),
+ existingMapSettings.isShowSimulationOverlay(),
+ existingMapSettings.isShowMapControls(),
+ existingMapSettings.getManeuverTypesToShow(),
+ existingMapSettings.isShowDouglasPeuckerPoints());
+ getRaceBoardPanel().getMap().updateSettings(newMapSettings);
}
/**
@@ -128,4 +89,46 @@ public class WinningLanesMode extends RaceBoardModeWithPerRaceCompetitors {
final int numberOfCompetitors = Util.size(competitors);
return numberOfCompetitors==0 ? 0 : numberOfCompetitors<=4 ? 1 : numberOfCompetitors <=9 ? 2 : 3;
}
+
+ @Override
+ protected void trigger() {
+ final Date startOfRace;
+ if (getRaceTimesInfoForRace() != null && (startOfRace=getRaceTimesInfoForRace().startOfRace) != null) {
+ if (getTimer().getPlayMode() == PlayModes.Live) {
+ // adjust tail length such that for the leading boat the tail is shown since the start time point
+ tailLength = new MillisecondsTimePoint(startOfRace).until(getTimer().getLiveTimePoint());
+ } else {
+ final List markPassingTimes = getRaceTimesInfoForRace().getMarkPassingTimes();
+ final Date firstPassingOfLastWaypointPassed = markPassingTimes == null || markPassingTimes.isEmpty() ? null :
+ markPassingTimes.get(markPassingTimes.size()-1).firstPassingDate;
+ final Date endOfRace = getRaceTimesInfoForRace().endOfRace;
+ final Date end = firstPassingOfLastWaypointPassed != null ? firstPassingOfLastWaypointPassed :
+ endOfRace != null ? endOfRace : getRaceTimesInfoForRace().endOfTracking;
+ if (end != null) {
+ tailLength = new MillisecondsTimePoint(startOfRace).until(new MillisecondsTimePoint(end));
+ getTimer().setTime(end.getTime());
+ }
+ }
+ // we've done our adjustments; remove listener and let go
+ stopReceivingRaceTimesInfos();
+ }
+ if (!adjustedLeaderboardSettings && getLeaderboard() != null) {
+ adjustedLeaderboardSettings = true;
+ // it's important to first unregister the listener before updateSettings is called because
+ // updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
+ stopReceivingLeaderboard();
+ adjustLeaderboardSettings();
+ }
+ if (adjustedLeaderboardSettings && tailLength != null) {
+ adjustMapSettings();
+ }
+ if (getLeaderboardForSpecificTimePoint() == null && tailLength != null && getLeaderboard() != null && getRaceColumn() != null) {
+ loadLeaderboardForSpecificTimePoint(getLeaderboard().name, getRaceColumn().getName(), getTimer().getTime());
+ }
+ if (!adjustedCompetitorSelection && getLeaderboardForSpecificTimePoint() != null) {
+ adjustedCompetitorSelection = true;
+ updateCompetitorSelection();
+ }
+ }
+
}