diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java index 2b6cc37255d..598e62db44e 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java @@ -8,12 +8,12 @@ import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.shared.controls.AbstractSortableColumnWithMinMax; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.SortedCellTable; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; -public class LeaderboardPanelMock extends LeaderboardPanel { +public class LeaderboardPanelMock extends ClassicLeaderboardPanel { public LeaderboardPanelMock(SailingServiceAsync sailingService, String leaderboardName, ErrorReporter errorReporter, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/SixtyInchLeaderBoard.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/SixtyInchLeaderBoard.java index e7b1ed2249c..87ef3326268 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/SixtyInchLeaderBoard.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/SixtyInchLeaderBoard.java @@ -14,14 +14,14 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.CompetitorFilterPanel; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardResources; -import com.sap.sailing.gwt.ui.leaderboard.UnStyledLeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.shared.components.ComponentResources; import com.sap.sse.gwt.client.useragent.UserAgentDetails; -public class SixtyInchLeaderBoard extends UnStyledLeaderboardPanel { +public class SixtyInchLeaderBoard extends LeaderboardPanel { private static final LeaderboardResources resources = GWT.create(LeaderboardResources.class); private static final ComponentResources componentResources = GWT.create(ComponentResources.class); private static final LeaderboardTableResourcesSixty tableResources = GWT diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/leaderboard/LeaderboardWithHeaderPerspective.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/leaderboard/LeaderboardWithHeaderPerspective.java index 5f79e5a93e1..c620a7ebfcf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/leaderboard/LeaderboardWithHeaderPerspective.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/leaderboard/LeaderboardWithHeaderPerspective.java @@ -19,7 +19,7 @@ import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.player.Timer; @@ -41,7 +41,7 @@ public class LeaderboardWithHeaderPerspective extends AbstractPerspectiveComposi private final DockLayoutPanel dockPanel; private final static int SAP_HEADER_HEIGHT = 75; private final Widget currentContentWidget; - private final LeaderboardPanel leaderboardPanel; + private final ClassicLeaderboardPanel leaderboardPanel; private final StringMessages stringMessages; public LeaderboardWithHeaderPerspective(Component parent, @@ -178,7 +178,7 @@ public class LeaderboardWithHeaderPerspective extends AbstractPerspectiveComposi leaderboardPanel.removeLeaderboardUpdateListener(listener); } - private LeaderboardPanel createLeaderboardPanel(LeaderboardWithHeaderPerspectiveLifecycle lifecycle, + private ClassicLeaderboardPanel createLeaderboardPanel(LeaderboardWithHeaderPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, CompetitorSelectionProvider competitorSelectionProvider, Timer timer, @@ -187,7 +187,7 @@ public class LeaderboardWithHeaderPerspective extends AbstractPerspectiveComposi LeaderboardSettings leaderboardSettings = settings .findSettingsByComponentId(leaderboardPanelLifecycle.getComponentId()); - LeaderboardPanel leaderboardPanel = new LeaderboardPanel(this, getComponentContext(), sailingService, + ClassicLeaderboardPanel leaderboardPanel = new ClassicLeaderboardPanel(this, getComponentContext(), sailingService, asyncActionsExecutor, leaderboardSettings, /*isEmbedded*/true, /* preSelectedRace */null, competitorSelectionProvider, timer, /* leaderboardGroupName */"", diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/oldleaderboard/OldLeaderboard.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/oldleaderboard/OldLeaderboard.java index a1d8160504b..a5dd193246b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/oldleaderboard/OldLeaderboard.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/oldleaderboard/OldLeaderboard.java @@ -15,7 +15,7 @@ import com.sap.sailing.domain.common.dto.RaceColumnDTO; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter; public class OldLeaderboard extends Composite implements LeaderboardUpdateListener { @@ -32,10 +32,10 @@ public class OldLeaderboard extends Composite implements LeaderboardUpdateListen @UiField DivElement lastScoringCommentDiv; @UiField DivElement scoringSchemeDiv; - private LeaderboardPanel leaderboardPanel; + private ClassicLeaderboardPanel leaderboardPanel; private final StringMessages stringmessages; - public OldLeaderboard(LeaderboardPanel leaderboardPanel, StringMessages stringmessages) { + public OldLeaderboard(ClassicLeaderboardPanel leaderboardPanel, StringMessages stringmessages) { this.stringmessages = stringmessages; this.leaderboardPanel = leaderboardPanel; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboard.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboard.java index 922c64e9ddd..b7b12f17fbc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboard.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboard.java @@ -33,7 +33,7 @@ import com.sap.sailing.gwt.ui.client.DebugIdHelper; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter; import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator; import com.sap.sse.gwt.client.controls.busyindicator.BusyStateChangeListener; @@ -61,7 +61,7 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener @UiField BusyIndicator busyIndicator; @UiField EventRegattaLeaderboardResources local_res; - private LeaderboardPanel leaderboardPanel; + private ClassicLeaderboardPanel leaderboardPanel; private Timer autoRefreshTimer; private final OldLeaderboardDelegate delegate; @@ -206,7 +206,7 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener } } - public void setLeaderboard(LeaderboardPanel leaderboardPanel, final Timer timer) { + public void setLeaderboard(ClassicLeaderboardPanel leaderboardPanel, final Timer timer) { this.autoRefreshTimer = timer; this.leaderboardPanel = leaderboardPanel; if (leaderboardPanel.getComponentContext() == null) { @@ -274,7 +274,7 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener } } - public interface OldLeaderboardDelegate extends LeaderboardDelegate { + public interface OldLeaderboardDelegate extends LeaderboardDelegate { Element getHasLiveRaceElement(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboardDelegateFullscreenViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboardDelegateFullscreenViewer.java index c8fa7123c11..0d796ece232 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboardDelegateFullscreenViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/leaderboard/OldLeaderboardDelegateFullscreenViewer.java @@ -4,9 +4,9 @@ import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.ui.Label; import com.sap.sailing.gwt.home.desktop.partials.old.AbstractLeaderboardFullscreenViewer; import com.sap.sailing.gwt.home.desktop.partials.old.leaderboard.OldLeaderboard.OldLeaderboardDelegate; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; -public class OldLeaderboardDelegateFullscreenViewer extends AbstractLeaderboardFullscreenViewer +public class OldLeaderboardDelegateFullscreenViewer extends AbstractLeaderboardFullscreenViewer implements OldLeaderboardDelegate { private final Label hasLiveRace = new Label(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/multileaderboard/OldMultiLeaderboard.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/multileaderboard/OldMultiLeaderboard.java index bdbea64cbbe..bef334afcb7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/multileaderboard/OldMultiLeaderboard.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/old/multileaderboard/OldMultiLeaderboard.java @@ -27,7 +27,7 @@ import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings; import com.sap.sailing.gwt.ui.client.DebugIdHelper; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.MultiLeaderboardProxyPanel; import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter; import com.sap.sailing.gwt.ui.leaderboard.SelectedLeaderboardChangeListener; @@ -59,7 +59,7 @@ public class OldMultiLeaderboard extends Composite implements SelectedLeaderboar private MultiLeaderboardProxyPanel multiLeaderboardPanel; private Timer autoRefreshTimer; private final OldMultiLeaderboardDelegate delegate; - private LeaderboardPanel lastSelectedLeaderboardPanel; + private ClassicLeaderboardPanel lastSelectedLeaderboardPanel; public OldMultiLeaderboard() { this(null); @@ -232,7 +232,7 @@ public class OldMultiLeaderboard extends Composite implements SelectedLeaderboar } @Override - public void onSelectedLeaderboardChanged(LeaderboardPanel selectedLeaderboard) { + public void onSelectedLeaderboardChanged(ClassicLeaderboardPanel selectedLeaderboard) { if(lastSelectedLeaderboardPanel != null) { lastSelectedLeaderboardPanel.removeBusyStateChangeListener(this); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java index e9ebb5f4743..4b86b2ec392 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java @@ -9,7 +9,7 @@ import com.sap.sailing.gwt.ui.client.DebugIdHelper; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorLeaderboardChart; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.common.settings.Settings; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; @@ -24,7 +24,7 @@ import com.sap.sse.gwt.client.shared.perspective.ComponentContext; * @author Frank Mittag (c163874) */ public class RegattaAnalyticsDataManager { - private LeaderboardPanel leaderboardPanel; + private ClassicLeaderboardPanel leaderboardPanel; private MultiCompetitorLeaderboardChart multiCompetitorChart; private final CompetitorSelectionModel competitorSelectionProvider; @@ -44,12 +44,12 @@ public class RegattaAnalyticsDataManager { this.multiCompetitorChart = null; } - public LeaderboardPanel createLeaderboardPanel(Component parent, ComponentContext context, + public ClassicLeaderboardPanel createLeaderboardPanel(Component parent, ComponentContext context, final LeaderboardSettings leaderboardSettings, final RegattaAndRaceIdentifier preselectedRace, final String leaderboardGroupName, String leaderboardName, boolean showRaceDetails, boolean autoExpandLastRaceColumn) { if (leaderboardPanel == null) { - leaderboardPanel = new LeaderboardPanel(parent, context, sailingService, + leaderboardPanel = new ClassicLeaderboardPanel(parent, context, sailingService, asyncActionsExecutor, leaderboardSettings, true, preselectedRace, @@ -71,7 +71,7 @@ public class RegattaAnalyticsDataManager { return multiCompetitorChart; } - public LeaderboardPanel getLeaderboardPanel() { + public ClassicLeaderboardPanel getLeaderboardPanel() { return leaderboardPanel; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/SharedLeaderboardRegattaTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/SharedLeaderboardRegattaTabView.java index 5fb944c37a8..02c2ecd518f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/SharedLeaderboardRegattaTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/SharedLeaderboardRegattaTabView.java @@ -10,7 +10,7 @@ import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardUrlSettings; import com.sap.sailing.gwt.settings.client.utils.StorageDefinitionIdFactory; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.gwt.client.mutationobserver.ElementSizeMutationObserver; import com.sap.sse.gwt.client.mutationobserver.ElementSizeMutationObserver.DomMutationCallback; import com.sap.sse.gwt.client.shared.perspective.ComponentContext; @@ -33,7 +33,7 @@ public abstract class SharedLeaderboardRegattaTabView consumer) { + String placeToken, final Consumer consumer) { // FIXME remove boolean autoExpandLastRaceColumn = GwtHttpRequestUtils.getBooleanParameter( @@ -44,7 +44,7 @@ public abstract class SharedLeaderboardRegattaTabView() { @Override public void onSuccess(LeaderboardSettings leaderboardSettings) { - final LeaderboardPanel leaderboardPanel = regattaAnalyticsManager.createLeaderboardPanel(null, componentContext, // + final ClassicLeaderboardPanel leaderboardPanel = regattaAnalyticsManager.createLeaderboardPanel(null, componentContext, // leaderboardSettings, // null, // "leaderboardGroupName", // TODO: keep using magic string? ask frank! diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java index fefc27fb73d..409dd6081ac 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java @@ -29,7 +29,7 @@ import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.common.Util; /** @@ -96,9 +96,9 @@ public class RegattaCompetitorAnalyticsTabView extends SharedLeaderboardRegattaT } }; if(regattaAnalyticsManager.getLeaderboardPanel() == null) { - createSharedLeaderboardPanel(leaderboardName, regattaAnalyticsManager, currentPresenter.getUserService(), null, new Consumer() { + createSharedLeaderboardPanel(leaderboardName, regattaAnalyticsManager, currentPresenter.getUserService(), null, new Consumer() { @Override - public void consume(LeaderboardPanel object) { + public void consume(ClassicLeaderboardPanel object) { callback.run(); } }); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java index e35d7d9bf26..7e08150bd65 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java @@ -22,7 +22,7 @@ import com.sap.sailing.gwt.home.shared.partials.placeholder.InfoPlaceholder; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.gwt.client.shared.perspective.DefaultOnSettingsLoadedCallback; /** @@ -73,10 +73,10 @@ public class RegattaLeaderboardTabView extends SharedLeaderboardRegattaTabView leaderboardConsumer = new Consumer() { + ClassicLeaderboardPanel leaderboardPanel = regattaAnalyticsManager.getLeaderboardPanel(); + final Consumer leaderboardConsumer = new Consumer() { @Override - public void consume(LeaderboardPanel leaderboardPanel) { + public void consume(ClassicLeaderboardPanel leaderboardPanel) { leaderboardUpdateProvider = leaderboardPanel; leaderboardUpdateProvider.addLeaderboardUpdateListener(RegattaLeaderboardTabView.this); initWidget(ourUiBinder.createAndBindUi(RegattaLeaderboardTabView.this)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java index 91ba2730b41..4cfc339624f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java @@ -16,7 +16,7 @@ import com.sap.sailing.gwt.ui.client.DebugIdHelper; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorLeaderboardChart; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.MultiLeaderboardProxyPanel; import com.sap.sse.common.Util; import com.sap.sse.common.settings.Settings; @@ -35,7 +35,7 @@ import com.sap.sse.gwt.client.shared.perspective.ComponentContext; * @author Axel Uhl (d043530) */ public class EventSeriesAnalyticsDataManager { - private LeaderboardPanel overallLeaderboardPanel; + private ClassicLeaderboardPanel overallLeaderboardPanel; private MultiCompetitorLeaderboardChart multiCompetitorChart; private MultiLeaderboardProxyPanel multiLeaderboardPanel; @@ -57,12 +57,12 @@ public class EventSeriesAnalyticsDataManager { this.multiCompetitorChart = null; } - public LeaderboardPanel createOverallLeaderboardPanel(Component parent, ComponentContext context, + public ClassicLeaderboardPanel createOverallLeaderboardPanel(Component parent, ComponentContext context, final LeaderboardSettings leaderboardSettings, final RegattaAndRaceIdentifier preselectedRace, final String leaderboardGroupName, String leaderboardName, boolean showRaceDetails, boolean autoExpandLastRaceColumn) { if(overallLeaderboardPanel == null) { - overallLeaderboardPanel = new LeaderboardPanel(parent, context, sailingService, asyncActionsExecutor, + overallLeaderboardPanel = new ClassicLeaderboardPanel(parent, context, sailingService, asyncActionsExecutor, leaderboardSettings, true, preselectedRace, competitorSelectionProvider, timer, leaderboardGroupName, leaderboardName, errorReporter, StringMessages.INSTANCE, showRaceDetails, /* competitorSearchTextBox */ null, @@ -103,7 +103,7 @@ public class EventSeriesAnalyticsDataManager { return multiLeaderboardPanel; } - public LeaderboardPanel getLeaderboardPanel() { + public ClassicLeaderboardPanel getLeaderboardPanel() { return overallLeaderboardPanel; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SharedLeaderboardEventSeriesTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SharedLeaderboardEventSeriesTabView.java index 21253247c87..1c314d3f9ac 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SharedLeaderboardEventSeriesTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SharedLeaderboardEventSeriesTabView.java @@ -10,7 +10,7 @@ import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardUrlSettings; import com.sap.sailing.gwt.settings.client.utils.StorageDefinitionIdFactory; import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.gwt.client.shared.perspective.ComponentContext; import com.sap.sse.gwt.client.shared.perspective.ComponentContextWithSettingsStorage; import com.sap.sse.gwt.client.shared.perspective.DefaultOnSettingsLoadedCallback; @@ -31,7 +31,7 @@ public abstract class SharedLeaderboardEventSeriesTabView consumer) { + String placeToken, final Consumer consumer) { // FIXME remove boolean autoExpandLastRaceColumn = GwtHttpRequestUtils @@ -42,7 +42,7 @@ public abstract class SharedLeaderboardEventSeriesTabView() { @Override public void onSuccess(LeaderboardSettings leaderboardSettings) { - LeaderboardPanel leaderboardPanel = eventSeriesAnalyticsManager.createOverallLeaderboardPanel(null, + ClassicLeaderboardPanel leaderboardPanel = eventSeriesAnalyticsManager.createOverallLeaderboardPanel(null, componentContext, leaderboardSettings, null, "leaderboardGroupName", leaderboardName, true, // this information came from place, now hard coded. check with frank autoExpandLastRaceColumn); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java index 7ef01d972de..48ca78848fc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java @@ -25,7 +25,7 @@ import com.sap.sailing.gwt.home.desktop.places.fakeseries.SharedLeaderboardEvent import com.sap.sailing.gwt.home.shared.ExperimentalFeatures; import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.common.Util; /** @@ -78,9 +78,9 @@ public class EventSeriesCompetitorAnalyticsTabView extends SharedLeaderboardEven } }; if(eventSeriesAnalyticsManager.getLeaderboardPanel() == null) { - createSharedLeaderboardPanel(leaderboardName, eventSeriesAnalyticsManager, currentPresenter.getUserService(), null, new Consumer() { + createSharedLeaderboardPanel(leaderboardName, eventSeriesAnalyticsManager, currentPresenter.getUserService(), null, new Consumer() { @Override - public void consume(LeaderboardPanel object) { + public void consume(ClassicLeaderboardPanel object) { callback.run(); } }); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java index 013779aaec9..29978d782b5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java @@ -17,7 +17,7 @@ import com.sap.sailing.gwt.home.desktop.places.fakeseries.EventSeriesAnalyticsDa import com.sap.sailing.gwt.home.desktop.places.fakeseries.SeriesView; import com.sap.sailing.gwt.home.desktop.places.fakeseries.SharedLeaderboardEventSeriesTabView; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.gwt.client.shared.perspective.DefaultOnSettingsLoadedCallback; /** @@ -61,9 +61,9 @@ public class EventSeriesOverallLeaderboardTabView extends SharedLeaderboardEvent String leaderboardName = currentPresenter.getSeriesDTO().getLeaderboardId(); if (leaderboardName != null && !leaderboardName.isEmpty()) { EventSeriesAnalyticsDataManager eventSeriesAnalyticsManager = currentPresenter.getCtx().getAnalyticsManager(); - final Consumer leaderboardConsumer = new Consumer() { + final Consumer leaderboardConsumer = new Consumer() { @Override - public void consume(LeaderboardPanel leaderboardPanel) { + public void consume(ClassicLeaderboardPanel leaderboardPanel) { initWidget(ourUiBinder.createAndBindUi(EventSeriesOverallLeaderboardTabView.this)); leaderboard.setLeaderboard(leaderboardPanel, currentPresenter.getAutoRefreshTimer()); eventSeriesAnalyticsManager.hideCompetitorChart(); @@ -73,7 +73,7 @@ public class EventSeriesOverallLeaderboardTabView extends SharedLeaderboardEvent } } }; - LeaderboardPanel leaderboardPanel = eventSeriesAnalyticsManager.getLeaderboardPanel(); + ClassicLeaderboardPanel leaderboardPanel = eventSeriesAnalyticsManager.getLeaderboardPanel(); if(leaderboardPanel == null) { createSharedLeaderboardPanel(leaderboardName, eventSeriesAnalyticsManager, currentPresenter.getUserService(), /*FIXME placeToken */ null, leaderboardConsumer); } else if( /*FIXME placeToken not empty */ false) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java index 29b936663a4..c33f73275da 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java @@ -27,7 +27,7 @@ import com.sap.sailing.gwt.settings.client.utils.StorageDefinitionIdFactory; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.shared.EventDTO; import com.sap.sailing.gwt.ui.shared.RaceGroupDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; @@ -268,7 +268,7 @@ public class RegattaOverviewPanel extends SimplePanel { public void onSuccess(List result) { leaderboardsTabPanel.clear(); for (StrippedLeaderboardDTO leaderboard : result) { - LeaderboardPanel leaderboardPanel = new LeaderboardPanel(null, null, sailingService, + ClassicLeaderboardPanel leaderboardPanel = new ClassicLeaderboardPanel(null, null, sailingService, new AsyncActionsExecutor(), leaderboardSettings, false, /*preSelectedRace*/null, competitorSelectionProvider, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java index 9f08d5b75b7..0f3ed09dbc6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettings.java @@ -8,7 +8,7 @@ import com.sap.sailing.domain.common.DetailType; import com.sap.sailing.gwt.settings.client.settingtypes.converter.DetailTypeStringToEnumConverter; import com.sap.sailing.gwt.settings.client.settingtypes.converter.RaceColumnSelectionStrategiesStringToEnumConverter; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.common.Util; import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings; import com.sap.sse.common.settings.generic.BooleanSetting; @@ -20,7 +20,7 @@ import com.sap.sse.common.settings.generic.StringListSetting; import com.sap.sse.common.settings.generic.StringSetting; /** - * Settings for the {@link LeaderboardPanel} component. If you change here, please also visit + * Settings for the {@link ClassicLeaderboardPanel} component. If you change here, please also visit * {@link LeaderboardSettingsDialogComponent} to make the setting editable, and edit {@link LeaderboardUrlSettings} * for URL generation and parsing. * @@ -216,7 +216,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings { /** * If null, this is to mean that the race columns should not be modified by - * {@link LeaderboardPanel#updateSettings(LeaderboardSettings)}. Otherwise a + * {@link ClassicLeaderboardPanel#updateSettings(LeaderboardSettings)}. Otherwise a * live collection that reflects the current state of the settings of a leaderboard panel */ public List getNamesOfRaceColumnsToShow() { @@ -225,7 +225,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings { /** * If null, this is to mean that the race columns should not be modified by - * {@link LeaderboardPanel#updateSettings(LeaderboardSettings)}. Otherwise + * {@link ClassicLeaderboardPanel#updateSettings(LeaderboardSettings)}. Otherwise * a live collection that reflects the current state of the settings of a leaderboard panel */ public List getNamesOfRacesToShow() { @@ -234,7 +234,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings { /** * If null, this is to mean that the race columns should not be modified by - * {@link LeaderboardPanel#updateSettings(LeaderboardSettings)}. + * {@link ClassicLeaderboardPanel#updateSettings(LeaderboardSettings)}. */ public Integer getNumberOfLastRacesToShow() { return activeRaceColumnSelectionStrategy.getValue() == RaceColumnSelectionStrategies.LAST_N ? numberOfLastRacesToShow.getValue() : null; @@ -246,7 +246,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings { /** * @return if null, leave refresh interval alone (don't change in - * {@link LeaderboardPanel#updateSettings(LeaderboardSettings)} + * {@link ClassicLeaderboardPanel#updateSettings(LeaderboardSettings)} */ public Long getDelayBetweenAutoAdvancesInMilliseconds() { return delayBetweenAutoAdvancesInMilliseconds.getValue(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java index 4a8c786f8f5..d708fb39fd2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsDialogComponent.java @@ -27,7 +27,7 @@ import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint; import com.sap.sailing.gwt.ui.leaderboard.LegColumn; import com.sap.sailing.gwt.ui.leaderboard.ManeuverCountRaceColumn; -import com.sap.sailing.gwt.ui.leaderboard.UnStyledLeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.controls.IntegerBox; import com.sap.sse.gwt.client.dialog.DataEntryDialog; @@ -134,7 +134,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone int detailCountInCurrentFlowPanel = 0; List currentRaceDetailSelection = initialSettings.getRaceDetailsToShow(); FlowPanel raceDetailDialogContent = null; - for (DetailType type : UnStyledLeaderboardPanel.getAvailableRaceDetailColumnTypes()) { + for (DetailType type : LeaderboardPanel.getAvailableRaceDetailColumnTypes()) { if (detailCountInCurrentFlowPanel % 8 == 0) { raceDetailDialogContent = new FlowPanel(); raceDetailDialogContent.addStyleName("dialogInnerContent"); @@ -168,7 +168,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone int detailCountInCurrentFlowPanel = 0; List currentRaceDetailSelection = initialSettings.getRaceDetailsToShow(); FlowPanel raceStartAnalysisDialogContent = null; - for (DetailType type : UnStyledLeaderboardPanel.getAvailableRaceStartAnalysisColumnTypes()) { + for (DetailType type : LeaderboardPanel.getAvailableRaceStartAnalysisColumnTypes()) { if (detailCountInCurrentFlowPanel % 8 == 0) { raceStartAnalysisDialogContent = new FlowPanel(); raceStartAnalysisDialogContent.addStyleName("dialogInnerContent"); @@ -190,7 +190,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone FlowPanel overallDetailDialogContent = new FlowPanel(); overallDetailDialogContent.addStyleName("dialogInnerContent"); List currentOverallDetailSelection = initialSettings.getOverallDetailsToShow(); - for (DetailType type : UnStyledLeaderboardPanel.getAvailableOverallDetailColumnTypes()) { + for (DetailType type : LeaderboardPanel.getAvailableOverallDetailColumnTypes()) { CheckBox checkbox = createAndRegisterCheckbox(dialog, type, currentOverallDetailSelection.contains(type), overallDetailCheckboxes); overallDetailDialogContent.add(checkbox); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsFactory.java index 046fd17f951..4a2b8d92522 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsFactory.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/leaderboard/LeaderboardSettingsFactory.java @@ -6,7 +6,7 @@ import java.util.List; import com.sap.sailing.domain.common.DetailType; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.player.Timer.PlayModes; @@ -28,7 +28,7 @@ public class LeaderboardSettingsFactory { * if null, don't sort any race column * @param nameOfRaceColumnToShow * if null, the settings returned will cause the list of race columns shown to remain - * unchanged during {@link LeaderboardPanel#updateSettings(LeaderboardSettings)}; otherwise, the settings + * unchanged during {@link ClassicLeaderboardPanel#updateSettings(LeaderboardSettings)}; otherwise, the settings * will show the single race column identified by this argument * @param nameOfRaceToShow * alternatively to nameOfRaceColumnToShow, this argument may be used in case the race name @@ -110,7 +110,7 @@ public class LeaderboardSettingsFactory { /** * @param namesOfRaceColumnsToShow * if null, create settings which leave the list of races to show unchanged when applied - * using {@link LeaderboardPanel#updateSettings(LeaderboardSettings)}; otherwise, the list of names of + * using {@link ClassicLeaderboardPanel#updateSettings(LeaderboardSettings)}; otherwise, the list of names of * the race columns (not their races!) that will be shown, so that, e.g., an empty list causes no race * columns to be shown * @param namesOfRacesToShow diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LeaderboardUpdateListener.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LeaderboardUpdateListener.java index 271a722658a..d343df37822 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LeaderboardUpdateListener.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LeaderboardUpdateListener.java @@ -3,13 +3,13 @@ package com.sap.sailing.gwt.ui.client; import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.raceboard.RaceBoardPanel; /** * Listener interface that gets information about refresh of leaderboard. * Currently implemented by {@link RaceBoardPanel} and triggered by - * {@link LeaderboardPanel}. + * {@link ClassicLeaderboardPanel}. * * @author Simon Marcel Pamies */ diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java index 9feb2986371..2f31c40aadb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java @@ -87,7 +87,7 @@ import com.sap.sailing.gwt.ui.client.shared.racemap.BoundsUtil; import com.sap.sailing.gwt.ui.client.shared.racemap.CourseMarkOverlay; import com.sap.sailing.gwt.ui.client.shared.racemap.FixOverlay; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.raceboard.SideBySideComponentViewer; import com.sap.sailing.gwt.ui.shared.GPSFixDTO; import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType; @@ -108,7 +108,7 @@ import com.sap.sse.gwt.client.shared.perspective.ComponentContext; public class EditMarkPositionPanel extends AbstractRaceChart implements RequiresResize, SelectionChangeEvent.Handler { protected static final int FIX_OVERLAY_Z_ORDER = 230; private final RaceMap raceMap; - private final LeaderboardPanel leaderboardPanel; + private final ClassicLeaderboardPanel leaderboardPanel; private final MarksPanel marksPanel; private Series markSeries; private LinePlotOptions markSeriesPlotOptions; @@ -134,7 +134,7 @@ public class EditMarkPositionPanel extends AbstractRaceChart i private final RaceIdentifierToLeaderboardRaceColumnAndFleetMapper raceIdentifierToLeaderboardRaceColumnAndFleetMapper; public EditMarkPositionPanel(Component parent, ComponentContext context, final RaceMap raceMap, - final LeaderboardPanel leaderboardPanel, + final ClassicLeaderboardPanel leaderboardPanel, RegattaAndRaceIdentifier selectedRaceIdentifier, String leaderboardName, final StringMessages stringMessages, SailingServiceAsync sailingService, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractLeaderboardViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractLeaderboardViewer.java index d0d67a831a8..00e146e8cf3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractLeaderboardViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/AbstractLeaderboardViewer.java @@ -35,7 +35,7 @@ public abstract class AbstractLeaderboardViewer parent, ComponentContext context, SailingServiceAsync sailingService, + AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, boolean isEmbedded, + RegattaAndRaceIdentifier preSelectedRace, CompetitorSelectionProvider competitorSelectionProvider, + String leaderboardGroupName, String leaderboardName, ErrorReporter errorReporter, + StringMessages stringMessages, boolean showRaceDetails) { + super(parent, context, sailingService, asyncActionsExecutor, settings, isEmbedded, preSelectedRace, + competitorSelectionProvider, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, + showRaceDetails, resources, componentResources, tableResources); + } + + public ClassicLeaderboardPanel(Component parent, ComponentContext context, SailingServiceAsync sailingService, + AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, boolean isEmbedded, + RegattaAndRaceIdentifier preSelectedRace, CompetitorSelectionProvider competitorSelectionProvider, + Timer timer, String leaderboardGroupName, String leaderboardName, ErrorReporter errorReporter, + StringMessages stringMessages, boolean showRaceDetails, CompetitorFilterPanel competitorSearchTextBox, + boolean showSelectionCheckbox, RaceTimesInfoProvider optionalRaceTimesInfoProvider, + boolean autoExpandLastRaceColumn, boolean adjustTimerDelay, boolean autoApplyTopNFilter, + boolean showCompetitorFilterStatus, boolean enableSyncScroller) { + super(parent, context, sailingService, asyncActionsExecutor, settings, isEmbedded, preSelectedRace, + competitorSelectionProvider, timer, leaderboardGroupName, leaderboardName, errorReporter, + stringMessages, showRaceDetails, competitorSearchTextBox, showSelectionCheckbox, + optionalRaceTimesInfoProvider, autoExpandLastRaceColumn, adjustTimerDelay, autoApplyTopNFilter, + showCompetitorFilterStatus, enableSyncScroller, resources, componentResources, tableResources); + } + + public ClassicLeaderboardPanel(Component parent, ComponentContext context, SailingServiceAsync sailingService, + AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, + CompetitorSelectionProvider competitorSelectionProvider, String leaderboardName, + ErrorReporter errorReporter, StringMessages stringMessages, boolean showRaceDetails) { + super(parent, context, sailingService, asyncActionsExecutor, settings, competitorSelectionProvider, + leaderboardName, errorReporter, stringMessages, showRaceDetails, resources, componentResources, + tableResources); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompetitorFilterPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompetitorFilterPanel.java index e3d22084d0e..ebb3da95596 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompetitorFilterPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompetitorFilterPanel.java @@ -44,7 +44,7 @@ import com.sap.sse.common.filter.FilterSet; import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; /** - * A text box that belongs to a {@link LeaderboardPanel} and allows the user to search for competitors by sail number + * A text box that belongs to a {@link ClassicLeaderboardPanel} and allows the user to search for competitors by sail number * and competitor name. When the user provides a non-empty search string, a new {@link Filter} for type {@link CompetitorDTO} * will be added that accepts competitors whose {@link CompetitorDTO#getSailID() sail number} or {@link CompetitorDTO#getName() name} * matches the user input. When the text box is emptied, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/DisplayedLeaderboardRowsProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/DisplayedLeaderboardRowsProvider.java index 2fcb0773291..e33c188588b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/DisplayedLeaderboardRowsProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/DisplayedLeaderboardRowsProvider.java @@ -7,10 +7,10 @@ import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; /** - * A {@link LeaderboardPanel} does not always display all rows available. One reason for filtering can be a filter set + * A {@link ClassicLeaderboardPanel} does not always display all rows available. One reason for filtering can be a filter set * explicitly by the user. Another can be that the leaderboard panel is configured to only show entries for one * particular race. Other UI controls that need to know which rows are being shown shall not need to see the entire - * {@link LeaderboardPanel} interface. Therefore, this interface provides the capabilities needed. + * {@link ClassicLeaderboardPanel} interface. Therefore, this interface provides the capabilities needed. * * @author Axel Uhl (D043530) * diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java index 7cee2c1b808..045da93d8a5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java @@ -52,7 +52,7 @@ public abstract class ExpandableSortableColumn extends LeaderboardSortableCol private final LinkedList queuedToggleRequests; private boolean suppressSortingOnce; - private final UnStyledLeaderboardPanel leaderboardPanel; + private final LeaderboardPanel leaderboardPanel; private final Map> detailColumnsMap; private final List detailSelection; @@ -71,7 +71,7 @@ public abstract class ExpandableSortableColumn extends LeaderboardSortableCol */ private boolean expanded; - public ExpandableSortableColumn(UnStyledLeaderboardPanel leaderboardPanel, boolean enableExpansion, Cell cell, + public ExpandableSortableColumn(LeaderboardPanel leaderboardPanel, boolean enableExpansion, Cell cell, SortingOrder preferredSortingOrder, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle, List detailSelection, DisplayedLeaderboardRowsProvider displayedLeaderboardRowsProvider) { super(cell, preferredSortingOrder, displayedLeaderboardRowsProvider); @@ -87,12 +87,12 @@ public abstract class ExpandableSortableColumn extends LeaderboardSortableCol * override this method. */ protected Map> getDetailColumnMap( - UnStyledLeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle) { return Collections.emptyMap(); } - protected UnStyledLeaderboardPanel getLeaderboardPanel() { + protected LeaderboardPanel getLeaderboardPanel() { return leaderboardPanel; } @@ -239,10 +239,10 @@ public abstract class ExpandableSortableColumn extends LeaderboardSortableCol } /** * Finishes the current toggling by removing the - * {@link LeaderboardPanel#removeBusyTask() busy task} from the {@link #getLeaderboardPanel() leaderboard panel} and setting the + * {@link ClassicLeaderboardPanel#removeBusyTask() busy task} from the {@link #getLeaderboardPanel() leaderboard panel} and setting the * {@link #setTogglingInProcess(boolean) togglingInProcess flag} to false. * Afterwards, {@link #queuedToggleRequests queued toggling requests} are executed, if any. - * Make sure that this method is called only once, and only after {@link LeaderboardPanel#addBusyTask()} has been called. + * Make sure that this method is called only once, and only after {@link ClassicLeaderboardPanel#addBusyTask()} has been called. */ private void finishCurrentToggling() { getLeaderboardPanel().removeBusyTask(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index ebc28cdd0f5..c7f25c2df98 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -1,58 +1,3649 @@ package com.sap.sailing.gwt.ui.leaderboard; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import com.google.gwt.cell.client.AbstractSafeHtmlCell; +import com.google.gwt.cell.client.Cell; +import com.google.gwt.cell.client.Cell.Context; +import com.google.gwt.cell.client.CompositeCell; +import com.google.gwt.cell.client.TextCell; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.BrowserEvents; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style.FontWeight; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.resources.client.ImageResource; +import com.google.gwt.safecss.shared.SafeStyles; +import com.google.gwt.safecss.shared.SafeStylesBuilder; +import com.google.gwt.safehtml.client.SafeHtmlTemplates; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.text.shared.AbstractSafeHtmlRenderer; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.Header; +import com.google.gwt.user.cellview.client.SafeHtmlHeader; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.AbstractImagePrototype; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.DockPanel; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HasHorizontalAlignment; +import com.google.gwt.user.client.ui.HasVerticalAlignment; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.ImageResourceRenderer; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.view.client.CellPreviewEvent; +import com.google.gwt.view.client.ListDataProvider; +import com.google.gwt.view.client.MultiSelectionModel; +import com.google.gwt.view.client.SelectionChangeEvent; +import com.google.gwt.view.client.SelectionChangeEvent.Handler; +import com.sap.sailing.domain.common.DetailType; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.InvertibleComparator; +import com.sap.sailing.domain.common.LeaderboardNameConstants; +import com.sap.sailing.domain.common.MaxPointsReason; +import com.sap.sailing.domain.common.Mile; +import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.SortingOrder; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.domain.common.dto.FleetDTO; +import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO; +import com.sap.sailing.domain.common.dto.LeaderboardDTO; +import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO; +import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; +import com.sap.sailing.domain.common.dto.LegEntryDTO; +import com.sap.sailing.domain.common.dto.RaceColumnDTO; +import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter; +import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardPanelLifecycle; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings; +import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies; +import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettingsDialogComponent; +import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettingsFactory; +import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction; +import com.sap.sailing.gwt.ui.client.Collator; +import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; +import com.sap.sailing.gwt.ui.client.FlagImageResolver; +import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; +import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; +import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.client.shared.controls.AbstractSortableColumnWithMinMax; +import com.sap.sailing.gwt.ui.client.shared.controls.FlushableSortedCellTableWithStylableHeaders; +import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn; +import com.sap.sailing.gwt.ui.client.shared.filter.CompetitorRaceRankFilter; +import com.sap.sailing.gwt.ui.client.shared.filter.FilterWithUI; +import com.sap.sailing.gwt.ui.client.shared.filter.LeaderboardFetcher; +import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; +import com.sap.sailing.gwt.ui.leaderboard.DetailTypeColumn.LegDetailField; +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.Util.Pair; +import com.sap.sse.common.filter.BinaryOperator; +import com.sap.sse.common.filter.Filter; +import com.sap.sse.common.filter.FilterSet; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; +import com.sap.sse.gwt.client.async.MarkedAsyncCallback; +import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; +import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator; +import com.sap.sse.gwt.client.controls.busyindicator.BusyStateChangeListener; +import com.sap.sse.gwt.client.controls.busyindicator.BusyStateProvider; +import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator; +import com.sap.sse.gwt.client.panels.OverlayAssistantScrollPanel; +import com.sap.sse.gwt.client.player.PlayStateListener; +import com.sap.sse.gwt.client.player.TimeListener; import com.sap.sse.gwt.client.player.Timer; +import com.sap.sse.gwt.client.player.Timer.PlayModes; +import com.sap.sse.gwt.client.player.Timer.PlayStates; +import com.sap.sse.gwt.client.shared.components.AbstractCompositeComponent; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.components.ComponentResources; +import com.sap.sse.gwt.client.shared.components.IsEmbeddableComponent; +import com.sap.sse.gwt.client.shared.components.SettingsDialog; +import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.perspective.ComponentContext; +/** + * A leaderboard essentially consists of a table widget that in its columns displays the entries. + * + * @author Axel Uhl (D043530) + * + */ +public class LeaderboardPanel extends AbstractCompositeComponent + implements TimeListener, PlayStateListener, DisplayedLeaderboardRowsProvider, IsEmbeddableComponent, + CompetitorSelectionChangeListener, LeaderboardFetcher, BusyStateProvider, LeaderboardUpdateProvider { -public class LeaderboardPanel extends UnStyledLeaderboardPanel { + private final LeaderboardResources resources; + private final ComponentResources componentResources; + private final LeaderboardTableResources tableResources; + // private static final LeaderboardResources RESOURCES = GWT.create(LeaderboardResources.class); + // private static final ComponentResources COMPONENT_RESOURCES = GWT.create(ComponentResources.class); + // private static final LeaderboardTableResources tableResources = GWT.create(LeaderboardTableResources.class); - private static final LeaderboardResources resources = GWT.create(LeaderboardResources.class); - private static final ComponentResources componentResources = GWT.create(ComponentResources.class); - private static final LeaderboardTableResources tableResources = GWT.create(LeaderboardTableResources.class); + public static final String LOAD_LEADERBOARD_DATA_CATEGORY = "loadLeaderboardData"; - public LeaderboardPanel(Component parent, ComponentContext context, SailingServiceAsync sailingService, - AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, boolean isEmbedded, - RegattaAndRaceIdentifier preSelectedRace, CompetitorSelectionProvider competitorSelectionProvider, - String leaderboardGroupName, String leaderboardName, ErrorReporter errorReporter, - StringMessages stringMessages, boolean showRaceDetails) { - super(parent, context, sailingService, asyncActionsExecutor, settings, isEmbedded, preSelectedRace, - competitorSelectionProvider, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, - showRaceDetails, resources, componentResources, tableResources); + protected static final NumberFormat scoreFormat = NumberFormat.getFormat("0.##"); + + private final SailingServiceAsync sailingService; + + private static String IS_LIVE_TEXT_COLOR = "#FF0000"; + private static String DEFAULT_TEXT_COLOR = "#000000"; + + private static final String STYLE_LEADERBOARD_CONTENT = "leaderboardContent"; + private static final String STYLE_LEADERBOARD_INFO = "leaderboardInfo"; + private static final String STYLE_LEADERBOARD_TOOLBAR = "leaderboardContent-toolbar"; + private static final String STYLE_LEADERBOARD_LIVE_RACE = "leaderboardContent-liverace"; + private static RaceColumnTemplate raceColumnTemplate = new RaceColumnTemplate(); + + static class RaceColumnTemplate { + private MyTemplate template = GWT.create(MyTemplate.class); + + interface MyTemplate extends SafeHtmlTemplates { + @SafeHtmlTemplates.Template("
") + SafeHtml styledDiv(SafeStyles style); + } + + /** + * Originally in the safehtml template: color:{0}; border-bottom: 3px solid {1}; + * + * @param textColor + * @param borderColor + * @return + */ + public SafeHtml cellFrameWithTextColorAndFleetBorder(String textColor, String borderColor) { + SafeStylesBuilder sb = new SafeStylesBuilder(); + sb.trustedColor(textColor); + sb.trustedNameAndValue("border-bottom", "3px solid " + borderColor); + return template.styledDiv(sb.toSafeStyles()); + } + + /** + * + * Originally in the safehtml template: color:{0}; + * + * @param textColor + * @return + */ + public SafeHtml cellFrameWithTextColor(String textColor) { + SafeStylesBuilder sb = new SafeStylesBuilder(); + sb.trustedColor(textColor); + return template.styledDiv(sb.toSafeStyles()); + } } - public LeaderboardPanel(Component parent, ComponentContext context, SailingServiceAsync sailingService, - AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, boolean isEmbedded, - RegattaAndRaceIdentifier preSelectedRace, CompetitorSelectionProvider competitorSelectionProvider, - Timer timer, String leaderboardGroupName, String leaderboardName, ErrorReporter errorReporter, - StringMessages stringMessages, boolean showRaceDetails, CompetitorFilterPanel competitorSearchTextBox, - boolean showSelectionCheckbox, RaceTimesInfoProvider optionalRaceTimesInfoProvider, - boolean autoExpandLastRaceColumn, boolean adjustTimerDelay, boolean autoApplyTopNFilter, - boolean showCompetitorFilterStatus, boolean enableSyncScroller) { - super(parent, context, sailingService, asyncActionsExecutor, settings, isEmbedded, preSelectedRace, - competitorSelectionProvider, timer, leaderboardGroupName, leaderboardName, errorReporter, - stringMessages, showRaceDetails, competitorSearchTextBox, showSelectionCheckbox, - optionalRaceTimesInfoProvider, autoExpandLastRaceColumn, adjustTimerDelay, autoApplyTopNFilter, - showCompetitorFilterStatus, enableSyncScroller, resources, componentResources, tableResources); - } + /** + * The leaderboard name is used to + * {@link SailingServiceAsync#getLeaderboardByName(String, java.util.Date, String[], boolean, String, com.google.gwt.user.client.rpc.AsyncCallback) + * obtain the leaderboard contents} from the server. It may change in case the leaderboard is renamed. + */ + private LeaderboardSettings currentSettings; + private String leaderboardName; - public LeaderboardPanel(Component parent, ComponentContext context, SailingServiceAsync sailingService, - AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, + private final ErrorReporter errorReporter; + + public final StringMessages stringMessages; + + private final FlushableSortedCellTableWithStylableHeaders leaderboardTable; + + private final MultiSelectionModel leaderboardSelectionModel; + + private LeaderboardDTO leaderboard; + + private final TotalRankColumn totalRankColumn; + + private RaceRankColumn raceRankColumn; + + private final SelectionCheckboxColumn selectionCheckboxColumn; + + /** + * Passed to the {@link ManeuverCountRaceColumn}. Modifications to this list will modify the column's children list + * when updated the next time. + */ + private final List selectedManeuverDetails = new ArrayList();; + + /** + * Passed to the {@link LegColumn}. Modifications to this list will modify the column's children list when updated + * the next time. + */ + private final List selectedLegDetails = new ArrayList(); + + /** + * Passed to the {@link TextRaceColumn}. Modifications to this list will modify the column's children list when + * updated the next time. + */ + private final List selectedRaceDetails = new ArrayList(); + + private final List selectedOverallDetailColumns = new ArrayList();; + + private final Map> overallDetailColumnMap; + + private RaceColumnSelection raceColumnSelection; + + protected final String RACE_COLUMN_HEADER_STYLE; + + protected final String LEG_DETAIL_COLUMN_HEADER_STYLE; + + protected final String LEG_DETAIL_COLUMN_STYLE; + + protected final String LEG_COLUMN_HEADER_STYLE; + + protected final String RACE_COLUMN_STYLE; + + protected final String LEG_COLUMN_STYLE; + + protected final String TOTAL_COLUMN_STYLE; + + private final Timer timer; + + /** + * A {@link LeaderboardDTO} tells something about the live delay through its + * {@link LeaderboardDTO#getDelayToLiveInMillisForLatestRace()} method. If this flag is true, the live + * delay in the {@link #timer} will be adjusted each time the {@link #updateLeaderboard(LeaderboardDTO)} method is + * invoked. Otherwise, the timer's delay will be left alone which is helpful, e.g., if the leaderboard panel is + * embedded in a race board panel that focuses on one particular race which may not be the same as the one + * controlling the leaderboard's overall live delay. + */ + private final boolean adjustTimerDelay; + + private boolean autoExpandPreSelectedRace; + + private boolean autoExpandLastRaceColumn; + + /** + * When true, the race columns don't display the competitors' scores in the race represented by the + * column but the cumulative score up to that race. + */ + private boolean showAddedScores; + + private boolean showCompetitorSailId; + private boolean showCompetitorFullName; + + /** + * When true then an additional column just before the overall points is displayed that sums up the + * number of races sailed per competitor. + */ + private boolean showOverallColumnWithNumberOfRacesCompletedPerCompetitor; + + /** + * Remembers whether the auto-expand of the pre-selected race (see {@link #autoExpandPreSelectedRace}) or last + * selected race {@link #autoExpandLastRaceColumn} has been performed once. It must not be performed another time. + */ + private boolean autoExpandPerformedOnce; + + /** + * This anchor's HTML holds the image tag for the play/pause button that needs to be updated when the {@link #timer} + * changes its playing state + */ + protected Anchor playPause; + + private final CompetitorSelectionProvider competitorSelectionProvider; + private final HorizontalPanel filterControlPanel; + private Label filterStatusLabel; + private Button filterClearButton; + + /** + * The handler for changes in the leaderboard table's selection; its registration is kept in + * {@link #leaderboardAsTableSelectionModelRegistration} while it is registered as a selection handler on the + * {@link #leaderboardTable}. + */ + private final Handler selectionChangeHandler; + + /** + * While the {@link #selectionChangeHandler} is registered as a selection change handler on the + * {@link #leaderboardTable}'s selection model, this field holds the registration which can be used to remove the + * registration again. We'll use this to temporarily suspend selection events when actively modifying / adjusting + * the table selection to match the {@link #competitorSelectionProvider}. + */ + private HandlerRegistration leaderboardAsTableSelectionModelRegistration; + + /** + * If this is null, all leaderboard columns added by updating the leaderboard from the server are + * automatically added to the table. Otherwise, only the column whose {@link RaceColumnDTO#getRaceIdentifier(String) + * race identifier} matches the value of this attribute will be added. + */ + private final RegattaAndRaceIdentifier preSelectedRace; + + protected final FlowPanel contentPanel = new FlowPanel(); + + private HorizontalPanel refreshAndSettingsPanel; + private Label scoreCorrectionLastUpdateTimeLabel; + private Label scoreCorrectionCommentLabel; + private Label liveRaceLabel; + + private boolean isEmbedded; + + private ImageResource pauseIcon; + private ImageResource playIcon; + + /** + * For a leaderboard, zero or more tasks may be currently busy. The counter keeps track. If it goes to {@code 0}, + * the {@link #busyIndicator} is set to non-busy. If it goes from {@code 0} to {@code 1} the {@link #busyIndicator} + * is set to busy. + */ + private int busyTaskCounter; + private final BusyIndicator busyIndicator; + private final Set busyStateChangeListeners; + + private final AsyncActionsExecutor asyncActionsExecutor; + + /** + * See also {@link #getDefaultSortColumn()}. If no other column is explicitly selected for sorting and this + * attribute holds a non-null string identifying a valid race by name that is represented in this + * leaderboard panel then sort by it. Otherwise, default sorting will default to the overall rank column. + */ + private String raceNameForDefaultSorting; + + /** + * Can be used to disallow users to drill into the race details. + */ + private final boolean showRaceDetails; + + /** + * The {@link LastNRacesColumnSelection} column selection strategy requires a {@link RaceTimesInfoProvider}. This + * can either be injected by passing a non-null object of that type to the constructor, or such an + * object is created and remembered in this attribute when required the first time. + */ + private RaceTimesInfoProvider raceTimesInfoProvider; + private RaceTimesInfoProviderListener raceTimesInfoProviderListener; + + private int blurInOnSelectionChanged; + + /** + * When an element in the leaderboard receives focus, it needs to be blurred again to keep the surrounding scroll + * panel from scrolling anything into view + */ + private Element elementToBlur; + private boolean showSelectionCheckbox; + + private final List leaderboardUpdateListener = new ArrayList(); + + private boolean initialCompetitorFilterHasBeenApplied = false; + private final boolean showCompetitorFilterStatus; + + private CompetitorFilterPanel competitorFilterPanel; + + /** + * Whether or not a second scroll bar, synchronized with the invisible native scroll bar, shall appear at the bottom + * of the viewport. See {@link OverlayAssistantScrollPanel}. + */ + private final boolean enableSyncedScroller; + private boolean showRaceRankColumn = false; + public boolean isShowCompetitorNationality; + + public LeaderboardPanel(Component parent, ComponentContext context, + SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, CompetitorSelectionProvider competitorSelectionProvider, String leaderboardName, - ErrorReporter errorReporter, StringMessages stringMessages, boolean showRaceDetails) { - super(parent, context, sailingService, asyncActionsExecutor, settings, competitorSelectionProvider, - leaderboardName, errorReporter, stringMessages, showRaceDetails, resources, componentResources, + ErrorReporter errorReporter, final StringMessages stringMessages, boolean showRaceDetails, + LeaderboardResources resources, ComponentResources componentResources, + LeaderboardTableResources tableResources) { + this(parent, context, sailingService, asyncActionsExecutor, settings, false, /* preSelectedRace */null, + competitorSelectionProvider, null, leaderboardName, errorReporter, stringMessages, showRaceDetails, + resources, componentResources, tableResources); + } + + public LeaderboardPanel(Component parent, ComponentContext context, + SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, + boolean isEmbedded, RegattaAndRaceIdentifier preSelectedRace, + CompetitorSelectionProvider competitorSelectionProvider, String leaderboardGroupName, + String leaderboardName, ErrorReporter errorReporter, final StringMessages stringMessages, + boolean showRaceDetails, LeaderboardResources resources, ComponentResources componentResources, + LeaderboardTableResources tableResources) { + this(parent, context, sailingService, asyncActionsExecutor, settings, isEmbedded, preSelectedRace, + competitorSelectionProvider, + new Timer( + // perform the first request as "live" but don't by default auto-play + PlayModes.Live, PlayStates.Paused, + /* delayBetweenAutoAdvancesInMilliseconds */ LeaderboardEntryPoint.DEFAULT_REFRESH_INTERVAL_MILLIS), + leaderboardGroupName, leaderboardName, errorReporter, stringMessages, showRaceDetails, + /* competitorSearchTextBox */ null, /* showSelectionCheckbox */ true, + /* optionalRaceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false, + /* adjustTimerDelay */ true, /* autoApplyTopNFilter */ false, /* showCompetitorFilterStatus */ false, + /* enableSyncScroller */ false, resources, componentResources, tableResources); + } + + public LeaderboardPanel(Component parent, ComponentContext context, + SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, + boolean isEmbedded, RegattaAndRaceIdentifier preSelectedRace, + CompetitorSelectionProvider competitorSelectionProvider, Timer timer, String leaderboardGroupName, + String leaderboardName, final ErrorReporter errorReporter, final StringMessages stringMessages, + boolean showRaceDetails, CompetitorFilterPanel competitorSearchTextBox, boolean showSelectionCheckbox, + RaceTimesInfoProvider optionalRaceTimesInfoProvider, boolean autoExpandLastRaceColumn, + boolean adjustTimerDelay, boolean autoApplyTopNFilter, boolean showCompetitorFilterStatus, + boolean enableSyncScroller, LeaderboardResources resources, ComponentResources componentResources, + LeaderboardTableResources tableResources) { + super(parent, context); + this.resources = resources; + this.componentResources = componentResources; + this.tableResources = tableResources; + this.showSelectionCheckbox = showSelectionCheckbox; + this.showRaceDetails = showRaceDetails; + this.sailingService = sailingService; + this.asyncActionsExecutor = asyncActionsExecutor; + this.isEmbedded = isEmbedded; + this.preSelectedRace = preSelectedRace; + this.competitorSelectionProvider = competitorSelectionProvider; + competitorSelectionProvider.addCompetitorSelectionChangeListener(this); + this.setLeaderboardName(leaderboardName); + this.errorReporter = errorReporter; + this.stringMessages = stringMessages; + this.raceTimesInfoProvider = optionalRaceTimesInfoProvider; + this.adjustTimerDelay = adjustTimerDelay; + this.initialCompetitorFilterHasBeenApplied = !autoApplyTopNFilter; + this.showCompetitorFilterStatus = showCompetitorFilterStatus; + this.enableSyncedScroller = enableSyncScroller; + this.autoExpandLastRaceColumn = autoExpandLastRaceColumn; + this.timer = timer; + RACE_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableRaceColumnHeader(); + LEG_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableLegColumnHeader(); + LEG_DETAIL_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableLegDetailColumnHeader(); + RACE_COLUMN_STYLE = tableResources.cellTableStyle().cellTableRaceColumn(); + LEG_COLUMN_STYLE = tableResources.cellTableStyle().cellTableLegColumn(); + LEG_DETAIL_COLUMN_STYLE = tableResources.cellTableStyle().cellTableLegDetailColumn(); + TOTAL_COLUMN_STYLE = tableResources.cellTableStyle().cellTableTotalColumn(); + overallDetailColumnMap = createOverallDetailColumnMap(); + raceNameForDefaultSorting = settings.getNameOfRaceToSort(); + if (settings.getLegDetailsToShow() != null) { + selectedLegDetails.addAll(settings.getLegDetailsToShow()); + } + if (settings.getOverallDetailsToShow() != null) { + selectedOverallDetailColumns.addAll(settings.getOverallDetailsToShow()); + } + timer.addPlayStateListener(this); + timer.addTimeListener(this); + switch (settings.getActiveRaceColumnSelectionStrategy()) { + case EXPLICIT: + if (preSelectedRace == null) { + raceColumnSelection = new ExplicitRaceColumnSelection(); + } else { + raceColumnSelection = new ExplicitRaceColumnSelectionWithPreselectedRace(preSelectedRace); + } + break; + case LAST_N: + setRaceColumnSelectionToLastNStrategy(settings.getNumberOfLastRacesToShow()); + break; + } + if (preSelectedRace != null) { + raceRankColumn = new RaceRankColumn(preSelectedRace); + } + totalRankColumn = new TotalRankColumn(); + leaderboardTable = new FlushableSortedCellTableWithStylableHeaders(/* pageSize */10000, tableResources); + leaderboardTable.addCellPreviewHandler(new CellPreviewEvent.Handler() { + @Override + public void onCellPreview(CellPreviewEvent event) { + if (BrowserEvents.FOCUS.equals(event.getNativeEvent().getType())) { + elementToBlur = event.getNativeEvent().getEventTarget().cast(); + elementToBlur.blur(); + blurInOnSelectionChanged = 2; // blur a couple of times; doing it one time only doesn't seem to work + // reliably + blurFocusedElementAfterSelectionChange(); + } + } + }); + leaderboardTable.ensureDebugId("LeaderboardCellTable"); + selectionCheckboxColumn = new LeaderboardSelectionCheckboxColumn(competitorSelectionProvider); + leaderboardTable.setWidth("100%"); + leaderboardSelectionModel = new MultiSelectionModel(); + // remember handler registration so we can temporarily remove it and re-add it to suspend selection events while + // we're actively changing it + selectionChangeHandler = new Handler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + List selection = new ArrayList(); + for (LeaderboardRowDTO row : getSelectedRows()) { + selection.add(row.competitor); + } + LeaderboardPanel.this.competitorSelectionProvider.setSelection(selection, + /* listenersNotToNotify */LeaderboardPanel.this); + if (blurInOnSelectionChanged > 0) { + blurInOnSelectionChanged--; + blurFocusedElementAfterSelectionChange(); + } + } + }; + leaderboardAsTableSelectionModelRegistration = leaderboardSelectionModel + .addSelectionChangeHandler(selectionChangeHandler); + leaderboardTable.setSelectionModel(leaderboardSelectionModel, selectionCheckboxColumn.getSelectionManager()); + SimplePanel mainPanel = new SimplePanel(); + leaderboardTable.getElement().getStyle().setMarginTop(10, Unit.PX); + contentPanel.setStyleName(STYLE_LEADERBOARD_CONTENT); + busyIndicator = new SimpleBusyIndicator(false, 0.8f); + busyIndicator.ensureDebugId("BusyIndicator"); + busyStateChangeListeners = new HashSet<>(); + // the information panel + if (!isEmbedded) { + Widget toolbarPanel = createToolbarPanel(); + contentPanel.add(toolbarPanel); + } + if (competitorSearchTextBox != null) { + competitorSearchTextBox.add(busyIndicator); + contentPanel.add(competitorSearchTextBox); + competitorSearchTextBox.getSettingsButton().addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + new SettingsDialog(LeaderboardPanel.this, stringMessages).show(); + } + }); + this.competitorFilterPanel = competitorSearchTextBox; + } + filterControlPanel = new HorizontalPanel(); + filterControlPanel.setStyleName("LeaderboardPanel-FilterControl-Panel"); + if (enableSyncedScroller) { + contentPanel.add(new OverlayAssistantScrollPanel(leaderboardTable)); + } else { + contentPanel.add(leaderboardTable); + } + if (showCompetitorFilterStatus) { + contentPanel.add(createFilterDeselectionControl()); + } + initWidget(mainPanel); + mainPanel.setWidget(contentPanel); + this.setTitle(stringMessages.leaderboard()); + this.setStyleName("leaderboardMainpanel"); + if (timer.isInitialized()) { + loadCompleteLeaderboard(/* showProgress */ false); + } + updateSettings(settings); + } + + public void scrollRowIntoView(int selected) { + leaderboardTable.getRowElement(selected).scrollIntoView(); + } + + protected Widget createToolbarPanel() { + GWT.log("createToolbarPanel()"); + FlowPanel informationPanel = new FlowPanel(); + informationPanel.setStyleName(STYLE_LEADERBOARD_INFO); + scoreCorrectionLastUpdateTimeLabel = new Label(""); + scoreCorrectionCommentLabel = new Label(""); + informationPanel.add(scoreCorrectionCommentLabel); + informationPanel.add(scoreCorrectionLastUpdateTimeLabel); + + liveRaceLabel = new Label(stringMessages.live()); + liveRaceLabel.setStyleName(STYLE_LEADERBOARD_LIVE_RACE); + liveRaceLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD); + liveRaceLabel.getElement().getStyle().setColor(IS_LIVE_TEXT_COLOR); + liveRaceLabel.setVisible(false); + informationPanel.add(liveRaceLabel); + + // the toolbar panel + DockPanel toolbarPanel = new DockPanel(); + toolbarPanel.ensureDebugId("ToolbarPanel"); + toolbarPanel.setStyleName(STYLE_LEADERBOARD_TOOLBAR); + toolbarPanel.add(informationPanel, DockPanel.WEST); + toolbarPanel.add(busyIndicator, DockPanel.WEST); + toolbarPanel.setWidth("100%"); + toolbarPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); + ClickHandler playPauseHandler = new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (LeaderboardPanel.this.timer.getPlayState() == PlayStates.Playing) { + LeaderboardPanel.this.timer.pause(); + } else { + // playing the standalone leaderboard means putting it into live mode + LeaderboardPanel.this.timer.setPlayMode(PlayModes.Live); + } + } + }; + pauseIcon = resources.autoRefreshEnabledIcon(); + playIcon = resources.autoRefreshDisabledIcon(); + refreshAndSettingsPanel = new HorizontalPanel(); + refreshAndSettingsPanel.ensureDebugId("RefreshAndSettingsPanel"); + refreshAndSettingsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); + FlowPanel refreshPanel = new FlowPanel(); + refreshPanel.addStyleName("refreshPanel"); + toolbarPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); + toolbarPanel.addStyleName("refreshAndSettings"); + playPause = new Anchor(getPlayPauseImgHtml(timer.getPlayState())); + playPause.ensureDebugId("PlayAndPauseAnchor"); + playPause.addClickHandler(playPauseHandler); + playStateChanged(timer.getPlayState(), timer.getPlayMode()); + refreshPanel.add(playPause); + + refreshAndSettingsPanel.add(refreshPanel); + toolbarPanel.add(refreshAndSettingsPanel, DockPanel.EAST); + return toolbarPanel; + } + + @Override + public Widget getEntryWidget() { + return this; + } + + @Override + public Widget getHeaderWidget() { + return null; + } + + @Override + public Widget getContentWidget() { + return contentPanel; + } + + @Override + public Widget getToolbarWidget() { + return null; + } + + @Override + public Widget getLegendWidget() { + return null; + } + + @Override + public boolean isEmbedded() { + return isEmbedded; + } + + public FlowPanel getContentPanel() { + return contentPanel; + } + + protected ImageResource getSettingsIcon() { + return componentResources.settingsIcon(); + } + + public void setShowCompetitorNationality(boolean isShowCompetitorNationality) { + this.isShowCompetitorNationality = isShowCompetitorNationality; + } + + public void updateSettings(final LeaderboardSettings newSettings) { + this.currentSettings = newSettings; + boolean oldShallAddOverallDetails = shallAddOverallDetails(); + + showRaceRankColumn = newSettings.isShowRaceRankColumn(); + + if (newSettings.getOverallDetailsToShow() != null) { + selectedOverallDetailColumns.clear(); + selectedOverallDetailColumns.addAll(newSettings.getOverallDetailsToShow()); + } + setShowCompetitorNationality(newSettings.isShowCompetitorNationality()); + setShowAddedScores(newSettings.isShowAddedScores()); + setShowCompetitorSailId(newSettings.isShowCompetitorSailIdColumn()); + setShowCompetitorFullName(newSettings.isShowCompetitorFullNameColumn()); + setShowOverallColumnWithNumberOfRacesCompletedPerCompetitor( + newSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor()); + final List> columnsToExpandAgain = new ArrayList>(); + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column c = getLeaderboardTable().getColumn(i); + if (c instanceof ExpandableSortableColumn) { + ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; + if (expandableSortableColumn.isExpanded()) { + // now toggle expansion back and forth, + // enforcing a re-build of the visible + // child columns + expandableSortableColumn.changeExpansionState(/* expand */ false); + columnsToExpandAgain.add(expandableSortableColumn); + } + } + } + if (newSettings.getManeuverDetailsToShow() != null) { + selectedManeuverDetails.clear(); + selectedManeuverDetails.addAll(newSettings.getManeuverDetailsToShow()); + } + if (newSettings.getLegDetailsToShow() != null) { + selectedLegDetails.clear(); + selectedLegDetails.addAll(newSettings.getLegDetailsToShow()); + } + if (newSettings.getRaceDetailsToShow() != null) { + selectedRaceDetails.clear(); + selectedRaceDetails.addAll(newSettings.getRaceDetailsToShow()); + } + + addBusyTask(); + Runnable doWhenNecessaryDetailHasBeenLoaded = new Runnable() { + @Override + public void run() { + try { + setAutoExpandPreSelectedRace(false); // avoid expansion during updateLeaderboard(...); will expand + // later + // if it was expanded before + // update leaderboard after settings panel column selection change + // update strategy for determining the race columns to show; if settings' race columns to show is + // null, use the + // previously selected race columns / number of race columns for the new configuration + Iterable oldNamesOfRaceColumnsToShow = null; + if (newSettings.getNamesOfRaceColumnsToShow() == null) { + oldNamesOfRaceColumnsToShow = raceColumnSelection.getSelectedRaceColumnNames(); + } + switch (newSettings.getActiveRaceColumnSelectionStrategy()) { + case EXPLICIT: + if (preSelectedRace == null) { + raceColumnSelection = new ExplicitRaceColumnSelection(); + } else { + raceColumnSelection = new ExplicitRaceColumnSelectionWithPreselectedRace(preSelectedRace); + } + if (newSettings.getNamesOfRaceColumnsToShow() != null) { + raceColumnSelection.requestClear(); + for (String nameOfRaceColumnToShow : newSettings.getNamesOfRaceColumnsToShow()) { + RaceColumnDTO raceColumnToShow = getRaceByColumnName(nameOfRaceColumnToShow); + if (raceColumnToShow != null) { + raceColumnSelection.requestRaceColumnSelection(raceColumnToShow); + } + } + } else { + // apply the old column selections again + for (String oldNameOfRaceColumnToShow : oldNamesOfRaceColumnsToShow) { + final RaceColumnDTO raceColumnByName = getLeaderboard() + .getRaceColumnByName(oldNameOfRaceColumnToShow); + if (raceColumnByName != null) { + raceColumnSelection.requestRaceColumnSelection(raceColumnByName); + } + } + if (newSettings.getNamesOfRacesToShow() != null) { + raceColumnSelection.requestClear(); + for (String nameOfRaceToShow : newSettings.getNamesOfRacesToShow()) { + RaceColumnDTO raceColumnToShow = getRaceByName(nameOfRaceToShow); + if (raceColumnToShow != null) { + raceColumnSelection.requestRaceColumnSelection(raceColumnToShow); + } + } + } + } + break; + case LAST_N: + setRaceColumnSelectionToLastNStrategy(newSettings.getNumberOfLastRacesToShow()); + break; + } + updateLeaderboard(leaderboard); + setAutoExpandPreSelectedRace(newSettings.isAutoExpandPreSelectedRace()); + + if (newSettings.getDelayBetweenAutoAdvancesInMilliseconds() != null) { + timer.setRefreshInterval(newSettings.getDelayBetweenAutoAdvancesInMilliseconds()); + } + for (ExpandableSortableColumn expandableSortableColumn : columnsToExpandAgain) { + expandableSortableColumn.changeExpansionState(/* expand */ true); + } + if (newSettings.getNameOfRaceToSort() != null) { + final RaceColumn raceColumnByRaceName = getRaceColumnByRaceName( + newSettings.getNameOfRaceToSort()); + if (raceColumnByRaceName != null) { + getLeaderboardTable().sortColumn(raceColumnByRaceName, /* ascending */true); + } + } + } finally { + removeBusyTask(); + } + } + }; + if (oldShallAddOverallDetails == shallAddOverallDetails() || oldShallAddOverallDetails + || getLeaderboard().hasOverallDetails()) { + doWhenNecessaryDetailHasBeenLoaded.run(); + } else { // meaning that now the details need to be loaded from the server + updateLeaderboardAndRun(doWhenNecessaryDetailHasBeenLoaded); + } + } + + /** + * @param callWhenExpansionDataIsLoaded + */ + private void updateLeaderboardAndRun(final Runnable callWhenExpansionDataIsLoaded) { + final LeaderboardDTO previousLeaderboard = getLeaderboard(); + getSailingService().getLeaderboardByName(getLeaderboardName(), + timer.getPlayMode() == PlayModes.Live ? null : getLeaderboardDisplayDate(), + /* namesOfRacesForWhichToLoadLegDetails */getNamesOfExpandedRaceColumns(), shallAddOverallDetails(), + previousLeaderboard.getId(), /* fillTotalPointsUncorrected */ false, + new MarkedAsyncCallback( + new AsyncCallback() { + @Override + public void onSuccess(IncrementalOrFullLeaderboardDTO result) { + updateLeaderboard(result.getLeaderboardDTO(previousLeaderboard)); + callWhenExpansionDataIsLoaded.run(); + } + + @Override + public void onFailure(Throwable caught) { + getErrorReporter().reportError( + stringMessages.errorTryingToObtainLeaderboardContents(caught.getMessage()), + true /* silentMode */); + } + })); + } + + public void setRaceColumnSelectionToLastNStrategy(final Integer numberOfLastRacesToShow) { + raceColumnSelection = new LastNRacesColumnSelection(numberOfLastRacesToShow, getRaceTimesInfoProvider()); + if (timer.getPlayState() != Timer.PlayStates.Playing) { + // wait for the first update and adjust leaderboard once the race times have been received + raceTimesInfoProviderListener = new RaceTimesInfoProviderListener() { + @Override + public void raceTimesInfosReceived(Map raceTimesInfo, + long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, + long clientTimeWhenResponseWasReceived) { + // remove + timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, + clientTimeWhenResponseWasReceived); + // remove the listener only in case a leaderboard has already been loaded + if (getLeaderboard() != null) { + updateLeaderboard(getLeaderboard()); + getRaceTimesInfoProvider().removeRaceTimesInfoProviderListener(raceTimesInfoProviderListener); + raceTimesInfoProviderListener = null; + } + } + }; + getRaceTimesInfoProvider().addRaceTimesInfoProviderListener(raceTimesInfoProviderListener); + } + } + + /** + * A leaderboard panel may have been provided with a valid {@link RaceTimesInfoProvider} upon creation; in this + * case, that object will be returned. If none was provided to the constructor, one is created and remembered if no + * previously created/remembered object exists. + *

+ * + * Precondition: {@link #timer} is not null + */ + private RaceTimesInfoProvider getRaceTimesInfoProvider() { + if (raceTimesInfoProvider == null) { + final List trackedRacesIdentifiers; + if (leaderboard != null && getTrackedRacesIdentifiers() != null) { + trackedRacesIdentifiers = getTrackedRacesIdentifiers(); + } else { + trackedRacesIdentifiers = Collections.emptyList(); + } + raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), asyncActionsExecutor, errorReporter, + trackedRacesIdentifiers, timer.getRefreshInterval()); + } + return raceTimesInfoProvider; + } + + public AsyncActionsExecutor getExecutor() { + return asyncActionsExecutor; + } + + protected class CompetitorColumn extends LeaderboardSortableColumnWithMinMax { + private final CompetitorColumnBase base; + + protected CompetitorColumn(CompetitorColumnBase base) { + super(base.getCell(getLeaderboard()), SortingOrder.ASCENDING, LeaderboardPanel.this); + this.base = base; + } + + public CompetitorColumn(CompositeCell compositeCell, + CompetitorColumnBase base) { + super(compositeCell, SortingOrder.ASCENDING, LeaderboardPanel.this); + this.base = base; + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + return Collator.getInstance().compare(getLeaderboard().getDisplayName(o1.competitor), + getLeaderboard().getDisplayName(o2.competitor)); + } + }; + } + + @Override + public SafeHtmlHeader getHeader() { + return base.getHeader(); + } + + @Override + public LeaderboardRowDTO getValue(LeaderboardRowDTO object) { + return object; + } + + protected void defaultRender(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { + super.render(context, object, sb); + } + + @Override + public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { + String competitorColor = LeaderboardPanel.this.competitorSelectionProvider + .getColor(object.competitor, LeaderboardPanel.this.preSelectedRace).getAsHtml(); + String competitorColorBarStyle; + if (LeaderboardPanel.this.isEmbedded && preSelectedRace != null) { + competitorColorBarStyle = "style=\"border-bottom: 2px solid " + competitorColor + ";\""; + } else { + competitorColorBarStyle = "style=\"border: none;\""; + } + base.render(object, competitorColorBarStyle, sb); + } + } + + /** + * Shows the country flag and sail ID, if present + * + * @author Axel Uhl (d043530) + * + */ + private class SailIDColumn extends LeaderboardSortableColumnWithMinMax { + private final CompetitorFetcher competitorFetcher; + + protected SailIDColumn(CompetitorFetcher competitorFetcher) { + super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this); + this.competitorFetcher = competitorFetcher; + // This style is adding to avoid contained images CSS property "max-width: 100%", which could cause + // an overflow to the next column (see https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=3537) + setCellStyleNames(tableResources.cellTableStyle().cellTableSailIdColumn()); + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(T o1, T o2) { + return competitorFetcher.getCompetitor(o1).getSailID() == null + ? competitorFetcher.getCompetitor(o2).getSailID() == null ? 0 : -1 + : competitorFetcher.getCompetitor(o2).getSailID() == null ? 1 + : Collator.getInstance().compare(competitorFetcher.getCompetitor(o1).getSailID(), + competitorFetcher.getCompetitor(o2).getSailID()); + } + }; + } + + @Override + public SafeHtmlHeader getHeader() { + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.competitor()), + stringMessages.sailIdColumnTooltip()); + } + + @Override + public void render(Context context, T object, SafeHtmlBuilder sb) { + ImageResourceRenderer renderer = new ImageResourceRenderer(); + CompetitorDTO competitor = competitorFetcher.getCompetitor(object); + final String twoLetterIsoCountryCode = competitor.getTwoLetterIsoCountryCode(); + final String flagImageURL = competitor.getFlagImageURL(); + + boolean showBoatColor = !isShowCompetitorFullName() && LeaderboardPanel.this.isEmbedded + && preSelectedRace != null; + if (showBoatColor) { + String competitorColor = LeaderboardPanel.this.competitorSelectionProvider + .getColor(competitorFetcher.getCompetitor(object), + LeaderboardPanel.this.preSelectedRace) + .getAsHtml(); + String style = determineBoatColorDivStyle(competitorColor); + sb.append(SafeHtmlUtils.fromTrustedString("

")); + } + + if (isShowCompetitorNationality || flagImageURL == null || flagImageURL.isEmpty()) { + final ImageResource nationalityFlagImageResource; + if (twoLetterIsoCountryCode==null || twoLetterIsoCountryCode.isEmpty()) { + nationalityFlagImageResource = FlagImageResolver.getEmptyFlagImageResource(); + } else { + nationalityFlagImageResource = FlagImageResolver.getFlagImageResource(twoLetterIsoCountryCode); + } + if (nationalityFlagImageResource != null) { + sb.append(renderer.render(nationalityFlagImageResource)); + sb.appendHtmlConstant(" "); + } + } + if (flagImageURL != null && !flagImageURL.isEmpty()) { + sb.appendHtmlConstant(""); + sb.appendHtmlConstant(" "); + } + sb.appendEscaped(competitor.getSailID()); + if (showBoatColor) { + sb.append(SafeHtmlUtils.fromTrustedString("
")); + } + } + + @Override + public String getValue(T object) { + return competitorFetcher.getCompetitor(object).getSailID(); + } + } + + protected String determineBoatColorDivStyle(String competitorColor) { + return "border-bottom: 2px solid " + competitorColor + ";"; + } + + protected void processStyleForRaceColumnWithoutReasonForMaxPoints(boolean isDiscarded, SafeStylesBuilder ssb) { + if (isDiscarded) { + ssb.opacity(0.5d); + } else { + ssb.fontWeight(FontWeight.BOLD); + } + } + + protected void processStyleForRaceColumnWithReasonForMaxPoints(boolean isDiscarded, SafeStylesBuilder ssb) { + ssb.opacity(0.5d); + } + + protected void processStyleForTotalNetPointsColumn(String textColor, SafeStylesBuilder ssb) { + ssb.fontWeight(FontWeight.BOLD); + ssb.trustedColor(textColor); + } + + /** + * Displays net/total points and possible max-points reasons based on a {@link LeaderboardRowDTO} and a race name + * and makes the column sortable by the total points. + * + * @author Axel Uhl (D043530) + * + */ + protected abstract class RaceColumn extends ExpandableSortableColumn { + private RaceColumnDTO race; + + private final String headerStyle; + private final String columnStyle; + + public RaceColumn(RaceColumnDTO race, boolean enableExpansion, Cell cell, SortingOrder preferredSortingOrder, + String headerStyle, String columnStyle) { + super(LeaderboardPanel.this, enableExpansion, cell, preferredSortingOrder, stringMessages, + LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, selectedRaceDetails, LeaderboardPanel.this); + setHorizontalAlignment(ALIGN_CENTER); + this.race = race; + this.headerStyle = headerStyle; + this.columnStyle = columnStyle; + } + + public RaceColumnDTO getRace() { + return race; + } + + public void setRace(RaceColumnDTO race) { + this.race = race; + } + + public String getRaceColumnName() { + return race.getRaceColumnName(); + } + + public boolean isMedalRace() { + return race.isMedalRace(); + } + + public boolean isLive(FleetDTO fleetDTO) { + return race.isLive(fleetDTO, timer.getLiveTimePointInMillis()); + } + + @Override + public String getColumnStyle() { + return columnStyle; + } + + /** + * Computes added scores for this RaceColumn + */ + private double computeAddedScores(LeaderboardRowDTO object) { + double addedScores = 0; + for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { + LeaderboardEntryDTO entryBefore = object.fieldsByRaceColumnName.get(raceColumn.getName()); + if (entryBefore.netPoints != null) { + addedScores += entryBefore.netPoints; + } + // else "add 0" + if (raceColumn.getName().equals(getRaceColumnName())) { + break; // we've reached the current column - stop here + } + } + return addedScores; + } + + /** + * Displays a combination of net points and maxPointsReason in bold, transparent, strike-through, depending on + * various criteria. Here's how: + * + *
+         *                                  net points                |    maxPointsReason
+         * -------------------------------+-----------------------------+-----------------------
+         *  not discarded, no maxPoints   | bold                        | none
+         *  not discarded, maxPoints      | bold                        | transparent
+         *  discarded, no maxPoints       | transparent, strike-through | none
+         *  discarded, maxPoints          | transparent, strike-through | transparent, strike-through
+         * 
+ */ + @Override + public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder html) { + LeaderboardEntryDTO entry = object.fieldsByRaceColumnName.get(getRaceColumnName()); + if (entry != null) { + boolean isLive = isLive(entry.fleet); + final String textColor = isLive ? IS_LIVE_TEXT_COLOR : DEFAULT_TEXT_COLOR; + final String addedScores = isShowAddedScores() ? scoreFormat.format(computeAddedScores(object)) : ""; + String netOrAddedPointsAsText = isShowAddedScores() ? addedScores + : entry.netPoints == null ? "" : scoreFormat.format(entry.netPoints); + String totalOrAddedPointsAsText = isShowAddedScores() ? addedScores + : entry.totalPoints == null ? "" : scoreFormat.format(entry.totalPoints); + if (entry.fleet != null && entry.fleet.getColor() != null) { + html.append(raceColumnTemplate.cellFrameWithTextColorAndFleetBorder(textColor, + entry.fleet.getColor().getAsHtml())); + } else { + html.append(raceColumnTemplate.cellFrameWithTextColor(textColor)); + } + // don't show points if max points / penalty + if (entry.reasonForMaxPoints == null || entry.reasonForMaxPoints == MaxPointsReason.NONE) { + SafeStylesBuilder ssb = new SafeStylesBuilder(); + processStyleForRaceColumnWithoutReasonForMaxPoints(entry.discarded, ssb); + html.append(SafeHtmlUtils.fromTrustedString("")); + if (!entry.discarded) { + html.append(SafeHtmlUtils.fromTrustedString(netOrAddedPointsAsText)); + } else { + html.append(SafeHtmlUtils.fromTrustedString("")); + html.append(SafeHtmlUtils.fromTrustedString(totalOrAddedPointsAsText)); + html.append(SafeHtmlUtils.fromTrustedString("")); + } + html.append(SafeHtmlUtils.fromTrustedString("")); + + } else { + SafeStylesBuilder ssb = new SafeStylesBuilder(); + processStyleForRaceColumnWithReasonForMaxPoints(entry.discarded, ssb); + html.append(SafeHtmlUtils.fromTrustedString( + "")); + if (entry.discarded) { + html.append(SafeHtmlUtils.fromTrustedString("")); + } + html.appendEscaped( + entry.reasonForMaxPoints == MaxPointsReason.NONE ? "" : entry.reasonForMaxPoints.name()); + if (entry.discarded) { + html.append(SafeHtmlUtils.fromTrustedString("")); + } + html.append(SafeHtmlUtils.fromTrustedString("")); + } + html.append(SafeHtmlUtils.fromTrustedString("
")); + } + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + final int result; + if (isShowAddedScores()) { + double o1AddedScore = computeAddedScores(o1); + double o2AddedScore = computeAddedScores(o2); + double intermediate_result = o1AddedScore == 0. + ? (o2AddedScore == 0. ? -1. : isAscending() ? 1. : -1.) + : o2AddedScore == 0. ? (isAscending() ? -1. : 1.) : o1AddedScore - o2AddedScore; + if (!getLeaderboard().isHigherScoreBetter()) { + result = intermediate_result > 0 ? 1 : intermediate_result < 0 ? -1 : 0; + } else { + result = intermediate_result > 0 ? -1 : intermediate_result < 0 ? 1 : 0; + } + } else { + List competitorsFromBestToWorst = getLeaderboard() + .getCompetitorsFromBestToWorst(race); + int o1Rank = competitorsFromBestToWorst.indexOf(o1.competitor) + 1; + int o2Rank = competitorsFromBestToWorst.indexOf(o2.competitor) + 1; + result = o1Rank == 0 ? o2Rank == 0 ? 0 : isAscending() ? 1 : -1 + : o2Rank == 0 ? isAscending() ? -1 : 1 : o1Rank - o2Rank; + } + return result; + } + }; + } + + @Override + public String getHeaderStyle() { + return headerStyle; + } + + @Override + public Header getHeader() { + SortableExpandableColumnHeader header = new SortableExpandableColumnHeader( + /* title */race.getRaceColumnName(), + /* iconURL */race.isMedalRace() ? "/gwt/images/medal_small.png" : null, + LeaderboardPanel.this, this, stringMessages); + return header; + } + } + + public static DetailType[] getAvailableRaceDetailColumnTypes() { + return new DetailType[] { DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, + DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TRAVELED, + DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, DetailType.RACE_TIME_TRAVELED, + DetailType.RACE_CALCULATED_TIME_TRAVELED, + DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, + DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, + DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, DetailType.NUMBER_OF_MANEUVERS, + DetailType.DISPLAY_LEGS, DetailType.CURRENT_LEG, + DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, + DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, + DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL }; + } + + public int getFlagScale() { + return 1; + } + + public static DetailType[] getAvailableRaceStartAnalysisColumnTypes() { + return new DetailType[] { DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, + DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, DetailType.DISTANCE_TO_START_AT_RACE_START, + DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, DetailType.SPEED_OVER_GROUND_AT_RACE_START, + DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, + DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, DetailType.START_TACK }; + } + + public static DetailType[] getAvailableOverallDetailColumnTypes() { + return new DetailType[] { DetailType.REGATTA_RANK, DetailType.TOTAL_DISTANCE_TRAVELED, + DetailType.TOTAL_AVERAGE_SPEED_OVER_GROUND, DetailType.TOTAL_TIME_SAILED_IN_SECONDS, + DetailType.MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, DetailType.TIME_ON_TIME_FACTOR, + DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE }; + } + + private class TextRaceColumn extends RaceColumn implements RaceNameProvider { + /** + * Remembers the leg columns; null-padded, if {@link #getLegColumn(int)} asks for a column index + * not yet existing. It is important to remember the columns because column removal happens based on identity. + */ + private final List legColumns; + + public TextRaceColumn(RaceColumnDTO race, boolean expandable, SortingOrder preferredSortingOrder, + String headerStyle, String columnStyle) { + super(race, expandable, new TextCell(), preferredSortingOrder, headerStyle, columnStyle); + legColumns = new ArrayList(); + } + + public String getValue(LeaderboardRowDTO object) { + // The following code exists only for robustness. This method should never be called because + // RaceColumn implements its own render(...) method which doesn't make use of getValue(...) + final Double netPoints = object.fieldsByRaceColumnName.get(getRaceColumnName()).netPoints; + return "" + (netPoints == null ? "" : scoreFormat.format(netPoints)); + } + + @Override + protected void ensureExpansionDataIsLoaded(final Runnable callWhenExpansionDataIsLoaded) { + if (getLeaderboard().getLegCount(getRaceColumnName(), preSelectedRace) != -1) { + callWhenExpansionDataIsLoaded.run(); + } else { + updateLeaderboardAndRun(callWhenExpansionDataIsLoaded); + } + } + + @Override + protected Map> getDetailColumnMap( + LeaderboardPanel leaderboardPanel, StringMessages stringMessages, String detailHeaderStyle, + String detailColumnStyle) { + Map> result = new HashMap<>(); + result.put(DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL, + new TimeSinceLastGpsFixColumn( + DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL, + new RaceRatioBetweenTimeSinceLastPositionFixAndAverageSamplingInterval(), + LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); + result.put(DetailType.RACE_DISTANCE_TRAVELED, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TRAVELED, + new RaceDistanceTraveledInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, + new RaceDistanceTraveledIncludingGateStartInMeters(), LEG_COLUMN_HEADER_STYLE, + LEG_COLUMN_STYLE, LeaderboardPanel.this)); + result.put(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, + new RaceAverageSpeedInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, + new RaceGapToLeaderInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, + new RaceCurrentSpeedOverGroundInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, + new RideHeightColumn(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, + new RaceCurrentRideHeightInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, + new RaceDistanceToCompetitorFarthestAheadInMeters(), LEG_COLUMN_HEADER_STYLE, + LEG_COLUMN_STYLE, LeaderboardPanel.this)); + result.put(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, + new RaceAverageAbsoluteCrossTrackErrorInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, + new RaceAverageSignedCrossTrackErrorInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, + new FormattedDoubleDetailTypeColumn( + DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, + new DistanceToStartFiveSecondsBeforeStartInMeters(), LEG_COLUMN_HEADER_STYLE, + LEG_COLUMN_STYLE, LeaderboardPanel.this)); + result.put(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, + new SpeedFiveSecondsBeforeStartInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.DISTANCE_TO_START_AT_RACE_START, + new FormattedDoubleDetailTypeColumn(DetailType.DISTANCE_TO_START_AT_RACE_START, + new DistanceToStartAtRaceStartInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, + new FormattedDoubleDetailTypeColumn(DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, + new TimeBetweenRaceStartAndCompetitorStartInSeconds(), LEG_COLUMN_HEADER_STYLE, + LEG_COLUMN_STYLE, LeaderboardPanel.this)); + result.put(DetailType.SPEED_OVER_GROUND_AT_RACE_START, + new FormattedDoubleDetailTypeColumn(DetailType.SPEED_OVER_GROUND_AT_RACE_START, + new SpeedOverGroundAtRaceStartInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, + new FormattedDoubleDetailTypeColumn(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, + new SpeedOverGroundWhenPassingStartInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, + new FormattedDoubleDetailTypeColumn( + DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, + new DistanceToStarboardSideOfStartLineInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.START_TACK, + new StartingTackColumn(new TackWhenStarting(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE)); + result.put(DetailType.NUMBER_OF_MANEUVERS, + new ManeuverCountRaceColumn(getLeaderboardPanel(), this, stringMessages, + LeaderboardPanel.this.selectedManeuverDetails, LEG_COLUMN_HEADER_STYLE, + LEG_COLUMN_STYLE, LEG_DETAIL_COLUMN_HEADER_STYLE, LEG_DETAIL_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.CURRENT_LEG, new FormattedDoubleDetailTypeColumn(DetailType.CURRENT_LEG, + new CurrentLeg(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); + result.put(DetailType.RACE_TIME_TRAVELED, + new TimeTraveledRaceColumn(getLeaderboardPanel(), this, stringMessages, LEG_COLUMN_HEADER_STYLE, + LEG_COLUMN_STYLE, LEG_DETAIL_COLUMN_HEADER_STYLE, LEG_DETAIL_COLUMN_STYLE)); + result.put(DetailType.RACE_CALCULATED_TIME_TRAVELED, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_CALCULATED_TIME_TRAVELED, + new RaceCalculatedTimeTraveledInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, + LeaderboardPanel.this)); + result.put(DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, + new FormattedDoubleDetailTypeColumn( + DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, + new RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(), + LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); + return result; + } + + @Override + protected Iterable> getDirectChildren() { + List> result = new ArrayList<>(); + for (AbstractSortableColumnWithMinMax column : super.getDirectChildren()) { + result.add(column); + } + if (isExpanded() && selectedRaceDetails.contains(DetailType.DISPLAY_LEGS)) { + // it is important to re-use existing LegColumn objects because + // removing the columns from the table + // is based on column identity + int maxLegCount = getLeaderboard().getLegCount(getRaceColumnName(), preSelectedRace); + if (maxLegCount != -1) { + for (int i = 0; i < maxLegCount; i++) { + LegColumn legColumn = getLegColumn(i); + result.add(legColumn); + } + } else { + // the race is no longer part of the LeaderboardDTO; consider the non-null legs in legColumns: + for (LegColumn legColumn : legColumns) { + if (legColumn != null) { + result.add(legColumn); + } + } + } + } + return result; + } + + private LegColumn getLegColumn(int legNumber) { + LegColumn result; + if (legColumns.size() > legNumber && legColumns.get(legNumber) != null) { + result = legColumns.get(legNumber); + } else { + result = new LegColumn(LeaderboardPanel.this, getRaceColumnName(), legNumber, + SortingOrder.ASCENDING, stringMessages, Collections.unmodifiableList(selectedLegDetails), + LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LEG_DETAIL_COLUMN_HEADER_STYLE, + LEG_DETAIL_COLUMN_STYLE); + while (legColumns.size() <= legNumber) { + legColumns.add(null); + } + legColumns.set(legNumber, result); + } + return result; + } + + /** + * Reports the ratio of the time that passed since the last position fix and the average sampling interval of + * the competitor's track. On a perfect track, this value will never exceed 1.0. Immediately when a fix is + * received, this value goes to 0.0. For a competitor whose tracker is lagging, this value can grow considerably + * greater than 1. The value goes to null if there are no fixes in the track. + * + * @author Axel Uhl (D043530) + * + */ + private class RaceRatioBetweenTimeSinceLastPositionFixAndAverageSamplingInterval + implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null && fieldsForRace.timeSinceLastPositionFixInSeconds != null + && fieldsForRace.averageSamplingInterval != null) { + result = fieldsForRace.timeSinceLastPositionFixInSeconds + / fieldsForRace.averageSamplingInterval.asSeconds(); + } + return result; + } + } + + /** + * Accumulates the average speed over all legs of a race + * + * @author Axel Uhl (D043530) + */ + private class RaceAverageSpeedInKnots implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + final Distance distanceTraveledInMeters = row.getDistanceTraveledInMeters(getRaceColumnName()); + final Duration time = row.getTimeSailed(getRaceColumnName()); + final Double result; + if (distanceTraveledInMeters != null && time != null) { + result = distanceTraveledInMeters.inTime(time).getKnots(); + } else { + result = null; + } + return result; + } + } + + /** + * Fetches the average absolute (distance always counted as positive, no matter whether left or right) + * cross-track error for the race + * + * @author Axel Uhl (D043530) + */ + private class RaceAverageAbsoluteCrossTrackErrorInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.averageAbsoluteCrossTrackErrorInMeters; + } + return result; + } + } + + /** + * Fetches the average signed (right course side is positive, left course side is negative) cross-track error + * for the race + * + * @author Axel Uhl (D043530) + */ + private class RaceAverageSignedCrossTrackErrorInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.averageSignedCrossTrackErrorInMeters; + } + return result; + } + } + + /** + * Fetches the competitor's distance to the start mark at the time the race started + * + * @author Axel Uhl (D043530) + */ + private class DistanceToStartAtRaceStartInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.distanceToStartLineAtStartOfRaceInMeters; + } + return result; + } + } + + /** + * Fetches the time between start of race and the competitors start mark passing, telling how long after the gun + * the competitor actually started. + * + * @author Axel Uhl (D043530) + */ + private class TimeBetweenRaceStartAndCompetitorStartInSeconds implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.timeBetweenRaceStartAndCompetitorStartInSeconds; + } + return result; + } + } + + /** + * Fetches the competitor's speed over ground at the time the race started + * + * @author Axel Uhl (D043530) + */ + private class SpeedOverGroundAtRaceStartInKnots implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.speedOverGroundAtStartOfRaceInKnots; + } + return result; + } + } + + /** + * Fetches the competitor's distance to the start mark five seconds before the race started + */ + private class DistanceToStartFiveSecondsBeforeStartInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.distanceToStartLineFiveSecondsBeforeStartInMeters; + } + return result; + } + } + + /** + * Fetches the competitor's speed over ground five seconds before the race started + */ + private class SpeedFiveSecondsBeforeStartInKnots implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.speedOverGroundFiveSecondsBeforeStartInKnots; + } + return result; + } + } + + /** + * Fetches the competitor's speed over ground at the time the competitor passed the start + * + * @author Axel Uhl (D043530) + */ + private class SpeedOverGroundWhenPassingStartInKnots implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.speedOverGroundAtPassingStartWaypointInKnots; + } + return result; + } + } + + /** + * Fetches the competitor's distance to the starboard side of the start line when competitor passed the start. + * If the start waypoint is not a gate/line, the distance to the single buoy is used. + * + * @author Axel Uhl (D043530) + */ + private class DistanceToStarboardSideOfStartLineInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.distanceToStarboardSideOfStartLineInMeters; + } + return result; + } + } + + /** + * Fetches the competitor's speed over ground at the time the competitor passed the start + * + * @author Axel Uhl (D043530) + */ + private class TackWhenStarting implements LegDetailField { + @Override + public Tack get(LeaderboardRowDTO row) { + Tack result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.startTack; + } + return result; + } + } + + /** + * Accumulates the distance traveled over all legs of a race + * + * @author Axel Uhl (D043530) + */ + private class RaceDistanceTraveledInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Distance distanceForRaceColumn = row.getDistanceTraveledInMeters(getRaceColumnName()); + return distanceForRaceColumn == null ? null : distanceForRaceColumn.getMeters(); + } + } + + private class RaceCalculatedTimeTraveledInSeconds implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.calculatedTime == null ? null : fieldsForRace.calculatedTime.asSeconds(); + } + return result; + } + } + + private class RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds + implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null ? null + : fieldsForRace.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.asSeconds(); + } + return result; + } + } + + /** + * Accumulates the distance traveled over all legs of a race and considers the specifics of a gate start. The + * first leg of a gate start gets as additional distance traveled the distance from the port side of the line to + * the point where the competitor started which helps normalize the distance traveled and make them comparable + * across early and late starters. + * + * @author Axel Uhl (D043530) + */ + private class RaceDistanceTraveledIncludingGateStartInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null && fieldsForRace.legDetails != null) { + for (LegEntryDTO legDetail : fieldsForRace.legDetails) { + if (legDetail != null) { + if (legDetail.distanceTraveledIncludingGateStartInMeters != null) { + if (result == null) { + result = 0.0; + } + result += legDetail.distanceTraveledIncludingGateStartInMeters; + } + } + } + } + return result; + } + } + + private class CurrentLeg implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null && fieldsForRace.legDetails != null && !fieldsForRace.legDetails.isEmpty()) { + for (LegEntryDTO legDetail : fieldsForRace.legDetails) { + if (legDetail != null) { + if (legDetail.started) { + if (result == null) { + result = 0.0; + } + result++; + } else { + if (result != null) { + result++; + } + break; + } + } + } + } + return result; + } + } + + /** + * Computes the gap to leader exploiting the ordering of the leg detail columns + * + * @author Axel Uhl (D043530) + */ + private class RaceGapToLeaderInSeconds implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.gapToLeaderInOwnTime == null ? null + : fieldsForRace.gapToLeaderInOwnTime.asSeconds(); + } + return result; + } + } + + /** + * Computes the windward distance to the competitor farthest ahead in the race (not necessarily the leader with + * handicap scoring) in meters + * + * @author Axel Uhl (D043530) + */ + private class RaceDistanceToCompetitorFarthestAheadInMeters implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null && fieldsForRace.windwardDistanceToCompetitorFarthestAheadInMeters != null) { + result = fieldsForRace.windwardDistanceToCompetitorFarthestAheadInMeters; + } + return result; + } + } + + private abstract class AbstractLastLegDetailField> implements LegDetailField { + @Override + public final T get(LeaderboardRowDTO row) { + T result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null && fieldsForRace.legDetails != null) { + int lastLegIndex = fieldsForRace.legDetails.size() - 1; + if (lastLegIndex >= 0) { + LegEntryDTO lastLegDetail = fieldsForRace.legDetails.get(lastLegIndex); + // competitor may be in leg prior to the one the leader is in; find competitors current leg + while (lastLegDetail == null && lastLegIndex > 0) { + lastLegDetail = fieldsForRace.legDetails.get(--lastLegIndex); + } + if (lastLegDetail != null) { + if (lastLegDetail.finished) { + result = getAfterLastLegFinished(row); + } else { + result = getBeforeLastLegFinished(lastLegDetail); + } + } + } + } + return result; + } + + protected abstract T getBeforeLastLegFinished(LegEntryDTO currentLegDetail); + + protected abstract T getAfterLastLegFinished(LeaderboardRowDTO row); + } + + private class RaceCurrentSpeedOverGroundInKnots extends AbstractLastLegDetailField { + @Override + protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) { + return currentLegDetail.currentSpeedOverGroundInKnots; + } + + @Override + protected Double getAfterLastLegFinished(LeaderboardRowDTO row) { + return new RaceAverageSpeedInKnots().get(row); + } + } + + private class RaceCurrentRideHeightInMeters extends AbstractLastLegDetailField { + @Override + protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) { + return currentLegDetail.currentRideHeightInMeters; + } + + @Override + protected Double getAfterLastLegFinished(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.averageRideHeightInMeters; + } + return result; + } + } + } + + /** + * Column that display the number of races a competitor has finished. Currently taking into account + * {@link MaxPointsReason#DNS}, {@link MaxPointsReason#DNF} and {@link MaxPointsReason#DNC}. Configurable over + * {@link LeaderboardSettings#isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor()}. + * + * @author Simon Marcel Pamies + */ + private class TotalRacesCompletedColumn extends LeaderboardSortableColumnWithMinMax { + private final String columnStyle; + private final MaxPointsReason[] MAX_POINTS_REASONS_THAT_IDENTIFY_NON_FINISHED_RACES = new MaxPointsReason[] { + MaxPointsReason.DNS, MaxPointsReason.DNF, MaxPointsReason.DNC }; + + protected TotalRacesCompletedColumn(String columnStyle) { + super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this); + this.columnStyle = columnStyle; + setHorizontalAlignment(ALIGN_CENTER); + } + + private int computeNumberOfRacesCompleted(LeaderboardRowDTO object) { + int racesSailedInRow = 0; + for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { + LeaderboardEntryDTO entryBefore = object.fieldsByRaceColumnName.get(raceColumn.getName()); + if (entryBefore.netPoints != null) { + if (entryBefore.reasonForMaxPoints.equals(MaxPointsReason.NONE) + || !Util.contains(Arrays.asList(MAX_POINTS_REASONS_THAT_IDENTIFY_NON_FINISHED_RACES), + entryBefore.reasonForMaxPoints)) { + racesSailedInRow++; + } + } + } + return racesSailedInRow; + } + + @Override + public String getValue(LeaderboardRowDTO object) { + return "" + computeNumberOfRacesCompleted(object); + } + + @Override + public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { + String textColor = getLeaderboard().hasLiveRace(timer.getLiveTimePointInMillis()) ? IS_LIVE_TEXT_COLOR + : DEFAULT_TEXT_COLOR; + + sb.append(SafeHtmlUtils.fromTrustedString("")); + sb.appendEscaped(getValue(object)); + sb.append(SafeHtmlUtils.fromTrustedString("")); + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + double o1RacesSailed = computeNumberOfRacesCompleted(o1); + double o2RacesSailed = computeNumberOfRacesCompleted(o2); + double result = o1RacesSailed == 0. ? o2RacesSailed == 0. ? 0. : isAscending() ? 1. : -1. + : o2RacesSailed == 0. ? isAscending() ? 1. : -1. : o2RacesSailed - o1RacesSailed; + return result > 0 ? 1 : result < 0 ? -1 : 0; + } + }; + } + + @Override + public String getColumnStyle() { + return columnStyle; + } + + @Override + public SafeHtmlHeader getHeader() { + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.racesScored()), + stringMessages.racesScoredTooltip()); + } + } + + /** + * Displays the net points totals for a competitor for the entire leaderboard. + * + * @author Axel Uhl (D043530) + * + */ + private class TotalNetPointssColumn extends LeaderboardSortableColumnWithMinMax { + private final String columnStyle; + + protected TotalNetPointssColumn(String columnStyle) { + super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this); + this.columnStyle = columnStyle; + setHorizontalAlignment(ALIGN_CENTER); + } + + @Override + public String getValue(LeaderboardRowDTO object) { + Double netPoints = object.netPoints; + return "" + (netPoints == null ? "" : scoreFormat.format(netPoints)); + } + + @Override + public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { + String textColor = getLeaderboard().hasLiveRace(timer.getLiveTimePointInMillis()) ? IS_LIVE_TEXT_COLOR + : DEFAULT_TEXT_COLOR; + + SafeStylesBuilder ssb = new SafeStylesBuilder(); + processStyleForTotalNetPointsColumn(textColor, ssb); + sb.append(SafeHtmlUtils.fromTrustedString("")); + sb.appendEscaped(getValue(object)); + sb.append(SafeHtmlUtils.fromTrustedString("")); + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + return getLeaderboard().competitors.indexOf(o1.competitor) + - getLeaderboard().competitors.indexOf(o2.competitor); + } + }; + } + + @Override + public String getColumnStyle() { + return columnStyle; + } + + @Override + public SafeHtmlHeader getHeader() { + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.total()), + stringMessages.totalNetPointsColumnTooltip()); + } + } + + protected class CarryColumn extends LeaderboardSortableColumnWithMinMax { + public CarryColumn() { + super(new AbstractSafeHtmlCell(new AbstractSafeHtmlRenderer() { + @Override + public SafeHtml render(LeaderboardRowDTO object) { + return new SafeHtmlBuilder() + .appendEscaped(object.carriedPoints == null ? "" : scoreFormat.format(object.carriedPoints)) + .toSafeHtml(); + } + }) { + @Override + protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, + SafeHtmlBuilder sb) { + sb.append(data); + } + }, SortingOrder.ASCENDING, LeaderboardPanel.this); + setSortable(true); + } + + protected CarryColumn(Cell cell) { + super(cell, SortingOrder.ASCENDING, LeaderboardPanel.this); + setSortable(true); + } + + @Override + public LeaderboardRowDTO getValue(LeaderboardRowDTO object) { + return object; + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + Double o1CarriedPoints = o1.carriedPoints; + if (o1CarriedPoints == null) { + o1CarriedPoints = 0.0; + } + Double o2CarriedPoints = o2.carriedPoints; + if (o2CarriedPoints == null) { + o2CarriedPoints = 0.0; + } + return o1CarriedPoints.compareTo(o2CarriedPoints); + } + }; + } + + @Override + public SafeHtmlHeader getHeader() { + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.carry()), + stringMessages.carryColumnTooltip()); + } + } + + private class LeaderboardSelectionCheckboxColumn + extends com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn + implements CompetitorSelectionChangeListener { + protected LeaderboardSelectionCheckboxColumn(final CompetitorSelectionProvider competitorSelectionProvider) { + super(tableResources.cellTableStyle().cellTableCheckboxSelected(), + tableResources.cellTableStyle().cellTableCheckboxDeselected(), + tableResources.cellTableStyle().cellTableCheckboxColumnCell(), + new EntityIdentityComparator() { + @Override + public boolean representSameEntity(LeaderboardRowDTO dto1, LeaderboardRowDTO dto2) { + return dto1.competitor.getIdAsString().equals(dto2.competitor.getIdAsString()); + } + + @Override + public int hashCode(LeaderboardRowDTO t) { + return t.competitor.getIdAsString().hashCode(); + } + }, getData(), leaderboardTable); + competitorSelectionProvider.addCompetitorSelectionChangeListener(this); + } + + @Override + public Boolean getValue(LeaderboardRowDTO row) { + return competitorSelectionProvider.isSelected(row.competitor); + } + + @Override + public void competitorsListChanged(Iterable competitors) { + } + + @Override + public void filterChanged(FilterSet> oldFilterSet, + FilterSet> newFilterSet) { + } + + @Override + public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { + } + + /** + * Ensure that the checkbox is redrawn when the competitor selection changes + */ + @Override + public void addedToSelection(CompetitorDTO competitor) { + final LeaderboardRowDTO row = getRow(competitor.getIdAsString()); + if (row != null) { + getSelectionModel().setSelected(row, true); + } + } + + /** + * Ensure that the checkbox is redrawn when the competitor selection changes + */ + @Override + public void removedFromSelection(CompetitorDTO competitor) { + final LeaderboardRowDTO row = getRow(competitor.getIdAsString()); + if (row != null) { + getSelectionModel().setSelected(row, false); + } + } + + @Override + public void updateMinMax() { + } + } + + private class RaceRankColumn extends LeaderboardSortableColumnWithMinMax { + public RaceRankColumn(RegattaAndRaceIdentifier preSelectedRace) { + super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this); + setHorizontalAlignment(ALIGN_CENTER); + setSortable(true); + } + + @Override + public String getValue(LeaderboardRowDTO object) { + int raceRank = getRacePlace(object); + return "" + (raceRank == 0 ? "-" : raceRank); + } + + private int getRacePlace(LeaderboardRowDTO object) { + RaceColumn raceColumn = getRaceColumnByRaceName(preSelectedRace.getRaceName()); + List competitorsSorted = getLeaderboard().getCompetitorsFromBestToWorst(raceColumn.race); + int raceRank = -1; + for (int i = 0; i < competitorsSorted.size(); i++) { + if (object.competitor.equals(competitorsSorted.get(i))) { + raceRank = i + 1; + break; + } + } + return raceRank; + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + int racePlace1 = getRacePlace(o1); + int racePlace2 = getRacePlace(o2); + return racePlace1 == 0 ? racePlace2 == 0 ? 0 : 1 : racePlace2 == 0 ? -1 : racePlace1 - racePlace2; + } + }; + } + + @Override + public SafeHtmlHeader getHeader() { + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.raceRankShort()), + stringMessages.raceRank()); + } + } + + private class TotalRankColumn extends LeaderboardSortableColumnWithMinMax { + public TotalRankColumn() { + super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this); + setHorizontalAlignment(ALIGN_CENTER); + setSortable(true); + } + + @Override + public String getValue(LeaderboardRowDTO object) { + final int totalRank = getLeaderboard().getTotalRank(object.competitor); + return "" + (totalRank == 0 ? "" : totalRank); + } + + @Override + public InvertibleComparator getComparator() { + return new InvertibleComparatorAdapter() { + @Override + public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { + final int totalRank1 = getLeaderboard().getTotalRank(o1.competitor); + final int totalRank2 = getLeaderboard().getTotalRank(o2.competitor); + return totalRank1 == 0 ? totalRank2 == 0 ? 0 : 1 : totalRank2 == 0 ? -1 : totalRank1 - totalRank2; + } + }; + } + + @Override + public SafeHtmlHeader getHeader() { + return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.totalRegattaRank()), + stringMessages.rankColumnTooltip()); + } + } + + private class StartingTackColumn extends DetailTypeColumn { + public StartingTackColumn(LegDetailField field, String headerStyle, String columnStyle) { + super(DetailType.START_TACK, field, new TextCell(), headerStyle, columnStyle, + LeaderboardPanel.this); + } + + @Override + public String getValue(LeaderboardRowDTO row) { + return getField().get(row) == null ? null + : getField().get(row) == Tack.PORT ? stringMessages.portTack() : stringMessages.starboardTack(); + } + } + + private Widget createFilterDeselectionControl() { + filterStatusLabel = new Label(); + filterStatusLabel.setStyleName("LeaderboardPanel-FilterControl-StatusLabel"); + filterStatusLabel.setText(""); + filterControlPanel.add(filterStatusLabel); + filterClearButton = new Button(stringMessages.showAll()); + filterClearButton.setStyleName("LeaderboardPanel-FilterClear-Button"); + filterClearButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + competitorFilterPanel.clearAllActiveFilters(); + loadCompleteLeaderboard(/* showProgress */ true); + } + }); + filterControlPanel.add(filterClearButton); + filterControlPanel.setCellHorizontalAlignment(filterClearButton, HasHorizontalAlignment.ALIGN_RIGHT); + setFilterControlStatus(); + return filterControlPanel; + } + + private void setFilterControlStatus() { + if (showCompetitorFilterStatus) { + boolean filtersActive = competitorSelectionProvider.hasActiveFilters(); + if (filtersActive) { + String labelText = ""; + for (Filter filter : competitorSelectionProvider.getCompetitorsFilterSet() + .getFilters()) { + if (filter instanceof FilterWithUI) { + labelText += ((FilterWithUI) filter).getLocalizedDescription(stringMessages) + + ", "; + } else { + labelText += filter.getName() + ", "; + } + } + filterStatusLabel.setText("Active Filter(s): " + labelText.substring(0, labelText.length() - 2)); + filterClearButton.setVisible(true); + filterControlPanel.setVisible(true); + } else { + filterStatusLabel.setText(""); + filterClearButton.setVisible(false); + filterControlPanel.setVisible(false); + if (competitorFilterPanel != null) { + competitorFilterPanel.clearSelection(); + } + } + } + } + + private static class TotalDistanceTraveledInMetersField implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + return row.totalDistanceTraveledInMeters; + } + } + + private static class TimeOnTimeFactorColumn implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + return row.competitor.getTimeOnTimeFactor(); + } + } + + private static class TimeOnDistanceAllowanceInSecondsPerNauticalMileColumn implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + return row.competitor.getTimeOnDistanceAllowancePerNauticalMile() == null ? null + : row.competitor.getTimeOnDistanceAllowancePerNauticalMile().asSeconds(); + } + } + + private static class TotalAverageSpeedOverGroundField implements LegDetailField { + @Override + public Double get(LeaderboardRowDTO row) { + final Double result; + if (row.totalDistanceTraveledInMeters != null && row.totalTimeSailedInSeconds != null + && row.totalTimeSailedInSeconds != 0.0) { + result = row.totalDistanceTraveledInMeters / row.totalTimeSailedInSeconds + / Mile.METERS_PER_NAUTICAL_MILE * 3600; + } else { + result = null; + } + return result; + } + } + + private Map> createOverallDetailColumnMap() { + Map> result = new HashMap<>(); + result.put(DetailType.TOTAL_DISTANCE_TRAVELED, + new FormattedDoubleDetailTypeColumn(DetailType.TOTAL_DISTANCE_TRAVELED, + new TotalDistanceTraveledInMetersField(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); + result.put(DetailType.TOTAL_AVERAGE_SPEED_OVER_GROUND, + new FormattedDoubleDetailTypeColumn(DetailType.TOTAL_AVERAGE_SPEED_OVER_GROUND, + new TotalAverageSpeedOverGroundField(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); + result.put(DetailType.MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, + new MaxSpeedOverallColumn(RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); + result.put(DetailType.TOTAL_TIME_SAILED_IN_SECONDS, createOverallTimeTraveledColumn()); + result.put(DetailType.TIME_ON_TIME_FACTOR, new FormattedDoubleDetailTypeColumn(DetailType.TIME_ON_TIME_FACTOR, + new TimeOnTimeFactorColumn(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); + result.put(DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE, + new FormattedDoubleDetailTypeColumn(DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE, + new TimeOnDistanceAllowanceInSecondsPerNauticalMileColumn(), RACE_COLUMN_HEADER_STYLE, + RACE_COLUMN_STYLE, this)); + + return result; + } + + private OverallTimeTraveledColumn createOverallTimeTraveledColumn() { + return new OverallTimeTraveledColumn(this, stringMessages, RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, + LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE); + } + + /** + * Largely for subclasses to add more stuff to this panel, such as more toolbar buttons. + */ + protected HorizontalPanel getRefreshAndSettingsPanel() { + return refreshAndSettingsPanel; + } + + private RaceColumnDTO getRaceByName(String raceName) { + if (getLeaderboard() != null) { + for (RaceColumnDTO race : getLeaderboard().getRaceList()) { + for (FleetDTO fleet : race.getFleets()) { + if (race.getRaceIdentifier(fleet) != null + && raceName.equals(race.getRaceIdentifier(fleet).getRaceName())) { + return race; + } + } + } + } + return null; + } + + private RaceColumnDTO getRaceByColumnName(String columnName) { + if (getLeaderboard() != null) { + for (RaceColumnDTO race : getLeaderboard().getRaceList()) { + if (columnName.equals(race.getRaceColumnName())) { + return race; + } + } + } + return null; + } + + private RaceColumn getRaceColumnByRaceName(String raceName) { + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column column = getLeaderboardTable().getColumn(i); + if (column instanceof RaceColumn) { + RaceColumnDTO raceInLeaderboard = ((RaceColumn) column).getRace(); + for (FleetDTO fleet : raceInLeaderboard.getFleets()) { + final RegattaAndRaceIdentifier raceIdentifier = raceInLeaderboard.getRaceIdentifier(fleet); + if (raceIdentifier != null && raceIdentifier.getRaceName().equals(raceName)) { + return (RaceColumn) column; + } + } + } + } + return null; + } + + private RaceColumn getRaceColumnByRaceColumnName(String raceColumnName) { + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column column = getLeaderboardTable().getColumn(i); + if (column instanceof RaceColumn) { + if (((RaceColumn) column).getRaceColumnName().equals(raceColumnName)) { + return (RaceColumn) column; + } + } + } + return null; + } + + private SafeHtml getPlayPauseImgHtml(PlayStates playState) { + if (playState == PlayStates.Playing) { + return AbstractImagePrototype.create(pauseIcon).getSafeHtml(); + } else { + return AbstractImagePrototype.create(playIcon).getSafeHtml(); + } + } + + private void setDelayInMilliseconds(long delayInMilliseconds) { + // If the timer is currently in a mode that causes null to be used as the leaderboard request time stamp, + // don't let a live play delay change cause another load request by unregistering this panel as timer + // listener before and re-registering it after adjusting the live play delay. + if (useNullAsTimePoint()) { + timer.removeTimeListener(this); + } + timer.setLivePlayDelayInMillis(delayInMilliseconds); + if (useNullAsTimePoint()) { + timer.addTimeListener(this); + } + } + + private boolean isAutoExpandPreSelectedRace() { + return autoExpandPreSelectedRace; + } + + private boolean isAutoExpandLastRaceColumn() { + return autoExpandLastRaceColumn; + } + + private void setAutoExpandPreSelectedRace(boolean autoExpandPreSelectedRace) { + this.autoExpandPreSelectedRace = autoExpandPreSelectedRace; + if (autoExpandPreSelectedRace) { + autoExpandPerformedOnce = false; + } + } + + private boolean isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor() { + return showOverallColumnWithNumberOfRacesCompletedPerCompetitor; + } + + private void setShowOverallColumnWithNumberOfRacesCompletedPerCompetitor( + boolean showOverallColumnWithNumberOfRacesCompletedPerCompetitor) { + this.showOverallColumnWithNumberOfRacesCompletedPerCompetitor = showOverallColumnWithNumberOfRacesCompletedPerCompetitor; + } + + private boolean isShowAddedScores() { + return showAddedScores; + } + + private void setShowAddedScores(boolean showAddedScores) { + this.showAddedScores = showAddedScores; + } + + private boolean isShowCompetitorSailId() { + return showCompetitorSailId; + } + + private void setShowCompetitorSailId(boolean showCompetitorSailId) { + this.showCompetitorSailId = showCompetitorSailId; + } + + private boolean isShowCompetitorFullName() { + return showCompetitorFullName; + } + + private void setShowCompetitorFullName(boolean showCompetitorFullName) { + this.showCompetitorFullName = showCompetitorFullName; + } + + /** + * The time point for which the leaderboard currently shows results. In {@link PlayModes#Replay replay mode} this is + * the {@link #timer}'s time point. In {@link PlayModes#Live live mode} the {@link #timer}'s time is quantized to + * the closest full second to increase the likelihood of cache hits in the back end. + */ + protected Date getLeaderboardDisplayDate() { + return timer.getTime(); + } + + /** + * adds the column to the right end of the {@link #getLeaderboardTable() leaderboard table} and sets + * the column style according to the {@link LeaderboardSortableColumnWithMinMax#getColumnStyle() column's style + * definition}. + */ + protected void addColumn(AbstractSortableColumnWithMinMax column) { + leaderboardTable.addColumn(column, column.getHeader(), column.getComparator(), + column.getPreferredSortingOrder().isAscending()); + String columnStyle = column.getColumnStyle(); + if (columnStyle != null) { + getLeaderboardTable().addColumnStyleName(getLeaderboardTable().getColumnCount() - 1, columnStyle); + } + } + + protected void insertColumn(int beforeIndex, AbstractSortableColumnWithMinMax column) { + // remove column styles of those columns whose index will shift right by + // one: + removeColumnStyles(beforeIndex); + getLeaderboardTable().insertColumn(beforeIndex, column, column.getHeader(), column.getComparator(), + column.getPreferredSortingOrder().isAscending()); + addColumnStyles(beforeIndex); + } + + private void addColumnStyles(int startColumn) { + for (int i = startColumn; i < getLeaderboardTable().getColumnCount(); i++) { + AbstractSortableColumnWithMinMax columnToRemoveStyleFor = (AbstractSortableColumnWithMinMax) getLeaderboardTable() + .getColumn(i); + String columnStyle = columnToRemoveStyleFor.getColumnStyle(); + if (columnStyle != null) { + getLeaderboardTable().addColumnStyleName(i, columnStyle); + } + } + } + + private void removeColumnStyles(int startColumn) { + for (int i = startColumn; i < getLeaderboardTable().getColumnCount(); i++) { + AbstractSortableColumnWithMinMax columnToRemoveStyleFor = (AbstractSortableColumnWithMinMax) getLeaderboardTable() + .getColumn(i); + String columnStyle = columnToRemoveStyleFor.getColumnStyle(); + if (columnStyle != null) { + getLeaderboardTable().removeColumnStyleName(i, columnStyle); + } + } + } + + /** + * removes the column specified by columnIndex from the {@link #getLeaderboardTable() leaderboard + * table} and fixes the column styles again (see {@link #addColumnStyles(int)}). + */ + protected void removeColumn(int columnIndex) { + Column c = getLeaderboardTable().getColumn(columnIndex); + if (c instanceof ExpandableSortableColumn) { + ExpandableSortableColumn expandableColumn = (ExpandableSortableColumn) c; + if (expandableColumn.isExpanded()) { + // remove expanded child columns from the leaderboard... + expandableColumn.changeExpansionState(/* expand */ false); + // them remember that column c was expanded: + expandableColumn.setExpanded(true); + } + } + removeColumnStyles(/* startColumn */columnIndex); + getLeaderboardTable().removeColumn(columnIndex); + addColumnStyles(/* startColumn */columnIndex); + } + + protected void removeColumn(Column c) { + int columnIndex = getLeaderboardTable().getColumnIndex(c); + if (columnIndex != -1) { + removeColumn(columnIndex); + } + } + + public void loadCompleteLeaderboard(final boolean showProgress) { + final Date date = getLeaderboardDisplayDate(); + if (needsDataLoading()) { + if (showProgress) { + addBusyTask(); + } + GetLeaderboardByNameAction getLeaderboardByNameAction = new GetLeaderboardByNameAction(sailingService, + getLeaderboardName(), useNullAsTimePoint() ? null : date, + /* namesOfRaceColumnsForWhichToLoadLegDetails */getNamesOfExpandedRaceColumns(), + shallAddOverallDetails(), /* previousLeaderboard */ + getLeaderboard(), isFillTotalPointsUncorrected(), timer, errorReporter, stringMessages); + this.asyncActionsExecutor.execute(getLeaderboardByNameAction, LOAD_LEADERBOARD_DATA_CATEGORY, + new AsyncCallback() { + @Override + public void onSuccess(LeaderboardDTO result) { + currentSettings = LeaderboardSettingsFactory.getInstance() + .createNewSettingsWithCustomDefaults( + new LeaderboardSettings(result.getNamesOfRaceColumns()), currentSettings); + try { + updateLeaderboard(result); + // reapply, as columns might have changed + updateSettings(currentSettings); + } finally { + if (showProgress) { + removeBusyTask(); + } + } + } + + @Override + public void onFailure(Throwable caught) { + if (showProgress) { + removeBusyTask(); + } + getErrorReporter().reportError( + "Error trying to obtain leaderboard contents: " + caught.getMessage(), + true /* silentMode */); + } + }); + } + } + + protected boolean isFillTotalPointsUncorrected() { + return false; + } + + /** + * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(boolean) loading the leaderboard + * contents}, null is used as time point. The condition for this is encapsulated in this method so + * others can find out. For example, when a time change is signaled due to local offset / delay adjustments, no + * additional call to {@link #loadCompleteLeaderboard(boolean)} would be required as null will be + * passed in any case, not being affected by local time offsets. + */ + private boolean useNullAsTimePoint() { + return timer.getPlayMode() == PlayModes.Live; + } + + private boolean needsDataLoading() { + return isVisible(); + } + + /** + * Determine from column expansion state which races need their leg details + */ + private Collection getNamesOfExpandedRaceColumns() { + Collection namesOfExpandedRaceColumns = new ArrayList(); + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column column = getLeaderboardTable().getColumn(i); + if (column instanceof RaceColumn) { + RaceColumn raceColumn = (RaceColumn) column; + if (raceColumn.isExpanded()) { + namesOfExpandedRaceColumns.add(raceColumn.getRaceColumnName()); + } + } + } + return namesOfExpandedRaceColumns; + } + + private boolean shallAddOverallDetails() { + return !selectedOverallDetailColumns.isEmpty(); + } + + private void applyTop30FilterIfCompetitorSizeGreaterEqual40(LeaderboardDTO leaderboard) { + int maxRaceRank = 30; + if (leaderboard.competitors.size() >= 40) { + CompetitorRaceRankFilter raceRankFilter = new CompetitorRaceRankFilter(); + raceRankFilter.setLeaderboardFetcher(this); + raceRankFilter.setSelectedRace(preSelectedRace); + raceRankFilter.setQuickRankProvider(this.competitorFilterPanel.getQuickRankProvider()); + raceRankFilter.setOperator(new BinaryOperator(BinaryOperator.Operators.LessThanEquals)); + raceRankFilter.setValue(maxRaceRank); + FilterSet> activeFilterSet = competitorSelectionProvider + .getOrCreateCompetitorsFilterSet(stringMessages.topNCompetitorsByRaceRank(maxRaceRank)); + activeFilterSet.addFilter(raceRankFilter); + competitorSelectionProvider.setCompetitorsFilterSet(activeFilterSet); + } + } + + /** + * Assigns leaderboard to {@link #leaderboard} and updates the UI accordingly. Also updates the min/max + * values on the columns. + */ + public void updateLeaderboard(LeaderboardDTO leaderboard) { + if (leaderboard != null) { + Collection> columnsToCollapseAndExpandAgain = getExpandedRaceColumnsWhoseDisplayedLegCountChanged( + leaderboard); + for (RaceColumn columnToCollapseAndExpandAgain : columnsToCollapseAndExpandAgain) { + columnToCollapseAndExpandAgain.changeExpansionState(/* expand */ false); + } + competitorSelectionProvider.setCompetitors(leaderboard.competitors, /* listenersNotToNotify */this); + if (!initialCompetitorFilterHasBeenApplied) { + applyTop30FilterIfCompetitorSizeGreaterEqual40(leaderboard); + initialCompetitorFilterHasBeenApplied = true; + } + raceColumnSelection.autoUpdateRaceColumnSelectionForUpdatedLeaderboard(getLeaderboard(), leaderboard); + setLeaderboard(leaderboard); + adjustColumnLayout(leaderboard); + updateRaceColumnDTOsToRaceColumns(leaderboard); + // for (RaceColumn columnToCollapseAndExpandAgain : columnsToCollapseAndExpandAgain) { + // columnToCollapseAndExpandAgain.changeExpansionState(/* expand */ true); + // } + adjustDelayToLive(); + final Map rowsToDisplay = getRowsToDisplay(); + Set rowsToAdd = new HashSet<>(rowsToDisplay.values()); + Map rowsToUpdate = new HashMap<>(); + synchronized (getData().getList()) { + int index = 0; + for (Iterator i = getData().getList().iterator(); i.hasNext();) { + LeaderboardRowDTO oldRow = i.next(); + LeaderboardRowDTO newRow = rowsToDisplay.get(oldRow.competitor); + if (newRow != null) { + rowsToUpdate.put(index++, newRow); // update row in place, preserving its selection state + rowsToAdd.remove(newRow); // no need to add this row when it was updated in-place + } else { + i.remove(); // old row's competitor not found in new rows' competitors; remove old row from + // table + } + } + for (Entry updateEntry : rowsToUpdate.entrySet()) { + LeaderboardRowDTO oldElement = getData().getList().set(updateEntry.getKey(), + updateEntry.getValue()); + leaderboardSelectionModel.setSelected(oldElement, false); // make sure the old element is no longer + // part of the selection + updateSelection(updateEntry.getValue()); + } + for (LeaderboardRowDTO rowToAdd : rowsToAdd) { + getData().getList().add(rowToAdd); + updateSelection(rowToAdd); + } + } + RaceColumn lastRaceColumn = null; + for (int i = getLeaderboardTable().getColumnCount() - 1; i >= 0; i--) { + if (getLeaderboardTable().getColumn(i) instanceof RaceColumn) { + lastRaceColumn = (RaceColumn) getLeaderboardTable().getColumn(i); + break; + } + } + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + AbstractSortableColumnWithMinMax c = (AbstractSortableColumnWithMinMax) getLeaderboardTable() + .getColumn(i); + c.updateMinMax(); + // Toggle pre-selected race, if the setting is set and it isn't open yet, or the last race column if + // that was requested + if ((!autoExpandPerformedOnce && isAutoExpandPreSelectedRace() && c instanceof RaceColumn + && ((RaceColumn) c).getRace().hasTrackedRace(preSelectedRace)) + || (isAutoExpandLastRaceColumn() && c == lastRaceColumn)) { + ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; + if (!expandableSortableColumn.isExpanded()) { + expandableSortableColumn.changeExpansionState(/* expand */ true); + autoExpandPerformedOnce = true; + } + } + if (c instanceof RaceColumn && ((RaceColumn) c).getRace().hasTrackedRace(preSelectedRace)) { + RaceColumnDTO raceColumn = ((RaceColumn) c).getRace(); + informLeaderboardUpdateListenersAboutRaceSelected(preSelectedRace, raceColumn); + } + } + if (leaderboardTable.getCurrentlySortedColumn() != null) { + leaderboardTable.sort(); + } else { + AbstractSortableColumnWithMinMax columnToSortFor = getDefaultSortColumn(); + leaderboardTable.sortColumn(columnToSortFor, columnToSortFor.getPreferredSortingOrder().isAscending()); + } + + if (!isEmbedded) { + updateToolbar(leaderboard); + } + informLeaderboardUpdateListenersAboutLeaderboardUpdated(leaderboard); + } + } + + protected void updateToolbar(LeaderboardDTO leaderboard) { + scoreCorrectionCommentLabel.setText(leaderboard.getComment() != null ? leaderboard.getComment() : ""); + if (leaderboard.getTimePointOfLastCorrectionsValidity() != null) { + Date lastCorrectionDate = leaderboard.getTimePointOfLastCorrectionsValidity(); + String lastUpdate = DateAndTimeFormatterUtil.formatLongDateAndTimeGMT(lastCorrectionDate); + scoreCorrectionLastUpdateTimeLabel.setText(stringMessages.lastScoreUpdate() + ": " + lastUpdate); + } else { + scoreCorrectionLastUpdateTimeLabel.setText(""); + } + + boolean hasLiveRace = !leaderboard.getLiveRaces(timer.getLiveTimePointInMillis()).isEmpty(); + liveRaceLabel.setText(hasLiveRace ? getLiveRacesText() : ""); + scoreCorrectionLastUpdateTimeLabel.setVisible(!hasLiveRace); + liveRaceLabel.setVisible(hasLiveRace); + } + + /** + * Adjusts the row's selection in the {@link #leaderboardSelectionModel} so it matches its selection state in the + * {@link #competitorSelectionProvider}. + */ + private void updateSelection(LeaderboardRowDTO row) { + final boolean shallBeSelected = competitorSelectionProvider.isSelected(row.competitor); + if (leaderboardAsTableSelectionModelRegistration != null) { + // suspend selection events while actively adjusting the leaderboardSelectionModel to match the + // competitorSelectionProvider + leaderboardAsTableSelectionModelRegistration.removeHandler(); + leaderboardAsTableSelectionModelRegistration = null; + } + if (leaderboardSelectionModel.isSelected(row) != shallBeSelected) { + leaderboardSelectionModel.setSelected(row, shallBeSelected); + } + // register the selection change handler again + leaderboardAsTableSelectionModelRegistration = leaderboardTable.getSelectionModel() + .addSelectionChangeHandler(selectionChangeHandler); + } + + /** + * The race columns hold a now outdated copy of a {@link RaceColumnDTO} which needs to be updated from the + * {@link LeaderboardDTO} just received + */ + private void updateRaceColumnDTOsToRaceColumns(LeaderboardDTO leaderboard) { + for (RaceColumnDTO newRace : leaderboard.getRaceList()) { + RaceColumn raceColumn = getRaceColumnByRaceColumnName(newRace.getName()); + if (raceColumn != null) { + raceColumn.setRace(newRace); + } + } + } + + /** + * Due to a course change, a race may change its number of legs. All expanded race columns that show leg columns and + * whose leg count changed need to be collapsed before the leaderboard is replaced, and expanded afterwards again. + * Race columns whose toggling is {@link ExpandableSortableColumn#isTogglingInProcess() currently in progress} are + * not considered because their new state will be considered after replacing anyhow. + * + * @param newLeaderboard + * the new leaderboard before assigning to {@link #leaderboard} + * @return the columns that were collapsed in this step and that shall be expanded again after the leaderboard has + * been replaced + */ + private Collection> getExpandedRaceColumnsWhoseDisplayedLegCountChanged( + LeaderboardDTO newLeaderboard) { + Set> result = new HashSet>(); + if (selectedRaceDetails.contains(DetailType.DISPLAY_LEGS)) { + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column c = getLeaderboardTable().getColumn(i); + if (c instanceof RaceColumn) { + RaceColumn rc = (RaceColumn) c; + // If the new leaderboard no longer contains the column, getLegCount will return -1, causing the + // column + // to be collapsed if it was expanded. This is correct because otherwise, removing it would no + // longer + // know the correct leg count. + if (!rc.isTogglingInProcess() && rc.isExpanded()) { + int oldLegCount = getLeaderboard().getLegCount(rc.getRaceColumnName(), preSelectedRace); + int newLegCount = newLeaderboard.getLegCount(rc.getRaceColumnName(), preSelectedRace); + if (oldLegCount != newLegCount) { + result.add(rc); + } + } + } + } + } + return result; + } + + /** + * Based on the {@link LeaderboardDTO#getDelayToLiveInMillisForLatestRace()} for the race that has the latest + * {@link RaceColumnDTO#getStartDate(FleetDTO) start time}, automatically adjusts the delay accordingly unless the + * {@link #adjustTimerDelay} flag is false + */ + private void adjustDelayToLive() { + if (adjustTimerDelay) { + if (leaderboard.getDelayToLiveInMillisForLatestRace() != null) { + setDelayInMilliseconds(leaderboard.getDelayToLiveInMillisForLatestRace()); + } + } + } + + /** + * Extracts the rows to display of the leaderboard. These are all {@link AbstractLeaderboardDTO#rows + * rows} in case {@link #preSelectedRace} is null, or only the rows of the competitors who scored in + * the race identified by {@link #preSelectedRace} otherwise. + */ + @Override + public Map getRowsToDisplay() { + Map result; + Iterable allFilteredCompetitors = competitorSelectionProvider.getFilteredCompetitors(); + result = new HashMap(); + if (preSelectedRace == null) { + for (CompetitorDTO competitor : leaderboard.rows.keySet()) { + if (Util.contains(allFilteredCompetitors, competitor)) { + result.put(competitor, leaderboard.rows.get(competitor)); + } + } + } else { + for (CompetitorDTO competitorInPreSelectedRace : getCompetitors(preSelectedRace)) { + if (Util.contains(allFilteredCompetitors, competitorInPreSelectedRace)) { + result.put(competitorInPreSelectedRace, leaderboard.rows.get(competitorInPreSelectedRace)); + } + } + } + return result; + } + + /** + * The {@link LeaderboardDTO} holds {@link LeaderboardDTO#getRaceList() races} as {@link RaceColumnDTO} objects. + * Those map their fleet names to the {@link RegattaAndRaceIdentifier} which identifies the tracked race + * representing the fleet race within the race column. + *

+ * + * On the other hand, a {@link LeaderboardRowDTO} has {@link LeaderboardEntryDTO}, keyed by race column name. The + * entry DTOs, in turn, store the {@link RaceIdentifier} of the race in which the respective competitor achieved the + * score. + *

+ * + * With this information it is possible to identify the competitors who participated in a particular tracked race, + * as identified through the race parameter. + * + * @return all competitors for which the {@link #getLeaderboard() leaderboard} has an entry whose + * {@link LeaderboardEntryDTO#race} equals race + */ + public Iterable getCompetitors(RaceIdentifier race) { + Set result = new HashSet(); + if (getLeaderboard() != null) { + for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { + if (raceColumn.hasTrackedRace(race)) { + for (Map.Entry e : getLeaderboard().rows.entrySet()) { + LeaderboardEntryDTO entry = e.getValue().fieldsByRaceColumnName + .get(raceColumn.getRaceColumnName()); + if (entry != null && entry.race != null && entry.race.equals(race)) { + result.add(e.getKey()); + } + } + } + } + } + return result; + } + + private AbstractSortableColumnWithMinMax getDefaultSortColumn() { + AbstractSortableColumnWithMinMax defaultSortColumn = null; + if (raceNameForDefaultSorting != null) { + defaultSortColumn = getRaceColumnByRaceName(raceNameForDefaultSorting); + } + if (defaultSortColumn == null) { + defaultSortColumn = getRankColumn(); + } + return defaultSortColumn; + } + + private TotalRankColumn getRankColumn() { + return totalRankColumn; + } + + protected void setLeaderboard(LeaderboardDTO leaderboard) { + this.leaderboard = leaderboard; + } + + @Override + public LeaderboardDTO getLeaderboard() { + return leaderboard; + } + + private void adjustColumnLayout(LeaderboardDTO leaderboard) { + int columnIndex = 0; + columnIndex = ensureRaceRankColumn(columnIndex); + columnIndex = ensureSelectionCheckboxColumn(columnIndex); + columnIndex = ensureRankColumn(columnIndex); + columnIndex = ensureSailIDAndCompetitorColumn(columnIndex); + columnIndex = updateCarryColumn(leaderboard, columnIndex); + adjustOverallDetailColumns(leaderboard, columnIndex); + // first remove race columns no longer needed: + removeUnusedRaceColumns(leaderboard); + if (leaderboard != null) { + createMissingAndAdjustExistingRaceColumns(leaderboard); + ensureTotalsColumn(); + updateTotalRacesSailedColumn(); + } + } + + /** + * Ensures that the columns requested by {@link #selectedOverallDetailColumns} are in the table. Assumes that if + * there are any existing overall details columns, they start at indexOfFirstOverallDetailsColumn and + * are in the order defined by {@link #getAvailableOverallDetailColumnTypes()}. + * + * @param indexOfFirstOverallDetailsColumn + * tells the column index for the first overall details column + */ + private void adjustOverallDetailColumns(LeaderboardDTO leaderboard, int indexOfFirstOverallDetailsColumn) { + List> overallDetailColumnsToShow = new ArrayList>(); + // ensure the ordering in overallDetailColumnsToShow conforms to the ordering of + // getAvailableOverallDetailColumnTypes() + for (DetailType overallDetailType : getAvailableOverallDetailColumnTypes()) { + if (selectedOverallDetailColumns.contains(overallDetailType) + && overallDetailColumnMap.containsKey(overallDetailType)) { + overallDetailColumnsToShow.add(overallDetailColumnMap.get(overallDetailType)); + } + } + int currentColumnIndex = indexOfFirstOverallDetailsColumn; + int i = 0; // index into overallDetailColumnToShow + Column currentColumn = currentColumnIndex < getLeaderboardTable().getColumnCount() + ? getLeaderboardTable().getColumn(currentColumnIndex) : null; + // repeat until no more column to check for removal and no more column left to check for need to insert + while (i < overallDetailColumnsToShow.size() || overallDetailColumnMap.values().contains(currentColumn)) { + if (i < overallDetailColumnsToShow.size() && currentColumn == overallDetailColumnsToShow.get(i)) { + // found selected column in table; all good, advance both "pointers" + i++; + currentColumnIndex++; + currentColumn = getLeaderboardTable().getColumn(currentColumnIndex); + } else if (i < overallDetailColumnsToShow.size()) { + // selected column is missing; insert + insertColumn(currentColumnIndex++, overallDetailColumnsToShow.get(i++)); + } else { + // based on the while's condition, currentColumn is an overallDetailsColumnMap value; + // based on the previous if's failed condition, it is not selected. Remove: + removeColumn(currentColumnIndex); + currentColumn = getLeaderboardTable().getColumn(currentColumnIndex); + } + } + } + + /** + * If header information doesn't match the race column's actual state (tracked races attached meaning expandable; + * medal race), the column is removed and inserted again + * + * @param raceColumn + * the raceColumn to correct. + * @param selectedRaceColumn + * the new race column data for raceColumn + */ + private void correctColumnData(RaceColumn raceColumn, RaceColumnDTO race) { + int columnIndex = getRaceColumnPosition(raceColumn); + if (raceColumn.isExpansionEnabled() != race.hasTrackedRaces() + || race.isMedalRace() != raceColumn.isMedalRace()) { + if (raceColumn.isExpanded()) { + raceColumn.changeExpansionState(/* expand */ false); // remove children from table + } + removeColumn(columnIndex); + insertColumn(columnIndex, createRaceColumn(race)); + } + } + + /** + * Removes all RaceColumns, starting at count {@link raceColumnStartIndex raceColumnStartIndex} + * + * @param raceColumnStartIndex + * The index of the race column should be deleted from. + * @param raceName + * The name of the racing column until the table should be cleared. + */ + private void removeRaceColumnFromRaceColumnStartIndexBeforeRace(int raceColumnStartIndex, RaceColumnDTO race) { + int counter = 0; + for (int leaderboardposition = 0; leaderboardposition < getLeaderboardTable() + .getColumnCount(); leaderboardposition++) { + Column c = getLeaderboardTable().getColumn(leaderboardposition); + if (c instanceof RaceColumn) { + RaceColumn raceColumn = (RaceColumn) c; + if (!raceColumn.getRaceColumnName().equals(race.getRaceColumnName())) { + if (raceColumnStartIndex == counter) { + removeColumn(raceColumn); + } + } else { + return; + } + counter++; + } + } + } + + /** + * Gets a ColumnPosition of a raceColumn + * + * @param raceColumn + * The column for which the position is to be found in the leaderboard table + * @return the position. Returns -1 if raceColumn not existing in leaderboardTable. + */ + private int getRaceColumnPosition(RaceColumn raceColumn) { + for (int leaderboardposition = 0; leaderboardposition < getLeaderboardTable() + .getColumnCount(); leaderboardposition++) { + Column c = getLeaderboardTable().getColumn(leaderboardposition); + if (c instanceof RaceColumn) { + RaceColumn rc = (RaceColumn) c; + if (rc.equals(raceColumn)) { + return leaderboardposition; + } + } + } + return -1; + } + + /** + * This method returns the position where a race column should get inserted. + * + * @param raceName + * the name of the race to insert + * @param listpos + * the position of the race in the {@link selectedRaceColumns selectedRaceColumns} + * @return the position of a race column right before which to insert the race column so that it is the + * listposth race in the table or -1 if no such race column was found, e.g., in case the column + * needs to be inserted as the last race in the table + */ + private int getColumnPositionToInsert(RaceColumnDTO race, int listpos) { + int raceColumnCounter = 0; + int noRaceColumnCounter = 0; + boolean raceColumnFound = false; + for (int leaderboardPosition = 0; !raceColumnFound + && leaderboardPosition < getLeaderboardTable().getColumnCount(); leaderboardPosition++) { + Column c = getLeaderboardTable().getColumn(leaderboardPosition); + if (c instanceof RaceColumn) { + // RaceColumn raceColumn = (RaceColumn) c; + if (raceColumnCounter == listpos) { + raceColumnFound = true; + } else { + raceColumnCounter++; + } + } else { + noRaceColumnCounter++; + } + } + if (raceColumnFound) { + return raceColumnCounter + noRaceColumnCounter; + } else { + return -1; + } + } + + /** + * Removes all columns of type {@link RaceColumn} from the leaderboard table if their name is not in the list of + * names of the selectedRaceColumns. + */ + private void removeRaceColumnsNotSelected(Iterable selectedRaceColumns) { + Set selectedRaceColumnNames = new HashSet(); + for (RaceColumnDTO selectedRaceColumn : selectedRaceColumns) { + selectedRaceColumnNames.add(selectedRaceColumn.getRaceColumnName()); + } + List> columnsToRemove = new ArrayList>(); + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column c = getLeaderboardTable().getColumn(i); + if (c instanceof RaceColumn && (leaderboard == null + || !selectedRaceColumnNames.contains(((RaceColumn) c).getRaceColumnName()))) { + columnsToRemove.add(c); + } + } + for (Column c : columnsToRemove) { + removeColumn(c); + } + } + + /** + * Existing and matching race columns may still need to be removed, re-created and inserted because the "tracked" + * property may have changed, changing the columns expandability. + */ + private void createMissingAndAdjustExistingRaceColumns(LeaderboardDTO leaderboard) { + // Correct order of races in selectedRaceColum + Iterable correctedOrderSelectedRaces = raceColumnSelection + .getSelectedRaceColumnsOrderedAsInLeaderboard(leaderboard); + removeRaceColumnsNotSelected(correctedOrderSelectedRaces); + for (int selectedRaceCount = 0; selectedRaceCount < Util + .size(correctedOrderSelectedRaces); selectedRaceCount++) { + RaceColumnDTO selectedRaceColumn = Util.get(correctedOrderSelectedRaces, selectedRaceCount); + final RaceColumn raceColumn = selectedRaceColumn == null ? null + : getRaceColumnByRaceColumnName(selectedRaceColumn.getName()); + if (raceColumn != null) { + // remove all raceColumns, starting at a specific selectedRaceCount, up to but excluding the selected + // raceName with the result that selectedRace is at position selectedRaceCount afterwards + removeRaceColumnFromRaceColumnStartIndexBeforeRace(selectedRaceCount, selectedRaceColumn); + correctColumnData(raceColumn, selectedRaceColumn); + } else { + // get correct position to insert the column + int positionToInsert = getColumnPositionToInsert(selectedRaceColumn, selectedRaceCount); + if (positionToInsert != -1) { + insertColumn(positionToInsert, createRaceColumn(selectedRaceColumn)); + } else { + // Add the raceColumn with addRaceColumn, if no RaceColumn is existing in leaderboard + addRaceColumn(createRaceColumn(selectedRaceColumn)); + } + } + } + } + + protected RaceColumn createRaceColumn(RaceColumnDTO raceInLeaderboard) { + TextRaceColumn textRaceColumn = new TextRaceColumn(raceInLeaderboard, shallExpandRaceColumn(raceInLeaderboard), + SortingOrder.ASCENDING, RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE); + return textRaceColumn; + } + + /** + * To be expandable, a race column needs to have one or more tracked races associated and needs to have at least + * some GPS data available. Wind data is not required for expandability because several metrics can reasonably be + * determined even without reliable wind information. + */ + private boolean shallExpandRaceColumn(RaceColumnDTO raceColumnDTO) { + return showRaceDetails && raceColumnDTO.hasTrackedRaces() && raceColumnDTO.hasGPSData(); + } + + private void removeUnusedRaceColumns(LeaderboardDTO leaderboard) { + List> columnsToRemove = new ArrayList>(); + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column c = getLeaderboardTable().getColumn(i); + if (c instanceof RaceColumn && (leaderboard == null + || !leaderboard.raceListContains(((RaceColumn) c).getRaceColumnName()))) { + columnsToRemove.add(c); + } + } + // Tricky issue: if the race column is currently expanded, we can't know anymore how many detail columns + // there are because the updated LeaderboardDTO object doesn't contain the race anymore. We have to + // collapse and remove all LegColumns following the RaceColumn + for (Column c : columnsToRemove) { + removeColumn(c); + } + } + + /** + * If the last column is the totals column, remove it. Add the race column as the last column. + */ + private void addRaceColumn(RaceColumn raceColumn) { + if (getLeaderboardTable() + .getColumn(getLeaderboardTable().getColumnCount() - 1) instanceof TotalNetPointssColumn) { + removeColumn(getLeaderboardTable().getColumnCount() - 1); + } + addColumn(raceColumn); + } + + /** + * The regatta rank column shall be displayed if an only if the {@link DetailType#REGATTA_RANK} detail is selected + * in the {@link #selectedOverallDetailColumns}. It will then be displayed after the selection checkbox column (if + * any) and before the sail number / competitor name columns. + */ + private boolean isShowRegattaRankColumn() { + return selectedOverallDetailColumns.contains(DetailType.REGATTA_RANK); + } + + /** + * @param rankColumnIndex + * the column index (0-based) where to put the rank column, if needed + * @return the column index (0-based) for the next column; will equal rankColumnIndex if the rank + * column is not {@link #isShowRegattaRankColumn() supposed to be shown}, or one greater otherwise. + */ + private int ensureRankColumn(int rankColumnIndex) { + final int indexOfNextColumn = rankColumnIndex + (isShowRegattaRankColumn() ? 1 : 0); + if (getLeaderboardTable().getColumnCount() > rankColumnIndex) { + if (isShowRegattaRankColumn()) { + if (getLeaderboardTable().getColumn(rankColumnIndex) != getRankColumn()) { + insertColumn(rankColumnIndex, getRankColumn()); + } // else, the column is needed and is already in place + } else { + if (getLeaderboardTable().getColumn(rankColumnIndex) == getRankColumn()) { + removeColumn(rankColumnIndex); + } + } + } else { + if (isShowRegattaRankColumn()) { + insertColumn(rankColumnIndex, getRankColumn()); + } + } + return indexOfNextColumn; + } + + private int ensureRaceRankColumn(int rankColumnIndex) { + boolean required = isShowRaceRankColumn() && preSelectedRace != null; + final int indexOfNextColumn = required ? 1 : 0; + if (getLeaderboardTable().getColumnCount() > rankColumnIndex) { + if (required) { + if (getLeaderboardTable().getColumn(rankColumnIndex) != getRaceRankColumn()) { + insertColumn(rankColumnIndex, getRaceRankColumn()); + } + } else { + if (getLeaderboardTable().getColumn(rankColumnIndex) == getRaceRankColumn()) { + removeColumn(rankColumnIndex); + } + } + } else { + if (required) { + insertColumn(rankColumnIndex, getRaceRankColumn()); + } + } + return indexOfNextColumn; + } + + private AbstractSortableColumnWithMinMax getRaceRankColumn() { + return raceRankColumn; + } + + private boolean isShowRaceRankColumn() { + return showRaceRankColumn; + } + + /** + * @param selectionCheckboxColumnIndex + * the column index (0-based) where to put the selection checkbox column, if needed + * @return the column index (0-based) for the next column; will equal selectionCheckboxColumnIndex if + * the selection checkbox column is not {@link #showSelectionCheckbox supposed to be shown}, or one greater + * otherwise. + */ + private int ensureSelectionCheckboxColumn(int selectionCheckboxColumnIndex) { + final int indexOfNextColumn = selectionCheckboxColumnIndex + (showSelectionCheckbox ? 1 : 0); + if (getLeaderboardTable().getColumnCount() > selectionCheckboxColumnIndex) { + if (showSelectionCheckbox) { + if (getLeaderboardTable().getColumn(selectionCheckboxColumnIndex) != selectionCheckboxColumn) { + insertColumn(selectionCheckboxColumnIndex, selectionCheckboxColumn); + } // else, the column is needed and is already in place + } else { + if (getLeaderboardTable().getColumn(selectionCheckboxColumnIndex) == selectionCheckboxColumn) { + removeColumn(selectionCheckboxColumnIndex); + } + } + } else { + if (showSelectionCheckbox) { + insertColumn(selectionCheckboxColumnIndex, selectionCheckboxColumn); + } + } + return indexOfNextColumn; + } + + /** + * @return the 0-based index for the next column + */ + private int ensureSailIDAndCompetitorColumn(int columnIndexWhereToInsertTheNextColumn) { + if (isShowCompetitorSailId()) { + if (getLeaderboardTable().getColumnCount() <= columnIndexWhereToInsertTheNextColumn + || !(getLeaderboardTable() + .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof SailIDColumn)) { + insertColumn(columnIndexWhereToInsertTheNextColumn, + new SailIDColumn(new CompetitorFetcher() { + @Override + public CompetitorDTO getCompetitor(LeaderboardRowDTO t) { + return t.competitor; + } + })); + } + columnIndexWhereToInsertTheNextColumn++; + } else { + if (getLeaderboardTable().getColumnCount() > columnIndexWhereToInsertTheNextColumn && getLeaderboardTable() + .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof SailIDColumn) { + removeColumn(columnIndexWhereToInsertTheNextColumn); + } + } + if (isShowCompetitorFullName()) { + if (getLeaderboardTable().getColumnCount() <= columnIndexWhereToInsertTheNextColumn + || !(getLeaderboardTable() + .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof CompetitorColumn)) { + insertColumn(columnIndexWhereToInsertTheNextColumn, createCompetitorColumn()); + } + columnIndexWhereToInsertTheNextColumn++; + } else { + if (getLeaderboardTable().getColumnCount() > columnIndexWhereToInsertTheNextColumn && getLeaderboardTable() + .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof CompetitorColumn) { + removeColumn(columnIndexWhereToInsertTheNextColumn); + } + } + return columnIndexWhereToInsertTheNextColumn; + } + + protected CompetitorColumn createCompetitorColumn() { + return new CompetitorColumn(new CompetitorColumnBase(this, stringMessages, + new CompetitorFetcher() { + @Override + public CompetitorDTO getCompetitor(LeaderboardRowDTO t) { + return t.competitor; + } + })); + } + + private void ensureTotalsColumn() { + // add a totals column on the right + if (getLeaderboardTable().getColumnCount() == 0 || !(getLeaderboardTable() + .getColumn(getLeaderboardTable().getColumnCount() - 1) instanceof TotalNetPointssColumn)) { + addColumn(new TotalNetPointssColumn(TOTAL_COLUMN_STYLE)); + } + } + + private void ensureTotalRacesSailedColumn() { + // add a totals column on the right + if (getLeaderboardTable().getColumnCount() == 0 || !(getLeaderboardTable() + .getColumn(getLeaderboardTable().getColumnCount() - 2) instanceof TotalRacesCompletedColumn)) { + insertColumn(getLeaderboardTable().getColumnCount() - 1, new TotalRacesCompletedColumn(TOTAL_COLUMN_STYLE)); + } + } + + private void ensureNoTotalRacesSailedColumn() { + if ((getLeaderboardTable() + .getColumn(getLeaderboardTable().getColumnCount() - 2) instanceof TotalRacesCompletedColumn)) { + removeColumn(getLeaderboardTable().getColumnCount() - 2); + } + } + + protected void updateTotalRacesSailedColumn() { + final boolean showTotalRacesCompletedColumn = isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(); + if (showTotalRacesCompletedColumn) { + ensureTotalRacesSailedColumn(); + } else { + ensureNoTotalRacesSailedColumn(); + } + } + + /** + * If the leaderboard {@link LeaderboardDTO#hasCarriedPoints has carried points} and if column #1 + * (second column, right of the competitor column) does not exist or is not of type {@link CarryColumn}, all columns + * starting from #1 will be removed and a {@link CarryColumn} will be added. If the leaderboard has no carried + * points but the display still shows a carry column, the column is removed. + * + * @param zeroBasedIndexOfCarryColumn + * the 0-based column index where to put the carry column, if any + * @return the 0-based index of the next column following this column if the carry column is to be shown, or + * zeroBasedIndexOfCarryColumn otherwise + */ + protected int updateCarryColumn(LeaderboardDTO leaderboard, int zeroBasedIndexOfCarryColumn) { + final boolean needsCarryColumn = leaderboard != null && leaderboard.hasCarriedPoints; + if (needsCarryColumn) { + ensureCarryColumn(zeroBasedIndexOfCarryColumn); + } else { + ensureNoCarryColumn(zeroBasedIndexOfCarryColumn); + } + return needsCarryColumn ? zeroBasedIndexOfCarryColumn + 1 : zeroBasedIndexOfCarryColumn; + } + + private void ensureNoCarryColumn(int zeroBasedIndexOfCarryColumn) { + if (getLeaderboardTable().getColumnCount() > zeroBasedIndexOfCarryColumn + && getLeaderboardTable().getColumn(zeroBasedIndexOfCarryColumn) instanceof CarryColumn) { + removeColumn(zeroBasedIndexOfCarryColumn); + } + } + + protected void ensureCarryColumn(int zeroBasedIndexOfCarryColumn) { + if (getLeaderboardTable().getColumnCount() <= zeroBasedIndexOfCarryColumn + || !(getLeaderboardTable().getColumn(zeroBasedIndexOfCarryColumn) instanceof CarryColumn)) { + while (getLeaderboardTable().getColumnCount() > zeroBasedIndexOfCarryColumn) { + removeColumn(zeroBasedIndexOfCarryColumn); + } + addColumn(createCarryColumn()); + } + } + + protected CarryColumn createCarryColumn() { + return new CarryColumn(); + } + + public SortedCellTable getLeaderboardTable() { + return leaderboardTable; + } + + public SailingServiceAsync getSailingService() { + return sailingService; + } + + protected String getLeaderboardName() { + return leaderboardName; + } + + protected void setLeaderboardName(String leaderboardName) { + this.leaderboardName = leaderboardName; + } + + protected ErrorReporter getErrorReporter() { + return errorReporter; + } + + protected ListDataProvider getData() { + return getLeaderboardTable().getDataProvider(); + } + + /** + * @param newTime + * ignored; may be null. The time for loading the leaderboard is determined using + * {@link #getLeaderboardDisplayDate()}. + */ + @Override + public void timeChanged(Date newTime, Date oldTime) { + loadCompleteLeaderboard(/* showProgress */ false); + } + + @Override + public void playStateChanged(PlayStates playState, PlayModes playMode) { + if (!isEmbedded) { + playPause.setHTML(getPlayPauseImgHtml(playState)); + playPause.setTitle(playState == PlayStates.Playing ? stringMessages.pauseAutomaticRefresh() + : stringMessages.autoRefresh()); + } + } + + @Override + public void playSpeedFactorChanged(double newPlaySpeedFactor) { + // nothing to do + } + + private List getTrackedRacesIdentifiers() { + List result = new ArrayList(); + for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { + for (FleetDTO fleet : raceColumn.getFleets()) { + if (raceColumn.getRaceIdentifier(fleet) != null) { + result.add(raceColumn.getRaceIdentifier(fleet)); + } + } + } + return result; + } + + @Override + public boolean hasSettings() { + return true; + } + + @Override + public boolean hasToolbar() { + return false; + } + + @Override + public SettingsDialogComponent getSettingsDialogComponent() { + return new LeaderboardSettingsDialogComponent(getSettings(), leaderboard.getNamesOfRaceColumns(), + stringMessages); + } + + @Override + public LeaderboardSettings getSettings() { + Iterable selectedRaceColumns = raceColumnSelection + .getSelectedRaceColumnsOrderedAsInLeaderboard(leaderboard); + List namesOfRaceColumnsToShow = new ArrayList<>(); + for (RaceColumnDTO raceColumn : selectedRaceColumns) { + namesOfRaceColumnsToShow.add(raceColumn.getName()); + } + LeaderboardSettings leaderboardSettings = new LeaderboardSettings( + Collections.unmodifiableList(selectedManeuverDetails), Collections.unmodifiableList(selectedLegDetails), + Collections.unmodifiableList(selectedRaceDetails), + Collections.unmodifiableList(selectedOverallDetailColumns), namesOfRaceColumnsToShow, + /* namesOfRacesToShow */ null, raceColumnSelection.getNumberOfLastRaceColumnsToShow(), + autoExpandPreSelectedRace, timer.getRefreshInterval(), /* nameOfRaceToSort */ null, + /* sortAscending */ true, /* updateUponPlayStateChange */ true, raceColumnSelection.getType(), + isShowAddedScores(), isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(), + isShowCompetitorSailId(), isShowCompetitorFullName(), isShowRaceRankColumn(), isShowCompetitorNationality); + return LeaderboardSettingsFactory.getInstance().keepDefaults(currentSettings, leaderboardSettings); + } + + @Override + public String getLocalizedShortName() { + return stringMessages.leaderboard(); + } + + private LeaderboardRowDTO getRow(String competitorIdAsString) { + synchronized (getData().getList()) { + for (LeaderboardRowDTO row : getData().getList()) { + if (row.competitor.getIdAsString().equals(competitorIdAsString)) { + return row; + } + } + } + return null; + } + + @Override + public void addedToSelection(CompetitorDTO competitor) { + LeaderboardRowDTO row = getRow(competitor.getIdAsString()); + if (row != null) { + leaderboardSelectionModel.setSelected(row, true); + } + } + + @Override + public void removedFromSelection(CompetitorDTO competitor) { + LeaderboardRowDTO row = getRow(competitor.getIdAsString()); + if (row != null) { + leaderboardSelectionModel.setSelected(row, false); + } + } + + private Iterable getSelectedRows() { + return leaderboardSelectionModel.getSelectedSet(); + } + + @Override + public void competitorsListChanged(Iterable competitors) { + setFilterControlStatus(); + if (timer.isInitialized()) { + timeChanged(timer.getTime(), null); + } + } + + @Override + public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { + setFilterControlStatus(); + updateLeaderboard(getLeaderboard()); + } + + @Override + public void filterChanged(FilterSet> oldFilterSet, + FilterSet> newFilterSet) { + // nothing to do; if the list of filtered competitors has changed, a separate call to + // filteredCompetitorsListChanged will occur + setFilterControlStatus(); + } + + public RaceColumnSelection getRaceColumnSelection() { + return raceColumnSelection; + } + + public void removeAllListeners() { + if (raceTimesInfoProviderListener != null) { + getRaceTimesInfoProvider().removeRaceTimesInfoProviderListener(raceTimesInfoProviderListener); + } + if (raceColumnSelection != null && raceColumnSelection.getType() == RaceColumnSelectionStrategies.LAST_N) { + getRaceTimesInfoProvider() + .removeRaceTimesInfoProviderListener((LastNRacesColumnSelection) raceColumnSelection); + } + if (timer != null) { + timer.removeTimeListener(this); + } + if (leaderboardUpdateListener != null) { + leaderboardUpdateListener.clear(); + } + } + + protected Timer getTimer() { + return timer; + } + + protected void blurFocusedElementAfterSelectionChange() { + // now "blur" the selected leaderboard element because it seems to cause the cell table to scroll to its top; + // see bug 2093. + blur(); + final ScheduledCommand blurCommand = new ScheduledCommand() { + @Override + public void execute() { + blur(); + } + }; + Scheduler.get().scheduleDeferred(blurCommand); + } + + private void blur() { + if (elementToBlur != null) { + elementToBlur.blur(); + } + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) { + timeChanged(getLeaderboardDisplayDate(), null); + } + } + + @Override + public void addLeaderboardUpdateListener(LeaderboardUpdateListener listener) { + this.leaderboardUpdateListener.add(listener); + } + + @Override + public void removeLeaderboardUpdateListener(LeaderboardUpdateListener listener) { + this.leaderboardUpdateListener.remove(listener); + } + + protected void informLeaderboardUpdateListenersAboutLeaderboardUpdated(LeaderboardDTO leaderboard) { + for (LeaderboardUpdateListener listener : new ArrayList( + this.leaderboardUpdateListener)) { + listener.updatedLeaderboard(leaderboard); + } + } + + protected void informLeaderboardUpdateListenersAboutRaceSelected(RaceIdentifier raceIdentifier, + RaceColumnDTO raceColumn) { + for (LeaderboardUpdateListener listener : this.leaderboardUpdateListener) { + listener.currentRaceSelected(raceIdentifier, raceColumn); + } + } + + public boolean hasLiveRace() { + return getLeaderboard().hasLiveRace(timer.getLiveTimePointInMillis()); + } + + public String getLiveRacesText() { + String result = ""; + List> liveRaces = leaderboard.getLiveRaces(timer.getLiveTimePointInMillis()); + boolean isMeta = leaderboard.type.isMetaLeaderboard(); + if (!liveRaces.isEmpty()) { + if (liveRaces.size() == 1) { + String text = getLiveRaceText(liveRaces.get(0), isMeta); + result = isMeta ? stringMessages.regattaIsLive(text) : stringMessages.raceIsLive(text); + } else { + String names = ""; + for (Pair liveRace : liveRaces) { + names += getLiveRaceText(liveRace, isMeta) + ", "; + } + // remove last ", " + names = names.substring(0, names.length() - 2); + result = isMeta ? stringMessages.regattasAreLive(names) : stringMessages.racesAreLive(names); + } + } + return result; + } + + private String getLiveRaceText(Pair liveRace, boolean isMeta) { + String raceName = liveRace.getA().getRaceColumnName(), fleetName = liveRace.getB().getName(); + boolean isDefaultFleet = LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName); + return raceName + ((isDefaultFleet || isMeta) ? "" : (" (" + liveRace.getB().getName() + ")")); + } + + @Override + public String getDependentCssClassName() { + return "leaderboard"; + } + + @Override + public void addBusyStateChangeListener(BusyStateChangeListener listener) { + busyStateChangeListeners.add(listener); + } + + @Override + public void removeBusyStateChangeListener(BusyStateChangeListener listener) { + busyStateChangeListeners.remove(listener); + } + + @Override + public boolean isBusy() { + return busyIndicator.isBusy(); + } + + private void setBusyState(boolean isBusy) { + if (busyIndicator.isBusy() != isBusy) { + busyIndicator.setBusy(isBusy); + for (BusyStateChangeListener listener : busyStateChangeListeners) { + listener.onBusyStateChange(isBusy); + } + } + } + + @Override + public void addBusyTask() { + busyTaskCounter++; + if (busyTaskCounter == 1) { + setBusyState(true); + } + } + + @Override + public void removeBusyTask() { + busyTaskCounter--; + if (busyTaskCounter == 0) { + setBusyState(false); + } + } + + @Override + public String getId() { + return LeaderboardPanelLifecycle.ID; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardViewer.java index 85869658f77..89b73b1dabc 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardViewer.java @@ -30,7 +30,7 @@ import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings; */ public class LeaderboardViewer extends AbstractLeaderboardViewer { private final MultiCompetitorLeaderboardChart multiCompetitorChart; - private LeaderboardPanel overallLeaderboardPanel; + private ClassicLeaderboardPanel overallLeaderboardPanel; public LeaderboardViewer(Component parent, ComponentContext> componentContext, @@ -58,7 +58,7 @@ public class LeaderboardViewer extends AbstractLeaderboardViewer { } } - public LegColumn(UnStyledLeaderboardPanel leaderboardPanel, String raceColumnName, int legIndex, + public LegColumn(LeaderboardPanel leaderboardPanel, String raceColumnName, int legIndex, SortingOrder preferredSortingOrder, StringMessages stringMessages, List legDetailSelection, String headerStyle, String columnStyle, String detailHeaderStyle, String detailColumnStyle) { @@ -283,7 +283,7 @@ public class LegColumn extends ExpandableSortableColumn { @Override protected Map> getDetailColumnMap( - UnStyledLeaderboardPanel leaderboardPanel, StringMessages stringMessages, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringMessages, String detailHeaderStyle, String detailColumnStyle) { Map> result = new HashMap<>(); result.put(DetailType.DISTANCE_TRAVELED, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java index 3277a56e598..0eb3dff2801 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java @@ -85,7 +85,7 @@ public class ManeuverCountRaceColumn extends ExpandableSortableColumn im } } - public ManeuverCountRaceColumn(UnStyledLeaderboardPanel leaderboardPanel, RaceNameProvider raceNameProvider, + public ManeuverCountRaceColumn(LeaderboardPanel leaderboardPanel, RaceNameProvider raceNameProvider, StringMessages stringConstants, List maneuverDetailSelection, String headerStyle, String columnStylee, String detailHeaderStyle, String detailColumnStyle, DisplayedLeaderboardRowsProvider displayedLeaderboardRowsProvider) { super(leaderboardPanel, /* expandable */true /* all legs have details */, new TextCell(), DetailType.NUMBER_OF_MANEUVERS.getDefaultSortingOrder(), @@ -235,7 +235,7 @@ public class ManeuverCountRaceColumn extends ExpandableSortableColumn im @Override protected Map> getDetailColumnMap( - UnStyledLeaderboardPanel leaderboardPanel, StringMessages stringMessages, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringMessages, String detailHeaderStyle, String detailColumnStyle) { Map> result = new HashMap<>(); result.put(DetailType.TACK, new FormattedDoubleDetailTypeColumn(DetailType.TACK, new NumberOfTacks(), detailHeaderStyle, detailColumnStyle, leaderboardPanel)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java index 5de3a57d3ff..d81095e2335 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java @@ -62,7 +62,7 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer parent, ComponentContext context, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, boolean isEmbedded, RegattaAndRaceIdentifier preSelectedRace, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiLeaderboardProxyPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiLeaderboardProxyPanel.java index 6521cd5347c..8c989b0266c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiLeaderboardProxyPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MultiLeaderboardProxyPanel.java @@ -38,12 +38,12 @@ import com.sap.sse.gwt.client.shared.perspective.ComponentContext; import com.sap.sse.gwt.client.shared.perspective.ComponentContextWithSettingsStorage; /** - * A panel managing multiple {@link LeaderboardPanel}s (e.g. from a meta leaderboard) so that the user can switch between them. + * A panel managing multiple {@link ClassicLeaderboardPanel}s (e.g. from a meta leaderboard) so that the user can switch between them. * @author Frank */ public class MultiLeaderboardProxyPanel extends AbstractLazyComponent implements TimeListener, SelectedLeaderboardChangeProvider { - private LeaderboardPanel selectedLeaderboardPanel; + private ClassicLeaderboardPanel selectedLeaderboardPanel; private FlowPanel selectedLeaderboardFlowPanel; private final StringMessages stringMessages; @@ -299,7 +299,7 @@ public class MultiLeaderboardProxyPanel extends AbstractLazyComponent parent, ComponentContext context, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, boolean isEmbedded, RegattaAndRaceIdentifier preSelectedRace, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/OverallTimeTraveledColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/OverallTimeTraveledColumn.java index d9e33632238..3e57372a03a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/OverallTimeTraveledColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/OverallTimeTraveledColumn.java @@ -30,7 +30,7 @@ public class OverallTimeTraveledColumn extends ExpandableSortableColumn private String headerStyle; private MinMaxRenderer minmaxRenderer; - public OverallTimeTraveledColumn(UnStyledLeaderboardPanel leaderboardPanel, StringMessages stringMessages, + public OverallTimeTraveledColumn(LeaderboardPanel leaderboardPanel, StringMessages stringMessages, String headerStyle, String columnStyle, String detailHeaderStyle, String detailColumnStyle) { super(leaderboardPanel, /* expandable */true, new TextCell(), DETAIL_TYPE.getDefaultSortingOrder(), @@ -50,7 +50,7 @@ public class OverallTimeTraveledColumn extends ExpandableSortableColumn @Override protected Map> getDetailColumnMap( - UnStyledLeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle) { Map> result = new HashMap<>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeListener.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeListener.java index f4bf4168b97..9fc4159e30b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeListener.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeListener.java @@ -9,6 +9,6 @@ public interface SelectedLeaderboardChangeListener { /** * @param selectedLeaderboard the selected non-null LeaderboardPanel */ - void onSelectedLeaderboardChanged(LeaderboardPanel selectedLeaderboard); + void onSelectedLeaderboardChanged(ClassicLeaderboardPanel selectedLeaderboard); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeProvider.java index e19f52bd4bb..102b68bffff 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SelectedLeaderboardChangeProvider.java @@ -7,5 +7,5 @@ public interface SelectedLeaderboardChangeProvider { void removeSelectedLeaderboardChangeListener(SelectedLeaderboardChangeListener listener); - void setSelectedLeaderboard(LeaderboardPanel selectedLeaderboard); + void setSelectedLeaderboard(ClassicLeaderboardPanel selectedLeaderboard); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java index 55694b70f47..cd9e568aae4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java @@ -51,31 +51,31 @@ public class SortableExpandableColumnHeader extends Header { private static CellTemplates template = GWT.create(CellTemplates.class); - public SortableExpandableColumnHeader(String title, String iconURL, UnStyledLeaderboardPanel leaderboardPanel, + public SortableExpandableColumnHeader(String title, String iconURL, LeaderboardPanel leaderboardPanel, ExpandableSortableColumn column, StringMessages stringConstants) { this(title, null, null, iconURL, leaderboardPanel, column, stringConstants); } public SortableExpandableColumnHeader(String title, String tooltip, String iconURL, - UnStyledLeaderboardPanel leaderboardPanel, ExpandableSortableColumn column, + LeaderboardPanel leaderboardPanel, ExpandableSortableColumn column, StringMessages stringConstants) { this(title, tooltip, null, iconURL, leaderboardPanel, column, stringConstants); } public SortableExpandableColumnHeader(String title, String tooltip, String iconURL, - UnStyledLeaderboardPanel leaderboardPanel, ExpandableSortableColumn column, + LeaderboardPanel leaderboardPanel, ExpandableSortableColumn column, StringMessages stringConstants, String unit) { this(title, tooltip, unit, iconURL, leaderboardPanel, column, stringConstants); } public SortableExpandableColumnHeader(String title, String tooltip, String unit, - String iconURL, UnStyledLeaderboardPanel leaderboardPanel, ExpandableSortableColumn column, + String iconURL, LeaderboardPanel leaderboardPanel, ExpandableSortableColumn column, StringMessages stringConstants) { super(constructCell(title, tooltip, unit, iconURL, column.isExpansionEnabled(), leaderboardPanel, column, stringConstants)); } private static Cell constructCell(final String title, final String tooltip, final String unit, - final String iconURL, boolean isExpansionEnabled, final UnStyledLeaderboardPanel leaderboardPanel, + final String iconURL, boolean isExpansionEnabled, final LeaderboardPanel leaderboardPanel, final ExpandableSortableColumn column, final StringMessages stringConstants) { final List> cells = new ArrayList>(3); // if it's a medal race, add the cell rendering the medal image diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TimeTraveledRaceColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TimeTraveledRaceColumn.java index 6ba5daba6cc..5ecdb85e90a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TimeTraveledRaceColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/TimeTraveledRaceColumn.java @@ -32,7 +32,7 @@ public class TimeTraveledRaceColumn extends ExpandableSortableColumn imp private String headerStyle; private MinMaxRenderer minmaxRenderer; - public TimeTraveledRaceColumn(UnStyledLeaderboardPanel leaderboardPanel, RaceNameProvider raceNameProvider, + public TimeTraveledRaceColumn(LeaderboardPanel leaderboardPanel, RaceNameProvider raceNameProvider, StringMessages stringMessages, String headerStyle, String columnStyle, String detailHeaderStyle, String detailColumnStyle) { super(leaderboardPanel, /* expandable */true, new TextCell(), DETAIL_TYPE.getDefaultSortingOrder(), @@ -83,7 +83,7 @@ public class TimeTraveledRaceColumn extends ExpandableSortableColumn imp @Override protected Map> getDetailColumnMap( - UnStyledLeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle) { Map> result = new HashMap<>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/UnStyledLeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/UnStyledLeaderboardPanel.java deleted file mode 100644 index 348595db412..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/UnStyledLeaderboardPanel.java +++ /dev/null @@ -1,3649 +0,0 @@ -package com.sap.sailing.gwt.ui.leaderboard; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.google.gwt.cell.client.AbstractSafeHtmlCell; -import com.google.gwt.cell.client.Cell; -import com.google.gwt.cell.client.Cell.Context; -import com.google.gwt.cell.client.CompositeCell; -import com.google.gwt.cell.client.TextCell; -import com.google.gwt.core.client.GWT; -import com.google.gwt.core.client.Scheduler; -import com.google.gwt.core.client.Scheduler.ScheduledCommand; -import com.google.gwt.dom.client.BrowserEvents; -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Style.FontWeight; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.i18n.client.NumberFormat; -import com.google.gwt.resources.client.ImageResource; -import com.google.gwt.safecss.shared.SafeStyles; -import com.google.gwt.safecss.shared.SafeStylesBuilder; -import com.google.gwt.safehtml.client.SafeHtmlTemplates; -import com.google.gwt.safehtml.shared.SafeHtml; -import com.google.gwt.safehtml.shared.SafeHtmlBuilder; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.google.gwt.text.shared.AbstractSafeHtmlRenderer; -import com.google.gwt.user.cellview.client.Column; -import com.google.gwt.user.cellview.client.Header; -import com.google.gwt.user.cellview.client.SafeHtmlHeader; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.AbstractImagePrototype; -import com.google.gwt.user.client.ui.Anchor; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.DockPanel; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HasHorizontalAlignment; -import com.google.gwt.user.client.ui.HasVerticalAlignment; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.ImageResourceRenderer; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.view.client.CellPreviewEvent; -import com.google.gwt.view.client.ListDataProvider; -import com.google.gwt.view.client.MultiSelectionModel; -import com.google.gwt.view.client.SelectionChangeEvent; -import com.google.gwt.view.client.SelectionChangeEvent.Handler; -import com.sap.sailing.domain.common.DetailType; -import com.sap.sailing.domain.common.Distance; -import com.sap.sailing.domain.common.InvertibleComparator; -import com.sap.sailing.domain.common.LeaderboardNameConstants; -import com.sap.sailing.domain.common.MaxPointsReason; -import com.sap.sailing.domain.common.Mile; -import com.sap.sailing.domain.common.RaceIdentifier; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.domain.common.SortingOrder; -import com.sap.sailing.domain.common.Tack; -import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO; -import com.sap.sailing.domain.common.dto.CompetitorDTO; -import com.sap.sailing.domain.common.dto.FleetDTO; -import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO; -import com.sap.sailing.domain.common.dto.LeaderboardDTO; -import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO; -import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; -import com.sap.sailing.domain.common.dto.LegEntryDTO; -import com.sap.sailing.domain.common.dto.RaceColumnDTO; -import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter; -import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardPanelLifecycle; -import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings; -import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies; -import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettingsDialogComponent; -import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettingsFactory; -import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction; -import com.sap.sailing.gwt.ui.client.Collator; -import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; -import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; -import com.sap.sailing.gwt.ui.client.FlagImageResolver; -import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener; -import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider; -import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; -import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener; -import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.client.shared.controls.AbstractSortableColumnWithMinMax; -import com.sap.sailing.gwt.ui.client.shared.controls.FlushableSortedCellTableWithStylableHeaders; -import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn; -import com.sap.sailing.gwt.ui.client.shared.filter.CompetitorRaceRankFilter; -import com.sap.sailing.gwt.ui.client.shared.filter.FilterWithUI; -import com.sap.sailing.gwt.ui.client.shared.filter.LeaderboardFetcher; -import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; -import com.sap.sailing.gwt.ui.leaderboard.DetailTypeColumn.LegDetailField; -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.Util.Pair; -import com.sap.sse.common.filter.BinaryOperator; -import com.sap.sse.common.filter.Filter; -import com.sap.sse.common.filter.FilterSet; -import com.sap.sse.gwt.client.ErrorReporter; -import com.sap.sse.gwt.client.async.AsyncActionsExecutor; -import com.sap.sse.gwt.client.async.MarkedAsyncCallback; -import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; -import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator; -import com.sap.sse.gwt.client.controls.busyindicator.BusyStateChangeListener; -import com.sap.sse.gwt.client.controls.busyindicator.BusyStateProvider; -import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator; -import com.sap.sse.gwt.client.panels.OverlayAssistantScrollPanel; -import com.sap.sse.gwt.client.player.PlayStateListener; -import com.sap.sse.gwt.client.player.TimeListener; -import com.sap.sse.gwt.client.player.Timer; -import com.sap.sse.gwt.client.player.Timer.PlayModes; -import com.sap.sse.gwt.client.player.Timer.PlayStates; -import com.sap.sse.gwt.client.shared.components.AbstractCompositeComponent; -import com.sap.sse.gwt.client.shared.components.Component; -import com.sap.sse.gwt.client.shared.components.ComponentResources; -import com.sap.sse.gwt.client.shared.components.IsEmbeddableComponent; -import com.sap.sse.gwt.client.shared.components.SettingsDialog; -import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; -import com.sap.sse.gwt.client.shared.perspective.ComponentContext; - -/** - * A leaderboard essentially consists of a table widget that in its columns displays the entries. - * - * @author Axel Uhl (D043530) - * - */ -public class UnStyledLeaderboardPanel extends AbstractCompositeComponent - implements TimeListener, PlayStateListener, DisplayedLeaderboardRowsProvider, IsEmbeddableComponent, - CompetitorSelectionChangeListener, LeaderboardFetcher, BusyStateProvider, LeaderboardUpdateProvider { - - private final LeaderboardResources resources; - private final ComponentResources componentResources; - private final LeaderboardTableResources tableResources; - // private static final LeaderboardResources RESOURCES = GWT.create(LeaderboardResources.class); - // private static final ComponentResources COMPONENT_RESOURCES = GWT.create(ComponentResources.class); - // private static final LeaderboardTableResources tableResources = GWT.create(LeaderboardTableResources.class); - - public static final String LOAD_LEADERBOARD_DATA_CATEGORY = "loadLeaderboardData"; - - protected static final NumberFormat scoreFormat = NumberFormat.getFormat("0.##"); - - private final SailingServiceAsync sailingService; - - private static String IS_LIVE_TEXT_COLOR = "#FF0000"; - private static String DEFAULT_TEXT_COLOR = "#000000"; - - private static final String STYLE_LEADERBOARD_CONTENT = "leaderboardContent"; - private static final String STYLE_LEADERBOARD_INFO = "leaderboardInfo"; - private static final String STYLE_LEADERBOARD_TOOLBAR = "leaderboardContent-toolbar"; - private static final String STYLE_LEADERBOARD_LIVE_RACE = "leaderboardContent-liverace"; - private static RaceColumnTemplate raceColumnTemplate = new RaceColumnTemplate(); - - static class RaceColumnTemplate { - private MyTemplate template = GWT.create(MyTemplate.class); - - interface MyTemplate extends SafeHtmlTemplates { - @SafeHtmlTemplates.Template("

") - SafeHtml styledDiv(SafeStyles style); - } - - /** - * Originally in the safehtml template: color:{0}; border-bottom: 3px solid {1}; - * - * @param textColor - * @param borderColor - * @return - */ - public SafeHtml cellFrameWithTextColorAndFleetBorder(String textColor, String borderColor) { - SafeStylesBuilder sb = new SafeStylesBuilder(); - sb.trustedColor(textColor); - sb.trustedNameAndValue("border-bottom", "3px solid " + borderColor); - return template.styledDiv(sb.toSafeStyles()); - } - - /** - * - * Originally in the safehtml template: color:{0}; - * - * @param textColor - * @return - */ - public SafeHtml cellFrameWithTextColor(String textColor) { - SafeStylesBuilder sb = new SafeStylesBuilder(); - sb.trustedColor(textColor); - return template.styledDiv(sb.toSafeStyles()); - } - } - - /** - * The leaderboard name is used to - * {@link SailingServiceAsync#getLeaderboardByName(String, java.util.Date, String[], boolean, String, com.google.gwt.user.client.rpc.AsyncCallback) - * obtain the leaderboard contents} from the server. It may change in case the leaderboard is renamed. - */ - private LeaderboardSettings currentSettings; - private String leaderboardName; - - private final ErrorReporter errorReporter; - - public final StringMessages stringMessages; - - private final FlushableSortedCellTableWithStylableHeaders leaderboardTable; - - private final MultiSelectionModel leaderboardSelectionModel; - - private LeaderboardDTO leaderboard; - - private final TotalRankColumn totalRankColumn; - - private RaceRankColumn raceRankColumn; - - private final SelectionCheckboxColumn selectionCheckboxColumn; - - /** - * Passed to the {@link ManeuverCountRaceColumn}. Modifications to this list will modify the column's children list - * when updated the next time. - */ - private final List selectedManeuverDetails = new ArrayList();; - - /** - * Passed to the {@link LegColumn}. Modifications to this list will modify the column's children list when updated - * the next time. - */ - private final List selectedLegDetails = new ArrayList(); - - /** - * Passed to the {@link TextRaceColumn}. Modifications to this list will modify the column's children list when - * updated the next time. - */ - private final List selectedRaceDetails = new ArrayList(); - - private final List selectedOverallDetailColumns = new ArrayList();; - - private final Map> overallDetailColumnMap; - - private RaceColumnSelection raceColumnSelection; - - protected final String RACE_COLUMN_HEADER_STYLE; - - protected final String LEG_DETAIL_COLUMN_HEADER_STYLE; - - protected final String LEG_DETAIL_COLUMN_STYLE; - - protected final String LEG_COLUMN_HEADER_STYLE; - - protected final String RACE_COLUMN_STYLE; - - protected final String LEG_COLUMN_STYLE; - - protected final String TOTAL_COLUMN_STYLE; - - private final Timer timer; - - /** - * A {@link LeaderboardDTO} tells something about the live delay through its - * {@link LeaderboardDTO#getDelayToLiveInMillisForLatestRace()} method. If this flag is true, the live - * delay in the {@link #timer} will be adjusted each time the {@link #updateLeaderboard(LeaderboardDTO)} method is - * invoked. Otherwise, the timer's delay will be left alone which is helpful, e.g., if the leaderboard panel is - * embedded in a race board panel that focuses on one particular race which may not be the same as the one - * controlling the leaderboard's overall live delay. - */ - private final boolean adjustTimerDelay; - - private boolean autoExpandPreSelectedRace; - - private boolean autoExpandLastRaceColumn; - - /** - * When true, the race columns don't display the competitors' scores in the race represented by the - * column but the cumulative score up to that race. - */ - private boolean showAddedScores; - - private boolean showCompetitorSailId; - private boolean showCompetitorFullName; - - /** - * When true then an additional column just before the overall points is displayed that sums up the - * number of races sailed per competitor. - */ - private boolean showOverallColumnWithNumberOfRacesCompletedPerCompetitor; - - /** - * Remembers whether the auto-expand of the pre-selected race (see {@link #autoExpandPreSelectedRace}) or last - * selected race {@link #autoExpandLastRaceColumn} has been performed once. It must not be performed another time. - */ - private boolean autoExpandPerformedOnce; - - /** - * This anchor's HTML holds the image tag for the play/pause button that needs to be updated when the {@link #timer} - * changes its playing state - */ - protected Anchor playPause; - - private final CompetitorSelectionProvider competitorSelectionProvider; - private final HorizontalPanel filterControlPanel; - private Label filterStatusLabel; - private Button filterClearButton; - - /** - * The handler for changes in the leaderboard table's selection; its registration is kept in - * {@link #leaderboardAsTableSelectionModelRegistration} while it is registered as a selection handler on the - * {@link #leaderboardTable}. - */ - private final Handler selectionChangeHandler; - - /** - * While the {@link #selectionChangeHandler} is registered as a selection change handler on the - * {@link #leaderboardTable}'s selection model, this field holds the registration which can be used to remove the - * registration again. We'll use this to temporarily suspend selection events when actively modifying / adjusting - * the table selection to match the {@link #competitorSelectionProvider}. - */ - private HandlerRegistration leaderboardAsTableSelectionModelRegistration; - - /** - * If this is null, all leaderboard columns added by updating the leaderboard from the server are - * automatically added to the table. Otherwise, only the column whose {@link RaceColumnDTO#getRaceIdentifier(String) - * race identifier} matches the value of this attribute will be added. - */ - private final RegattaAndRaceIdentifier preSelectedRace; - - protected final FlowPanel contentPanel = new FlowPanel(); - - private HorizontalPanel refreshAndSettingsPanel; - private Label scoreCorrectionLastUpdateTimeLabel; - private Label scoreCorrectionCommentLabel; - private Label liveRaceLabel; - - private boolean isEmbedded; - - private ImageResource pauseIcon; - private ImageResource playIcon; - - /** - * For a leaderboard, zero or more tasks may be currently busy. The counter keeps track. If it goes to {@code 0}, - * the {@link #busyIndicator} is set to non-busy. If it goes from {@code 0} to {@code 1} the {@link #busyIndicator} - * is set to busy. - */ - private int busyTaskCounter; - private final BusyIndicator busyIndicator; - private final Set busyStateChangeListeners; - - private final AsyncActionsExecutor asyncActionsExecutor; - - /** - * See also {@link #getDefaultSortColumn()}. If no other column is explicitly selected for sorting and this - * attribute holds a non-null string identifying a valid race by name that is represented in this - * leaderboard panel then sort by it. Otherwise, default sorting will default to the overall rank column. - */ - private String raceNameForDefaultSorting; - - /** - * Can be used to disallow users to drill into the race details. - */ - private final boolean showRaceDetails; - - /** - * The {@link LastNRacesColumnSelection} column selection strategy requires a {@link RaceTimesInfoProvider}. This - * can either be injected by passing a non-null object of that type to the constructor, or such an - * object is created and remembered in this attribute when required the first time. - */ - private RaceTimesInfoProvider raceTimesInfoProvider; - private RaceTimesInfoProviderListener raceTimesInfoProviderListener; - - private int blurInOnSelectionChanged; - - /** - * When an element in the leaderboard receives focus, it needs to be blurred again to keep the surrounding scroll - * panel from scrolling anything into view - */ - private Element elementToBlur; - private boolean showSelectionCheckbox; - - private final List leaderboardUpdateListener = new ArrayList(); - - private boolean initialCompetitorFilterHasBeenApplied = false; - private final boolean showCompetitorFilterStatus; - - private CompetitorFilterPanel competitorFilterPanel; - - /** - * Whether or not a second scroll bar, synchronized with the invisible native scroll bar, shall appear at the bottom - * of the viewport. See {@link OverlayAssistantScrollPanel}. - */ - private final boolean enableSyncedScroller; - private boolean showRaceRankColumn = false; - public boolean isShowCompetitorNationality; - - public UnStyledLeaderboardPanel(Component parent, ComponentContext context, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, - CompetitorSelectionProvider competitorSelectionProvider, String leaderboardName, - ErrorReporter errorReporter, final StringMessages stringMessages, boolean showRaceDetails, - LeaderboardResources resources, ComponentResources componentResources, - LeaderboardTableResources tableResources) { - this(parent, context, sailingService, asyncActionsExecutor, settings, false, /* preSelectedRace */null, - competitorSelectionProvider, null, leaderboardName, errorReporter, stringMessages, showRaceDetails, - resources, componentResources, tableResources); - } - - public UnStyledLeaderboardPanel(Component parent, ComponentContext context, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, - boolean isEmbedded, RegattaAndRaceIdentifier preSelectedRace, - CompetitorSelectionProvider competitorSelectionProvider, String leaderboardGroupName, - String leaderboardName, ErrorReporter errorReporter, final StringMessages stringMessages, - boolean showRaceDetails, LeaderboardResources resources, ComponentResources componentResources, - LeaderboardTableResources tableResources) { - this(parent, context, sailingService, asyncActionsExecutor, settings, isEmbedded, preSelectedRace, - competitorSelectionProvider, - new Timer( - // perform the first request as "live" but don't by default auto-play - PlayModes.Live, PlayStates.Paused, - /* delayBetweenAutoAdvancesInMilliseconds */ LeaderboardEntryPoint.DEFAULT_REFRESH_INTERVAL_MILLIS), - leaderboardGroupName, leaderboardName, errorReporter, stringMessages, showRaceDetails, - /* competitorSearchTextBox */ null, /* showSelectionCheckbox */ true, - /* optionalRaceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false, - /* adjustTimerDelay */ true, /* autoApplyTopNFilter */ false, /* showCompetitorFilterStatus */ false, - /* enableSyncScroller */ false, resources, componentResources, tableResources); - } - - public UnStyledLeaderboardPanel(Component parent, ComponentContext context, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, LeaderboardSettings settings, - boolean isEmbedded, RegattaAndRaceIdentifier preSelectedRace, - CompetitorSelectionProvider competitorSelectionProvider, Timer timer, String leaderboardGroupName, - String leaderboardName, final ErrorReporter errorReporter, final StringMessages stringMessages, - boolean showRaceDetails, CompetitorFilterPanel competitorSearchTextBox, boolean showSelectionCheckbox, - RaceTimesInfoProvider optionalRaceTimesInfoProvider, boolean autoExpandLastRaceColumn, - boolean adjustTimerDelay, boolean autoApplyTopNFilter, boolean showCompetitorFilterStatus, - boolean enableSyncScroller, LeaderboardResources resources, ComponentResources componentResources, - LeaderboardTableResources tableResources) { - super(parent, context); - this.resources = resources; - this.componentResources = componentResources; - this.tableResources = tableResources; - this.showSelectionCheckbox = showSelectionCheckbox; - this.showRaceDetails = showRaceDetails; - this.sailingService = sailingService; - this.asyncActionsExecutor = asyncActionsExecutor; - this.isEmbedded = isEmbedded; - this.preSelectedRace = preSelectedRace; - this.competitorSelectionProvider = competitorSelectionProvider; - competitorSelectionProvider.addCompetitorSelectionChangeListener(this); - this.setLeaderboardName(leaderboardName); - this.errorReporter = errorReporter; - this.stringMessages = stringMessages; - this.raceTimesInfoProvider = optionalRaceTimesInfoProvider; - this.adjustTimerDelay = adjustTimerDelay; - this.initialCompetitorFilterHasBeenApplied = !autoApplyTopNFilter; - this.showCompetitorFilterStatus = showCompetitorFilterStatus; - this.enableSyncedScroller = enableSyncScroller; - this.autoExpandLastRaceColumn = autoExpandLastRaceColumn; - this.timer = timer; - RACE_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableRaceColumnHeader(); - LEG_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableLegColumnHeader(); - LEG_DETAIL_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableLegDetailColumnHeader(); - RACE_COLUMN_STYLE = tableResources.cellTableStyle().cellTableRaceColumn(); - LEG_COLUMN_STYLE = tableResources.cellTableStyle().cellTableLegColumn(); - LEG_DETAIL_COLUMN_STYLE = tableResources.cellTableStyle().cellTableLegDetailColumn(); - TOTAL_COLUMN_STYLE = tableResources.cellTableStyle().cellTableTotalColumn(); - overallDetailColumnMap = createOverallDetailColumnMap(); - raceNameForDefaultSorting = settings.getNameOfRaceToSort(); - if (settings.getLegDetailsToShow() != null) { - selectedLegDetails.addAll(settings.getLegDetailsToShow()); - } - if (settings.getOverallDetailsToShow() != null) { - selectedOverallDetailColumns.addAll(settings.getOverallDetailsToShow()); - } - timer.addPlayStateListener(this); - timer.addTimeListener(this); - switch (settings.getActiveRaceColumnSelectionStrategy()) { - case EXPLICIT: - if (preSelectedRace == null) { - raceColumnSelection = new ExplicitRaceColumnSelection(); - } else { - raceColumnSelection = new ExplicitRaceColumnSelectionWithPreselectedRace(preSelectedRace); - } - break; - case LAST_N: - setRaceColumnSelectionToLastNStrategy(settings.getNumberOfLastRacesToShow()); - break; - } - if (preSelectedRace != null) { - raceRankColumn = new RaceRankColumn(preSelectedRace); - } - totalRankColumn = new TotalRankColumn(); - leaderboardTable = new FlushableSortedCellTableWithStylableHeaders(/* pageSize */10000, - tableResources); - leaderboardTable.addCellPreviewHandler(new CellPreviewEvent.Handler() { - @Override - public void onCellPreview(CellPreviewEvent event) { - if (BrowserEvents.FOCUS.equals(event.getNativeEvent().getType())) { - elementToBlur = event.getNativeEvent().getEventTarget().cast(); - elementToBlur.blur(); - blurInOnSelectionChanged = 2; // blur a couple of times; doing it one time only doesn't seem to work - // reliably - blurFocusedElementAfterSelectionChange(); - } - } - }); - leaderboardTable.ensureDebugId("LeaderboardCellTable"); - selectionCheckboxColumn = new LeaderboardSelectionCheckboxColumn(competitorSelectionProvider); - leaderboardTable.setWidth("100%"); - leaderboardSelectionModel = new MultiSelectionModel(); - // remember handler registration so we can temporarily remove it and re-add it to suspend selection events while - // we're actively changing it - selectionChangeHandler = new Handler() { - @Override - public void onSelectionChange(SelectionChangeEvent event) { - List selection = new ArrayList(); - for (LeaderboardRowDTO row : getSelectedRows()) { - selection.add(row.competitor); - } - UnStyledLeaderboardPanel.this.competitorSelectionProvider.setSelection(selection, - /* listenersNotToNotify */UnStyledLeaderboardPanel.this); - if (blurInOnSelectionChanged > 0) { - blurInOnSelectionChanged--; - blurFocusedElementAfterSelectionChange(); - } - } - }; - leaderboardAsTableSelectionModelRegistration = leaderboardSelectionModel - .addSelectionChangeHandler(selectionChangeHandler); - leaderboardTable.setSelectionModel(leaderboardSelectionModel, selectionCheckboxColumn.getSelectionManager()); - SimplePanel mainPanel = new SimplePanel(); - leaderboardTable.getElement().getStyle().setMarginTop(10, Unit.PX); - contentPanel.setStyleName(STYLE_LEADERBOARD_CONTENT); - busyIndicator = new SimpleBusyIndicator(false, 0.8f); - busyIndicator.ensureDebugId("BusyIndicator"); - busyStateChangeListeners = new HashSet<>(); - // the information panel - if (!isEmbedded) { - Widget toolbarPanel = createToolbarPanel(); - contentPanel.add(toolbarPanel); - } - if (competitorSearchTextBox != null) { - competitorSearchTextBox.add(busyIndicator); - contentPanel.add(competitorSearchTextBox); - competitorSearchTextBox.getSettingsButton().addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - new SettingsDialog(UnStyledLeaderboardPanel.this, stringMessages).show(); - } - }); - this.competitorFilterPanel = competitorSearchTextBox; - } - filterControlPanel = new HorizontalPanel(); - filterControlPanel.setStyleName("LeaderboardPanel-FilterControl-Panel"); - if (enableSyncedScroller) { - contentPanel.add(new OverlayAssistantScrollPanel(leaderboardTable)); - } else { - contentPanel.add(leaderboardTable); - } - if (showCompetitorFilterStatus) { - contentPanel.add(createFilterDeselectionControl()); - } - initWidget(mainPanel); - mainPanel.setWidget(contentPanel); - this.setTitle(stringMessages.leaderboard()); - this.setStyleName("leaderboardMainpanel"); - if (timer.isInitialized()) { - loadCompleteLeaderboard(/* showProgress */ false); - } - updateSettings(settings); - } - - public void scrollRowIntoView(int selected) { - leaderboardTable.getRowElement(selected).scrollIntoView(); - } - - protected Widget createToolbarPanel() { - GWT.log("createToolbarPanel()"); - FlowPanel informationPanel = new FlowPanel(); - informationPanel.setStyleName(STYLE_LEADERBOARD_INFO); - scoreCorrectionLastUpdateTimeLabel = new Label(""); - scoreCorrectionCommentLabel = new Label(""); - informationPanel.add(scoreCorrectionCommentLabel); - informationPanel.add(scoreCorrectionLastUpdateTimeLabel); - - liveRaceLabel = new Label(stringMessages.live()); - liveRaceLabel.setStyleName(STYLE_LEADERBOARD_LIVE_RACE); - liveRaceLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD); - liveRaceLabel.getElement().getStyle().setColor(IS_LIVE_TEXT_COLOR); - liveRaceLabel.setVisible(false); - informationPanel.add(liveRaceLabel); - - // the toolbar panel - DockPanel toolbarPanel = new DockPanel(); - toolbarPanel.ensureDebugId("ToolbarPanel"); - toolbarPanel.setStyleName(STYLE_LEADERBOARD_TOOLBAR); - toolbarPanel.add(informationPanel, DockPanel.WEST); - toolbarPanel.add(busyIndicator, DockPanel.WEST); - toolbarPanel.setWidth("100%"); - toolbarPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); - ClickHandler playPauseHandler = new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - if (UnStyledLeaderboardPanel.this.timer.getPlayState() == PlayStates.Playing) { - UnStyledLeaderboardPanel.this.timer.pause(); - } else { - // playing the standalone leaderboard means putting it into live mode - UnStyledLeaderboardPanel.this.timer.setPlayMode(PlayModes.Live); - } - } - }; - pauseIcon = resources.autoRefreshEnabledIcon(); - playIcon = resources.autoRefreshDisabledIcon(); - refreshAndSettingsPanel = new HorizontalPanel(); - refreshAndSettingsPanel.ensureDebugId("RefreshAndSettingsPanel"); - refreshAndSettingsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); - FlowPanel refreshPanel = new FlowPanel(); - refreshPanel.addStyleName("refreshPanel"); - toolbarPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT); - toolbarPanel.addStyleName("refreshAndSettings"); - playPause = new Anchor(getPlayPauseImgHtml(timer.getPlayState())); - playPause.ensureDebugId("PlayAndPauseAnchor"); - playPause.addClickHandler(playPauseHandler); - playStateChanged(timer.getPlayState(), timer.getPlayMode()); - refreshPanel.add(playPause); - - refreshAndSettingsPanel.add(refreshPanel); - toolbarPanel.add(refreshAndSettingsPanel, DockPanel.EAST); - return toolbarPanel; - } - - @Override - public Widget getEntryWidget() { - return this; - } - - @Override - public Widget getHeaderWidget() { - return null; - } - - @Override - public Widget getContentWidget() { - return contentPanel; - } - - @Override - public Widget getToolbarWidget() { - return null; - } - - @Override - public Widget getLegendWidget() { - return null; - } - - @Override - public boolean isEmbedded() { - return isEmbedded; - } - - public FlowPanel getContentPanel() { - return contentPanel; - } - - protected ImageResource getSettingsIcon() { - return componentResources.settingsIcon(); - } - - public void setShowCompetitorNationality(boolean isShowCompetitorNationality) { - this.isShowCompetitorNationality = isShowCompetitorNationality; - } - - public void updateSettings(final LeaderboardSettings newSettings) { - this.currentSettings = newSettings; - boolean oldShallAddOverallDetails = shallAddOverallDetails(); - - showRaceRankColumn = newSettings.isShowRaceRankColumn(); - - if (newSettings.getOverallDetailsToShow() != null) { - selectedOverallDetailColumns.clear(); - selectedOverallDetailColumns.addAll(newSettings.getOverallDetailsToShow()); - } - setShowCompetitorNationality(newSettings.isShowCompetitorNationality()); - setShowAddedScores(newSettings.isShowAddedScores()); - setShowCompetitorSailId(newSettings.isShowCompetitorSailIdColumn()); - setShowCompetitorFullName(newSettings.isShowCompetitorFullNameColumn()); - setShowOverallColumnWithNumberOfRacesCompletedPerCompetitor( - newSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor()); - final List> columnsToExpandAgain = new ArrayList>(); - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column c = getLeaderboardTable().getColumn(i); - if (c instanceof ExpandableSortableColumn) { - ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; - if (expandableSortableColumn.isExpanded()) { - // now toggle expansion back and forth, - // enforcing a re-build of the visible - // child columns - expandableSortableColumn.changeExpansionState(/* expand */ false); - columnsToExpandAgain.add(expandableSortableColumn); - } - } - } - if (newSettings.getManeuverDetailsToShow() != null) { - selectedManeuverDetails.clear(); - selectedManeuverDetails.addAll(newSettings.getManeuverDetailsToShow()); - } - if (newSettings.getLegDetailsToShow() != null) { - selectedLegDetails.clear(); - selectedLegDetails.addAll(newSettings.getLegDetailsToShow()); - } - if (newSettings.getRaceDetailsToShow() != null) { - selectedRaceDetails.clear(); - selectedRaceDetails.addAll(newSettings.getRaceDetailsToShow()); - } - - addBusyTask(); - Runnable doWhenNecessaryDetailHasBeenLoaded = new Runnable() { - @Override - public void run() { - try { - setAutoExpandPreSelectedRace(false); // avoid expansion during updateLeaderboard(...); will expand - // later - // if it was expanded before - // update leaderboard after settings panel column selection change - // update strategy for determining the race columns to show; if settings' race columns to show is - // null, use the - // previously selected race columns / number of race columns for the new configuration - Iterable oldNamesOfRaceColumnsToShow = null; - if (newSettings.getNamesOfRaceColumnsToShow() == null) { - oldNamesOfRaceColumnsToShow = raceColumnSelection.getSelectedRaceColumnNames(); - } - switch (newSettings.getActiveRaceColumnSelectionStrategy()) { - case EXPLICIT: - if (preSelectedRace == null) { - raceColumnSelection = new ExplicitRaceColumnSelection(); - } else { - raceColumnSelection = new ExplicitRaceColumnSelectionWithPreselectedRace(preSelectedRace); - } - if (newSettings.getNamesOfRaceColumnsToShow() != null) { - raceColumnSelection.requestClear(); - for (String nameOfRaceColumnToShow : newSettings.getNamesOfRaceColumnsToShow()) { - RaceColumnDTO raceColumnToShow = getRaceByColumnName(nameOfRaceColumnToShow); - if (raceColumnToShow != null) { - raceColumnSelection.requestRaceColumnSelection(raceColumnToShow); - } - } - } else { - // apply the old column selections again - for (String oldNameOfRaceColumnToShow : oldNamesOfRaceColumnsToShow) { - final RaceColumnDTO raceColumnByName = getLeaderboard() - .getRaceColumnByName(oldNameOfRaceColumnToShow); - if (raceColumnByName != null) { - raceColumnSelection.requestRaceColumnSelection(raceColumnByName); - } - } - if (newSettings.getNamesOfRacesToShow() != null) { - raceColumnSelection.requestClear(); - for (String nameOfRaceToShow : newSettings.getNamesOfRacesToShow()) { - RaceColumnDTO raceColumnToShow = getRaceByName(nameOfRaceToShow); - if (raceColumnToShow != null) { - raceColumnSelection.requestRaceColumnSelection(raceColumnToShow); - } - } - } - } - break; - case LAST_N: - setRaceColumnSelectionToLastNStrategy(newSettings.getNumberOfLastRacesToShow()); - break; - } - updateLeaderboard(leaderboard); - setAutoExpandPreSelectedRace(newSettings.isAutoExpandPreSelectedRace()); - - if (newSettings.getDelayBetweenAutoAdvancesInMilliseconds() != null) { - timer.setRefreshInterval(newSettings.getDelayBetweenAutoAdvancesInMilliseconds()); - } - for (ExpandableSortableColumn expandableSortableColumn : columnsToExpandAgain) { - expandableSortableColumn.changeExpansionState(/* expand */ true); - } - if (newSettings.getNameOfRaceToSort() != null) { - final RaceColumn raceColumnByRaceName = getRaceColumnByRaceName( - newSettings.getNameOfRaceToSort()); - if (raceColumnByRaceName != null) { - getLeaderboardTable().sortColumn(raceColumnByRaceName, /* ascending */true); - } - } - } finally { - removeBusyTask(); - } - } - }; - if (oldShallAddOverallDetails == shallAddOverallDetails() || oldShallAddOverallDetails - || getLeaderboard().hasOverallDetails()) { - doWhenNecessaryDetailHasBeenLoaded.run(); - } else { // meaning that now the details need to be loaded from the server - updateLeaderboardAndRun(doWhenNecessaryDetailHasBeenLoaded); - } - } - - /** - * @param callWhenExpansionDataIsLoaded - */ - private void updateLeaderboardAndRun(final Runnable callWhenExpansionDataIsLoaded) { - final LeaderboardDTO previousLeaderboard = getLeaderboard(); - getSailingService().getLeaderboardByName(getLeaderboardName(), - timer.getPlayMode() == PlayModes.Live ? null : getLeaderboardDisplayDate(), - /* namesOfRacesForWhichToLoadLegDetails */getNamesOfExpandedRaceColumns(), shallAddOverallDetails(), - previousLeaderboard.getId(), /* fillTotalPointsUncorrected */ false, - new MarkedAsyncCallback( - new AsyncCallback() { - @Override - public void onSuccess(IncrementalOrFullLeaderboardDTO result) { - updateLeaderboard(result.getLeaderboardDTO(previousLeaderboard)); - callWhenExpansionDataIsLoaded.run(); - } - - @Override - public void onFailure(Throwable caught) { - getErrorReporter().reportError( - stringMessages.errorTryingToObtainLeaderboardContents(caught.getMessage()), - true /* silentMode */); - } - })); - } - - public void setRaceColumnSelectionToLastNStrategy(final Integer numberOfLastRacesToShow) { - raceColumnSelection = new LastNRacesColumnSelection(numberOfLastRacesToShow, getRaceTimesInfoProvider()); - if (timer.getPlayState() != Timer.PlayStates.Playing) { - // wait for the first update and adjust leaderboard once the race times have been received - raceTimesInfoProviderListener = new RaceTimesInfoProviderListener() { - @Override - public void raceTimesInfosReceived(Map raceTimesInfo, - long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, - long clientTimeWhenResponseWasReceived) { - // remove - timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, - clientTimeWhenResponseWasReceived); - // remove the listener only in case a leaderboard has already been loaded - if (getLeaderboard() != null) { - updateLeaderboard(getLeaderboard()); - getRaceTimesInfoProvider().removeRaceTimesInfoProviderListener(raceTimesInfoProviderListener); - raceTimesInfoProviderListener = null; - } - } - }; - getRaceTimesInfoProvider().addRaceTimesInfoProviderListener(raceTimesInfoProviderListener); - } - } - - /** - * A leaderboard panel may have been provided with a valid {@link RaceTimesInfoProvider} upon creation; in this - * case, that object will be returned. If none was provided to the constructor, one is created and remembered if no - * previously created/remembered object exists. - *

- * - * Precondition: {@link #timer} is not null - */ - private RaceTimesInfoProvider getRaceTimesInfoProvider() { - if (raceTimesInfoProvider == null) { - final List trackedRacesIdentifiers; - if (leaderboard != null && getTrackedRacesIdentifiers() != null) { - trackedRacesIdentifiers = getTrackedRacesIdentifiers(); - } else { - trackedRacesIdentifiers = Collections.emptyList(); - } - raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), asyncActionsExecutor, errorReporter, - trackedRacesIdentifiers, timer.getRefreshInterval()); - } - return raceTimesInfoProvider; - } - - public AsyncActionsExecutor getExecutor() { - return asyncActionsExecutor; - } - - protected class CompetitorColumn extends LeaderboardSortableColumnWithMinMax { - private final CompetitorColumnBase base; - - protected CompetitorColumn(CompetitorColumnBase base) { - super(base.getCell(getLeaderboard()), SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - this.base = base; - } - - public CompetitorColumn(CompositeCell compositeCell, - CompetitorColumnBase base) { - super(compositeCell, SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - this.base = base; - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - return Collator.getInstance().compare(getLeaderboard().getDisplayName(o1.competitor), - getLeaderboard().getDisplayName(o2.competitor)); - } - }; - } - - @Override - public SafeHtmlHeader getHeader() { - return base.getHeader(); - } - - @Override - public LeaderboardRowDTO getValue(LeaderboardRowDTO object) { - return object; - } - - protected void defaultRender(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { - super.render(context, object, sb); - } - - @Override - public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { - String competitorColor = UnStyledLeaderboardPanel.this.competitorSelectionProvider - .getColor(object.competitor, UnStyledLeaderboardPanel.this.preSelectedRace).getAsHtml(); - String competitorColorBarStyle; - if (UnStyledLeaderboardPanel.this.isEmbedded && preSelectedRace != null) { - competitorColorBarStyle = "style=\"border-bottom: 2px solid " + competitorColor + ";\""; - } else { - competitorColorBarStyle = "style=\"border: none;\""; - } - base.render(object, competitorColorBarStyle, sb); - } - } - - /** - * Shows the country flag and sail ID, if present - * - * @author Axel Uhl (d043530) - * - */ - private class SailIDColumn extends LeaderboardSortableColumnWithMinMax { - private final CompetitorFetcher competitorFetcher; - - protected SailIDColumn(CompetitorFetcher competitorFetcher) { - super(new TextCell(), SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - this.competitorFetcher = competitorFetcher; - // This style is adding to avoid contained images CSS property "max-width: 100%", which could cause - // an overflow to the next column (see https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=3537) - setCellStyleNames(tableResources.cellTableStyle().cellTableSailIdColumn()); - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(T o1, T o2) { - return competitorFetcher.getCompetitor(o1).getSailID() == null - ? competitorFetcher.getCompetitor(o2).getSailID() == null ? 0 : -1 - : competitorFetcher.getCompetitor(o2).getSailID() == null ? 1 - : Collator.getInstance().compare(competitorFetcher.getCompetitor(o1).getSailID(), - competitorFetcher.getCompetitor(o2).getSailID()); - } - }; - } - - @Override - public SafeHtmlHeader getHeader() { - return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.competitor()), - stringMessages.sailIdColumnTooltip()); - } - - @Override - public void render(Context context, T object, SafeHtmlBuilder sb) { - ImageResourceRenderer renderer = new ImageResourceRenderer(); - CompetitorDTO competitor = competitorFetcher.getCompetitor(object); - final String twoLetterIsoCountryCode = competitor.getTwoLetterIsoCountryCode(); - final String flagImageURL = competitor.getFlagImageURL(); - - boolean showBoatColor = !isShowCompetitorFullName() && UnStyledLeaderboardPanel.this.isEmbedded - && preSelectedRace != null; - if (showBoatColor) { - String competitorColor = UnStyledLeaderboardPanel.this.competitorSelectionProvider - .getColor(competitorFetcher.getCompetitor(object), - UnStyledLeaderboardPanel.this.preSelectedRace) - .getAsHtml(); - String style = determineBoatColorDivStyle(competitorColor); - sb.append(SafeHtmlUtils.fromTrustedString("

")); - } - - if (isShowCompetitorNationality || flagImageURL == null || flagImageURL.isEmpty()) { - final ImageResource nationalityFlagImageResource; - if (twoLetterIsoCountryCode==null || twoLetterIsoCountryCode.isEmpty()) { - nationalityFlagImageResource = FlagImageResolver.getEmptyFlagImageResource(); - } else { - nationalityFlagImageResource = FlagImageResolver.getFlagImageResource(twoLetterIsoCountryCode); - } - if (nationalityFlagImageResource != null) { - sb.append(renderer.render(nationalityFlagImageResource)); - sb.appendHtmlConstant(" "); - } - } - if (flagImageURL != null && !flagImageURL.isEmpty()) { - sb.appendHtmlConstant(""); - sb.appendHtmlConstant(" "); - } - sb.appendEscaped(competitor.getSailID()); - if (showBoatColor) { - sb.append(SafeHtmlUtils.fromTrustedString("
")); - } - } - - @Override - public String getValue(T object) { - return competitorFetcher.getCompetitor(object).getSailID(); - } - } - - protected String determineBoatColorDivStyle(String competitorColor) { - return "border-bottom: 2px solid " + competitorColor + ";"; - } - - protected void processStyleForRaceColumnWithoutReasonForMaxPoints(boolean isDiscarded, SafeStylesBuilder ssb) { - if (isDiscarded) { - ssb.opacity(0.5d); - } else { - ssb.fontWeight(FontWeight.BOLD); - } - } - - protected void processStyleForRaceColumnWithReasonForMaxPoints(boolean isDiscarded, SafeStylesBuilder ssb) { - ssb.opacity(0.5d); - } - - protected void processStyleForTotalNetPointsColumn(String textColor, SafeStylesBuilder ssb) { - ssb.fontWeight(FontWeight.BOLD); - ssb.trustedColor(textColor); - } - - /** - * Displays net/total points and possible max-points reasons based on a {@link LeaderboardRowDTO} and a race name - * and makes the column sortable by the total points. - * - * @author Axel Uhl (D043530) - * - */ - protected abstract class RaceColumn extends ExpandableSortableColumn { - private RaceColumnDTO race; - - private final String headerStyle; - private final String columnStyle; - - public RaceColumn(RaceColumnDTO race, boolean enableExpansion, Cell cell, SortingOrder preferredSortingOrder, - String headerStyle, String columnStyle) { - super(UnStyledLeaderboardPanel.this, enableExpansion, cell, preferredSortingOrder, stringMessages, - LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, selectedRaceDetails, UnStyledLeaderboardPanel.this); - setHorizontalAlignment(ALIGN_CENTER); - this.race = race; - this.headerStyle = headerStyle; - this.columnStyle = columnStyle; - } - - public RaceColumnDTO getRace() { - return race; - } - - public void setRace(RaceColumnDTO race) { - this.race = race; - } - - public String getRaceColumnName() { - return race.getRaceColumnName(); - } - - public boolean isMedalRace() { - return race.isMedalRace(); - } - - public boolean isLive(FleetDTO fleetDTO) { - return race.isLive(fleetDTO, timer.getLiveTimePointInMillis()); - } - - @Override - public String getColumnStyle() { - return columnStyle; - } - - /** - * Computes added scores for this RaceColumn - */ - private double computeAddedScores(LeaderboardRowDTO object) { - double addedScores = 0; - for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { - LeaderboardEntryDTO entryBefore = object.fieldsByRaceColumnName.get(raceColumn.getName()); - if (entryBefore.netPoints != null) { - addedScores += entryBefore.netPoints; - } - // else "add 0" - if (raceColumn.getName().equals(getRaceColumnName())) { - break; // we've reached the current column - stop here - } - } - return addedScores; - } - - /** - * Displays a combination of net points and maxPointsReason in bold, transparent, strike-through, depending on - * various criteria. Here's how: - * - *
-         *                                  net points                |    maxPointsReason
-         * -------------------------------+-----------------------------+-----------------------
-         *  not discarded, no maxPoints   | bold                        | none
-         *  not discarded, maxPoints      | bold                        | transparent
-         *  discarded, no maxPoints       | transparent, strike-through | none
-         *  discarded, maxPoints          | transparent, strike-through | transparent, strike-through
-         * 
- */ - @Override - public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder html) { - LeaderboardEntryDTO entry = object.fieldsByRaceColumnName.get(getRaceColumnName()); - if (entry != null) { - boolean isLive = isLive(entry.fleet); - final String textColor = isLive ? IS_LIVE_TEXT_COLOR : DEFAULT_TEXT_COLOR; - final String addedScores = isShowAddedScores() ? scoreFormat.format(computeAddedScores(object)) : ""; - String netOrAddedPointsAsText = isShowAddedScores() ? addedScores - : entry.netPoints == null ? "" : scoreFormat.format(entry.netPoints); - String totalOrAddedPointsAsText = isShowAddedScores() ? addedScores - : entry.totalPoints == null ? "" : scoreFormat.format(entry.totalPoints); - if (entry.fleet != null && entry.fleet.getColor() != null) { - html.append(raceColumnTemplate.cellFrameWithTextColorAndFleetBorder(textColor, - entry.fleet.getColor().getAsHtml())); - } else { - html.append(raceColumnTemplate.cellFrameWithTextColor(textColor)); - } - // don't show points if max points / penalty - if (entry.reasonForMaxPoints == null || entry.reasonForMaxPoints == MaxPointsReason.NONE) { - SafeStylesBuilder ssb = new SafeStylesBuilder(); - processStyleForRaceColumnWithoutReasonForMaxPoints(entry.discarded, ssb); - html.append(SafeHtmlUtils.fromTrustedString("")); - if (!entry.discarded) { - html.append(SafeHtmlUtils.fromTrustedString(netOrAddedPointsAsText)); - } else { - html.append(SafeHtmlUtils.fromTrustedString("")); - html.append(SafeHtmlUtils.fromTrustedString(totalOrAddedPointsAsText)); - html.append(SafeHtmlUtils.fromTrustedString("")); - } - html.append(SafeHtmlUtils.fromTrustedString("")); - - } else { - SafeStylesBuilder ssb = new SafeStylesBuilder(); - processStyleForRaceColumnWithReasonForMaxPoints(entry.discarded, ssb); - html.append(SafeHtmlUtils.fromTrustedString( - "")); - if (entry.discarded) { - html.append(SafeHtmlUtils.fromTrustedString("")); - } - html.appendEscaped( - entry.reasonForMaxPoints == MaxPointsReason.NONE ? "" : entry.reasonForMaxPoints.name()); - if (entry.discarded) { - html.append(SafeHtmlUtils.fromTrustedString("")); - } - html.append(SafeHtmlUtils.fromTrustedString("")); - } - html.append(SafeHtmlUtils.fromTrustedString("
")); - } - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - final int result; - if (isShowAddedScores()) { - double o1AddedScore = computeAddedScores(o1); - double o2AddedScore = computeAddedScores(o2); - double intermediate_result = o1AddedScore == 0. - ? (o2AddedScore == 0. ? -1. : isAscending() ? 1. : -1.) - : o2AddedScore == 0. ? (isAscending() ? -1. : 1.) : o1AddedScore - o2AddedScore; - if (!getLeaderboard().isHigherScoreBetter()) { - result = intermediate_result > 0 ? 1 : intermediate_result < 0 ? -1 : 0; - } else { - result = intermediate_result > 0 ? -1 : intermediate_result < 0 ? 1 : 0; - } - } else { - List competitorsFromBestToWorst = getLeaderboard() - .getCompetitorsFromBestToWorst(race); - int o1Rank = competitorsFromBestToWorst.indexOf(o1.competitor) + 1; - int o2Rank = competitorsFromBestToWorst.indexOf(o2.competitor) + 1; - result = o1Rank == 0 ? o2Rank == 0 ? 0 : isAscending() ? 1 : -1 - : o2Rank == 0 ? isAscending() ? -1 : 1 : o1Rank - o2Rank; - } - return result; - } - }; - } - - @Override - public String getHeaderStyle() { - return headerStyle; - } - - @Override - public Header getHeader() { - SortableExpandableColumnHeader header = new SortableExpandableColumnHeader( - /* title */race.getRaceColumnName(), - /* iconURL */race.isMedalRace() ? "/gwt/images/medal_small.png" : null, - UnStyledLeaderboardPanel.this, this, stringMessages); - return header; - } - } - - public static DetailType[] getAvailableRaceDetailColumnTypes() { - return new DetailType[] { DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, - DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TRAVELED, - DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, DetailType.RACE_TIME_TRAVELED, - DetailType.RACE_CALCULATED_TIME_TRAVELED, - DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, - DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, - DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, DetailType.NUMBER_OF_MANEUVERS, - DetailType.DISPLAY_LEGS, DetailType.CURRENT_LEG, - DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, - DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, - DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL }; - } - - public int getFlagScale() { - return 1; - } - - public static DetailType[] getAvailableRaceStartAnalysisColumnTypes() { - return new DetailType[] { DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, - DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, DetailType.DISTANCE_TO_START_AT_RACE_START, - DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, DetailType.SPEED_OVER_GROUND_AT_RACE_START, - DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, - DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, DetailType.START_TACK }; - } - - public static DetailType[] getAvailableOverallDetailColumnTypes() { - return new DetailType[] { DetailType.REGATTA_RANK, DetailType.TOTAL_DISTANCE_TRAVELED, - DetailType.TOTAL_AVERAGE_SPEED_OVER_GROUND, DetailType.TOTAL_TIME_SAILED_IN_SECONDS, - DetailType.MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, DetailType.TIME_ON_TIME_FACTOR, - DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE }; - } - - private class TextRaceColumn extends RaceColumn implements RaceNameProvider { - /** - * Remembers the leg columns; null-padded, if {@link #getLegColumn(int)} asks for a column index - * not yet existing. It is important to remember the columns because column removal happens based on identity. - */ - private final List legColumns; - - public TextRaceColumn(RaceColumnDTO race, boolean expandable, SortingOrder preferredSortingOrder, - String headerStyle, String columnStyle) { - super(race, expandable, new TextCell(), preferredSortingOrder, headerStyle, columnStyle); - legColumns = new ArrayList(); - } - - public String getValue(LeaderboardRowDTO object) { - // The following code exists only for robustness. This method should never be called because - // RaceColumn implements its own render(...) method which doesn't make use of getValue(...) - final Double netPoints = object.fieldsByRaceColumnName.get(getRaceColumnName()).netPoints; - return "" + (netPoints == null ? "" : scoreFormat.format(netPoints)); - } - - @Override - protected void ensureExpansionDataIsLoaded(final Runnable callWhenExpansionDataIsLoaded) { - if (getLeaderboard().getLegCount(getRaceColumnName(), preSelectedRace) != -1) { - callWhenExpansionDataIsLoaded.run(); - } else { - updateLeaderboardAndRun(callWhenExpansionDataIsLoaded); - } - } - - @Override - protected Map> getDetailColumnMap( - UnStyledLeaderboardPanel leaderboardPanel, StringMessages stringMessages, String detailHeaderStyle, - String detailColumnStyle) { - Map> result = new HashMap<>(); - result.put(DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL, - new TimeSinceLastGpsFixColumn( - DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL, - new RaceRatioBetweenTimeSinceLastPositionFixAndAverageSamplingInterval(), - LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_DISTANCE_TRAVELED, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TRAVELED, - new RaceDistanceTraveledInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, - new RaceDistanceTraveledIncludingGateStartInMeters(), LEG_COLUMN_HEADER_STYLE, - LEG_COLUMN_STYLE, UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, - new RaceAverageSpeedInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, - new RaceGapToLeaderInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, - new RaceCurrentSpeedOverGroundInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, - new RideHeightColumn(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, - new RaceCurrentRideHeightInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, - new RaceDistanceToCompetitorFarthestAheadInMeters(), LEG_COLUMN_HEADER_STYLE, - LEG_COLUMN_STYLE, UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, - new RaceAverageAbsoluteCrossTrackErrorInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, - new RaceAverageSignedCrossTrackErrorInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, - new FormattedDoubleDetailTypeColumn( - DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, - new DistanceToStartFiveSecondsBeforeStartInMeters(), LEG_COLUMN_HEADER_STYLE, - LEG_COLUMN_STYLE, UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, - new SpeedFiveSecondsBeforeStartInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.DISTANCE_TO_START_AT_RACE_START, - new FormattedDoubleDetailTypeColumn(DetailType.DISTANCE_TO_START_AT_RACE_START, - new DistanceToStartAtRaceStartInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, - new FormattedDoubleDetailTypeColumn(DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, - new TimeBetweenRaceStartAndCompetitorStartInSeconds(), LEG_COLUMN_HEADER_STYLE, - LEG_COLUMN_STYLE, UnStyledLeaderboardPanel.this)); - result.put(DetailType.SPEED_OVER_GROUND_AT_RACE_START, - new FormattedDoubleDetailTypeColumn(DetailType.SPEED_OVER_GROUND_AT_RACE_START, - new SpeedOverGroundAtRaceStartInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, - new FormattedDoubleDetailTypeColumn(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, - new SpeedOverGroundWhenPassingStartInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, - new FormattedDoubleDetailTypeColumn( - DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, - new DistanceToStarboardSideOfStartLineInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.START_TACK, - new StartingTackColumn(new TackWhenStarting(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE)); - result.put(DetailType.NUMBER_OF_MANEUVERS, - new ManeuverCountRaceColumn(getLeaderboardPanel(), this, stringMessages, - UnStyledLeaderboardPanel.this.selectedManeuverDetails, LEG_COLUMN_HEADER_STYLE, - LEG_COLUMN_STYLE, LEG_DETAIL_COLUMN_HEADER_STYLE, LEG_DETAIL_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.CURRENT_LEG, new FormattedDoubleDetailTypeColumn(DetailType.CURRENT_LEG, - new CurrentLeg(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_TIME_TRAVELED, - new TimeTraveledRaceColumn(getLeaderboardPanel(), this, stringMessages, LEG_COLUMN_HEADER_STYLE, - LEG_COLUMN_STYLE, LEG_DETAIL_COLUMN_HEADER_STYLE, LEG_DETAIL_COLUMN_STYLE)); - result.put(DetailType.RACE_CALCULATED_TIME_TRAVELED, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_CALCULATED_TIME_TRAVELED, - new RaceCalculatedTimeTraveledInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, - UnStyledLeaderboardPanel.this)); - result.put(DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, - new FormattedDoubleDetailTypeColumn( - DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, - new RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(), - LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, UnStyledLeaderboardPanel.this)); - return result; - } - - @Override - protected Iterable> getDirectChildren() { - List> result = new ArrayList<>(); - for (AbstractSortableColumnWithMinMax column : super.getDirectChildren()) { - result.add(column); - } - if (isExpanded() && selectedRaceDetails.contains(DetailType.DISPLAY_LEGS)) { - // it is important to re-use existing LegColumn objects because - // removing the columns from the table - // is based on column identity - int maxLegCount = getLeaderboard().getLegCount(getRaceColumnName(), preSelectedRace); - if (maxLegCount != -1) { - for (int i = 0; i < maxLegCount; i++) { - LegColumn legColumn = getLegColumn(i); - result.add(legColumn); - } - } else { - // the race is no longer part of the LeaderboardDTO; consider the non-null legs in legColumns: - for (LegColumn legColumn : legColumns) { - if (legColumn != null) { - result.add(legColumn); - } - } - } - } - return result; - } - - private LegColumn getLegColumn(int legNumber) { - LegColumn result; - if (legColumns.size() > legNumber && legColumns.get(legNumber) != null) { - result = legColumns.get(legNumber); - } else { - result = new LegColumn(UnStyledLeaderboardPanel.this, getRaceColumnName(), legNumber, - SortingOrder.ASCENDING, stringMessages, Collections.unmodifiableList(selectedLegDetails), - LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LEG_DETAIL_COLUMN_HEADER_STYLE, - LEG_DETAIL_COLUMN_STYLE); - while (legColumns.size() <= legNumber) { - legColumns.add(null); - } - legColumns.set(legNumber, result); - } - return result; - } - - /** - * Reports the ratio of the time that passed since the last position fix and the average sampling interval of - * the competitor's track. On a perfect track, this value will never exceed 1.0. Immediately when a fix is - * received, this value goes to 0.0. For a competitor whose tracker is lagging, this value can grow considerably - * greater than 1. The value goes to null if there are no fixes in the track. - * - * @author Axel Uhl (D043530) - * - */ - private class RaceRatioBetweenTimeSinceLastPositionFixAndAverageSamplingInterval - implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.timeSinceLastPositionFixInSeconds != null - && fieldsForRace.averageSamplingInterval != null) { - result = fieldsForRace.timeSinceLastPositionFixInSeconds - / fieldsForRace.averageSamplingInterval.asSeconds(); - } - return result; - } - } - - /** - * Accumulates the average speed over all legs of a race - * - * @author Axel Uhl (D043530) - */ - private class RaceAverageSpeedInKnots implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - final Distance distanceTraveledInMeters = row.getDistanceTraveledInMeters(getRaceColumnName()); - final Duration time = row.getTimeSailed(getRaceColumnName()); - final Double result; - if (distanceTraveledInMeters != null && time != null) { - result = distanceTraveledInMeters.inTime(time).getKnots(); - } else { - result = null; - } - return result; - } - } - - /** - * Fetches the average absolute (distance always counted as positive, no matter whether left or right) - * cross-track error for the race - * - * @author Axel Uhl (D043530) - */ - private class RaceAverageAbsoluteCrossTrackErrorInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.averageAbsoluteCrossTrackErrorInMeters; - } - return result; - } - } - - /** - * Fetches the average signed (right course side is positive, left course side is negative) cross-track error - * for the race - * - * @author Axel Uhl (D043530) - */ - private class RaceAverageSignedCrossTrackErrorInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.averageSignedCrossTrackErrorInMeters; - } - return result; - } - } - - /** - * Fetches the competitor's distance to the start mark at the time the race started - * - * @author Axel Uhl (D043530) - */ - private class DistanceToStartAtRaceStartInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.distanceToStartLineAtStartOfRaceInMeters; - } - return result; - } - } - - /** - * Fetches the time between start of race and the competitors start mark passing, telling how long after the gun - * the competitor actually started. - * - * @author Axel Uhl (D043530) - */ - private class TimeBetweenRaceStartAndCompetitorStartInSeconds implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.timeBetweenRaceStartAndCompetitorStartInSeconds; - } - return result; - } - } - - /** - * Fetches the competitor's speed over ground at the time the race started - * - * @author Axel Uhl (D043530) - */ - private class SpeedOverGroundAtRaceStartInKnots implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.speedOverGroundAtStartOfRaceInKnots; - } - return result; - } - } - - /** - * Fetches the competitor's distance to the start mark five seconds before the race started - */ - private class DistanceToStartFiveSecondsBeforeStartInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.distanceToStartLineFiveSecondsBeforeStartInMeters; - } - return result; - } - } - - /** - * Fetches the competitor's speed over ground five seconds before the race started - */ - private class SpeedFiveSecondsBeforeStartInKnots implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.speedOverGroundFiveSecondsBeforeStartInKnots; - } - return result; - } - } - - /** - * Fetches the competitor's speed over ground at the time the competitor passed the start - * - * @author Axel Uhl (D043530) - */ - private class SpeedOverGroundWhenPassingStartInKnots implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.speedOverGroundAtPassingStartWaypointInKnots; - } - return result; - } - } - - /** - * Fetches the competitor's distance to the starboard side of the start line when competitor passed the start. - * If the start waypoint is not a gate/line, the distance to the single buoy is used. - * - * @author Axel Uhl (D043530) - */ - private class DistanceToStarboardSideOfStartLineInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.distanceToStarboardSideOfStartLineInMeters; - } - return result; - } - } - - /** - * Fetches the competitor's speed over ground at the time the competitor passed the start - * - * @author Axel Uhl (D043530) - */ - private class TackWhenStarting implements LegDetailField { - @Override - public Tack get(LeaderboardRowDTO row) { - Tack result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.startTack; - } - return result; - } - } - - /** - * Accumulates the distance traveled over all legs of a race - * - * @author Axel Uhl (D043530) - */ - private class RaceDistanceTraveledInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Distance distanceForRaceColumn = row.getDistanceTraveledInMeters(getRaceColumnName()); - return distanceForRaceColumn == null ? null : distanceForRaceColumn.getMeters(); - } - } - - private class RaceCalculatedTimeTraveledInSeconds implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.calculatedTime == null ? null : fieldsForRace.calculatedTime.asSeconds(); - } - return result; - } - } - - private class RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds - implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null ? null - : fieldsForRace.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.asSeconds(); - } - return result; - } - } - - /** - * Accumulates the distance traveled over all legs of a race and considers the specifics of a gate start. The - * first leg of a gate start gets as additional distance traveled the distance from the port side of the line to - * the point where the competitor started which helps normalize the distance traveled and make them comparable - * across early and late starters. - * - * @author Axel Uhl (D043530) - */ - private class RaceDistanceTraveledIncludingGateStartInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.legDetails != null) { - for (LegEntryDTO legDetail : fieldsForRace.legDetails) { - if (legDetail != null) { - if (legDetail.distanceTraveledIncludingGateStartInMeters != null) { - if (result == null) { - result = 0.0; - } - result += legDetail.distanceTraveledIncludingGateStartInMeters; - } - } - } - } - return result; - } - } - - private class CurrentLeg implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.legDetails != null && !fieldsForRace.legDetails.isEmpty()) { - for (LegEntryDTO legDetail : fieldsForRace.legDetails) { - if (legDetail != null) { - if (legDetail.started) { - if (result == null) { - result = 0.0; - } - result++; - } else { - if (result != null) { - result++; - } - break; - } - } - } - } - return result; - } - } - - /** - * Computes the gap to leader exploiting the ordering of the leg detail columns - * - * @author Axel Uhl (D043530) - */ - private class RaceGapToLeaderInSeconds implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.gapToLeaderInOwnTime == null ? null - : fieldsForRace.gapToLeaderInOwnTime.asSeconds(); - } - return result; - } - } - - /** - * Computes the windward distance to the competitor farthest ahead in the race (not necessarily the leader with - * handicap scoring) in meters - * - * @author Axel Uhl (D043530) - */ - private class RaceDistanceToCompetitorFarthestAheadInMeters implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.windwardDistanceToCompetitorFarthestAheadInMeters != null) { - result = fieldsForRace.windwardDistanceToCompetitorFarthestAheadInMeters; - } - return result; - } - } - - private abstract class AbstractLastLegDetailField> implements LegDetailField { - @Override - public final T get(LeaderboardRowDTO row) { - T result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.legDetails != null) { - int lastLegIndex = fieldsForRace.legDetails.size() - 1; - if (lastLegIndex >= 0) { - LegEntryDTO lastLegDetail = fieldsForRace.legDetails.get(lastLegIndex); - // competitor may be in leg prior to the one the leader is in; find competitors current leg - while (lastLegDetail == null && lastLegIndex > 0) { - lastLegDetail = fieldsForRace.legDetails.get(--lastLegIndex); - } - if (lastLegDetail != null) { - if (lastLegDetail.finished) { - result = getAfterLastLegFinished(row); - } else { - result = getBeforeLastLegFinished(lastLegDetail); - } - } - } - } - return result; - } - - protected abstract T getBeforeLastLegFinished(LegEntryDTO currentLegDetail); - - protected abstract T getAfterLastLegFinished(LeaderboardRowDTO row); - } - - private class RaceCurrentSpeedOverGroundInKnots extends AbstractLastLegDetailField { - @Override - protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) { - return currentLegDetail.currentSpeedOverGroundInKnots; - } - - @Override - protected Double getAfterLastLegFinished(LeaderboardRowDTO row) { - return new RaceAverageSpeedInKnots().get(row); - } - } - - private class RaceCurrentRideHeightInMeters extends AbstractLastLegDetailField { - @Override - protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) { - return currentLegDetail.currentRideHeightInMeters; - } - - @Override - protected Double getAfterLastLegFinished(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null) { - result = fieldsForRace.averageRideHeightInMeters; - } - return result; - } - } - } - - /** - * Column that display the number of races a competitor has finished. Currently taking into account - * {@link MaxPointsReason#DNS}, {@link MaxPointsReason#DNF} and {@link MaxPointsReason#DNC}. Configurable over - * {@link LeaderboardSettings#isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor()}. - * - * @author Simon Marcel Pamies - */ - private class TotalRacesCompletedColumn extends LeaderboardSortableColumnWithMinMax { - private final String columnStyle; - private final MaxPointsReason[] MAX_POINTS_REASONS_THAT_IDENTIFY_NON_FINISHED_RACES = new MaxPointsReason[] { - MaxPointsReason.DNS, MaxPointsReason.DNF, MaxPointsReason.DNC }; - - protected TotalRacesCompletedColumn(String columnStyle) { - super(new TextCell(), SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - this.columnStyle = columnStyle; - setHorizontalAlignment(ALIGN_CENTER); - } - - private int computeNumberOfRacesCompleted(LeaderboardRowDTO object) { - int racesSailedInRow = 0; - for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { - LeaderboardEntryDTO entryBefore = object.fieldsByRaceColumnName.get(raceColumn.getName()); - if (entryBefore.netPoints != null) { - if (entryBefore.reasonForMaxPoints.equals(MaxPointsReason.NONE) - || !Util.contains(Arrays.asList(MAX_POINTS_REASONS_THAT_IDENTIFY_NON_FINISHED_RACES), - entryBefore.reasonForMaxPoints)) { - racesSailedInRow++; - } - } - } - return racesSailedInRow; - } - - @Override - public String getValue(LeaderboardRowDTO object) { - return "" + computeNumberOfRacesCompleted(object); - } - - @Override - public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { - String textColor = getLeaderboard().hasLiveRace(timer.getLiveTimePointInMillis()) ? IS_LIVE_TEXT_COLOR - : DEFAULT_TEXT_COLOR; - - sb.append(SafeHtmlUtils.fromTrustedString("")); - sb.appendEscaped(getValue(object)); - sb.append(SafeHtmlUtils.fromTrustedString("")); - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - double o1RacesSailed = computeNumberOfRacesCompleted(o1); - double o2RacesSailed = computeNumberOfRacesCompleted(o2); - double result = o1RacesSailed == 0. ? o2RacesSailed == 0. ? 0. : isAscending() ? 1. : -1. - : o2RacesSailed == 0. ? isAscending() ? 1. : -1. : o2RacesSailed - o1RacesSailed; - return result > 0 ? 1 : result < 0 ? -1 : 0; - } - }; - } - - @Override - public String getColumnStyle() { - return columnStyle; - } - - @Override - public SafeHtmlHeader getHeader() { - return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.racesScored()), - stringMessages.racesScoredTooltip()); - } - } - - /** - * Displays the net points totals for a competitor for the entire leaderboard. - * - * @author Axel Uhl (D043530) - * - */ - private class TotalNetPointssColumn extends LeaderboardSortableColumnWithMinMax { - private final String columnStyle; - - protected TotalNetPointssColumn(String columnStyle) { - super(new TextCell(), SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - this.columnStyle = columnStyle; - setHorizontalAlignment(ALIGN_CENTER); - } - - @Override - public String getValue(LeaderboardRowDTO object) { - Double netPoints = object.netPoints; - return "" + (netPoints == null ? "" : scoreFormat.format(netPoints)); - } - - @Override - public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) { - String textColor = getLeaderboard().hasLiveRace(timer.getLiveTimePointInMillis()) ? IS_LIVE_TEXT_COLOR - : DEFAULT_TEXT_COLOR; - - SafeStylesBuilder ssb = new SafeStylesBuilder(); - processStyleForTotalNetPointsColumn(textColor, ssb); - sb.append(SafeHtmlUtils.fromTrustedString("")); - sb.appendEscaped(getValue(object)); - sb.append(SafeHtmlUtils.fromTrustedString("")); - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - return getLeaderboard().competitors.indexOf(o1.competitor) - - getLeaderboard().competitors.indexOf(o2.competitor); - } - }; - } - - @Override - public String getColumnStyle() { - return columnStyle; - } - - @Override - public SafeHtmlHeader getHeader() { - return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.total()), - stringMessages.totalNetPointsColumnTooltip()); - } - } - - protected class CarryColumn extends LeaderboardSortableColumnWithMinMax { - public CarryColumn() { - super(new AbstractSafeHtmlCell(new AbstractSafeHtmlRenderer() { - @Override - public SafeHtml render(LeaderboardRowDTO object) { - return new SafeHtmlBuilder() - .appendEscaped(object.carriedPoints == null ? "" : scoreFormat.format(object.carriedPoints)) - .toSafeHtml(); - } - }) { - @Override - protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, - SafeHtmlBuilder sb) { - sb.append(data); - } - }, SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - setSortable(true); - } - - protected CarryColumn(Cell cell) { - super(cell, SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - setSortable(true); - } - - @Override - public LeaderboardRowDTO getValue(LeaderboardRowDTO object) { - return object; - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - Double o1CarriedPoints = o1.carriedPoints; - if (o1CarriedPoints == null) { - o1CarriedPoints = 0.0; - } - Double o2CarriedPoints = o2.carriedPoints; - if (o2CarriedPoints == null) { - o2CarriedPoints = 0.0; - } - return o1CarriedPoints.compareTo(o2CarriedPoints); - } - }; - } - - @Override - public SafeHtmlHeader getHeader() { - return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.carry()), - stringMessages.carryColumnTooltip()); - } - } - - private class LeaderboardSelectionCheckboxColumn - extends com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn - implements CompetitorSelectionChangeListener { - protected LeaderboardSelectionCheckboxColumn(final CompetitorSelectionProvider competitorSelectionProvider) { - super(tableResources.cellTableStyle().cellTableCheckboxSelected(), - tableResources.cellTableStyle().cellTableCheckboxDeselected(), - tableResources.cellTableStyle().cellTableCheckboxColumnCell(), - new EntityIdentityComparator() { - @Override - public boolean representSameEntity(LeaderboardRowDTO dto1, LeaderboardRowDTO dto2) { - return dto1.competitor.getIdAsString().equals(dto2.competitor.getIdAsString()); - } - - @Override - public int hashCode(LeaderboardRowDTO t) { - return t.competitor.getIdAsString().hashCode(); - } - }, getData(), leaderboardTable); - competitorSelectionProvider.addCompetitorSelectionChangeListener(this); - } - - @Override - public Boolean getValue(LeaderboardRowDTO row) { - return competitorSelectionProvider.isSelected(row.competitor); - } - - @Override - public void competitorsListChanged(Iterable competitors) { - } - - @Override - public void filterChanged(FilterSet> oldFilterSet, - FilterSet> newFilterSet) { - } - - @Override - public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { - } - - /** - * Ensure that the checkbox is redrawn when the competitor selection changes - */ - @Override - public void addedToSelection(CompetitorDTO competitor) { - final LeaderboardRowDTO row = getRow(competitor.getIdAsString()); - if (row != null) { - getSelectionModel().setSelected(row, true); - } - } - - /** - * Ensure that the checkbox is redrawn when the competitor selection changes - */ - @Override - public void removedFromSelection(CompetitorDTO competitor) { - final LeaderboardRowDTO row = getRow(competitor.getIdAsString()); - if (row != null) { - getSelectionModel().setSelected(row, false); - } - } - - @Override - public void updateMinMax() { - } - } - - private class RaceRankColumn extends LeaderboardSortableColumnWithMinMax { - public RaceRankColumn(RegattaAndRaceIdentifier preSelectedRace) { - super(new TextCell(), SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - setHorizontalAlignment(ALIGN_CENTER); - setSortable(true); - } - - @Override - public String getValue(LeaderboardRowDTO object) { - int raceRank = getRacePlace(object); - return "" + (raceRank == 0 ? "-" : raceRank); - } - - private int getRacePlace(LeaderboardRowDTO object) { - RaceColumn raceColumn = getRaceColumnByRaceName(preSelectedRace.getRaceName()); - List competitorsSorted = getLeaderboard().getCompetitorsFromBestToWorst(raceColumn.race); - int raceRank = -1; - for (int i = 0; i < competitorsSorted.size(); i++) { - if (object.competitor.equals(competitorsSorted.get(i))) { - raceRank = i + 1; - break; - } - } - return raceRank; - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - int racePlace1 = getRacePlace(o1); - int racePlace2 = getRacePlace(o2); - return racePlace1 == 0 ? racePlace2 == 0 ? 0 : 1 : racePlace2 == 0 ? -1 : racePlace1 - racePlace2; - } - }; - } - - @Override - public SafeHtmlHeader getHeader() { - return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.raceRankShort()), - stringMessages.raceRank()); - } - } - - private class TotalRankColumn extends LeaderboardSortableColumnWithMinMax { - public TotalRankColumn() { - super(new TextCell(), SortingOrder.ASCENDING, UnStyledLeaderboardPanel.this); - setHorizontalAlignment(ALIGN_CENTER); - setSortable(true); - } - - @Override - public String getValue(LeaderboardRowDTO object) { - final int totalRank = getLeaderboard().getTotalRank(object.competitor); - return "" + (totalRank == 0 ? "" : totalRank); - } - - @Override - public InvertibleComparator getComparator() { - return new InvertibleComparatorAdapter() { - @Override - public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) { - final int totalRank1 = getLeaderboard().getTotalRank(o1.competitor); - final int totalRank2 = getLeaderboard().getTotalRank(o2.competitor); - return totalRank1 == 0 ? totalRank2 == 0 ? 0 : 1 : totalRank2 == 0 ? -1 : totalRank1 - totalRank2; - } - }; - } - - @Override - public SafeHtmlHeader getHeader() { - return new SafeHtmlHeaderWithTooltip(SafeHtmlUtils.fromString(stringMessages.totalRegattaRank()), - stringMessages.rankColumnTooltip()); - } - } - - private class StartingTackColumn extends DetailTypeColumn { - public StartingTackColumn(LegDetailField field, String headerStyle, String columnStyle) { - super(DetailType.START_TACK, field, new TextCell(), headerStyle, columnStyle, - UnStyledLeaderboardPanel.this); - } - - @Override - public String getValue(LeaderboardRowDTO row) { - return getField().get(row) == null ? null - : getField().get(row) == Tack.PORT ? stringMessages.portTack() : stringMessages.starboardTack(); - } - } - - private Widget createFilterDeselectionControl() { - filterStatusLabel = new Label(); - filterStatusLabel.setStyleName("LeaderboardPanel-FilterControl-StatusLabel"); - filterStatusLabel.setText(""); - filterControlPanel.add(filterStatusLabel); - filterClearButton = new Button(stringMessages.showAll()); - filterClearButton.setStyleName("LeaderboardPanel-FilterClear-Button"); - filterClearButton.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - competitorFilterPanel.clearAllActiveFilters(); - loadCompleteLeaderboard(/* showProgress */ true); - } - }); - filterControlPanel.add(filterClearButton); - filterControlPanel.setCellHorizontalAlignment(filterClearButton, HasHorizontalAlignment.ALIGN_RIGHT); - setFilterControlStatus(); - return filterControlPanel; - } - - private void setFilterControlStatus() { - if (showCompetitorFilterStatus) { - boolean filtersActive = competitorSelectionProvider.hasActiveFilters(); - if (filtersActive) { - String labelText = ""; - for (Filter filter : competitorSelectionProvider.getCompetitorsFilterSet() - .getFilters()) { - if (filter instanceof FilterWithUI) { - labelText += ((FilterWithUI) filter).getLocalizedDescription(stringMessages) - + ", "; - } else { - labelText += filter.getName() + ", "; - } - } - filterStatusLabel.setText("Active Filter(s): " + labelText.substring(0, labelText.length() - 2)); - filterClearButton.setVisible(true); - filterControlPanel.setVisible(true); - } else { - filterStatusLabel.setText(""); - filterClearButton.setVisible(false); - filterControlPanel.setVisible(false); - if (competitorFilterPanel != null) { - competitorFilterPanel.clearSelection(); - } - } - } - } - - private static class TotalDistanceTraveledInMetersField implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - return row.totalDistanceTraveledInMeters; - } - } - - private static class TimeOnTimeFactorColumn implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - return row.competitor.getTimeOnTimeFactor(); - } - } - - private static class TimeOnDistanceAllowanceInSecondsPerNauticalMileColumn implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - return row.competitor.getTimeOnDistanceAllowancePerNauticalMile() == null ? null - : row.competitor.getTimeOnDistanceAllowancePerNauticalMile().asSeconds(); - } - } - - private static class TotalAverageSpeedOverGroundField implements LegDetailField { - @Override - public Double get(LeaderboardRowDTO row) { - final Double result; - if (row.totalDistanceTraveledInMeters != null && row.totalTimeSailedInSeconds != null - && row.totalTimeSailedInSeconds != 0.0) { - result = row.totalDistanceTraveledInMeters / row.totalTimeSailedInSeconds - / Mile.METERS_PER_NAUTICAL_MILE * 3600; - } else { - result = null; - } - return result; - } - } - - private Map> createOverallDetailColumnMap() { - Map> result = new HashMap<>(); - result.put(DetailType.TOTAL_DISTANCE_TRAVELED, - new FormattedDoubleDetailTypeColumn(DetailType.TOTAL_DISTANCE_TRAVELED, - new TotalDistanceTraveledInMetersField(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); - result.put(DetailType.TOTAL_AVERAGE_SPEED_OVER_GROUND, - new FormattedDoubleDetailTypeColumn(DetailType.TOTAL_AVERAGE_SPEED_OVER_GROUND, - new TotalAverageSpeedOverGroundField(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); - result.put(DetailType.MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, - new MaxSpeedOverallColumn(RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); - result.put(DetailType.TOTAL_TIME_SAILED_IN_SECONDS, createOverallTimeTraveledColumn()); - result.put(DetailType.TIME_ON_TIME_FACTOR, new FormattedDoubleDetailTypeColumn(DetailType.TIME_ON_TIME_FACTOR, - new TimeOnTimeFactorColumn(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this)); - result.put(DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE, - new FormattedDoubleDetailTypeColumn(DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE, - new TimeOnDistanceAllowanceInSecondsPerNauticalMileColumn(), RACE_COLUMN_HEADER_STYLE, - RACE_COLUMN_STYLE, this)); - - return result; - } - - private OverallTimeTraveledColumn createOverallTimeTraveledColumn() { - return new OverallTimeTraveledColumn(this, stringMessages, RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, - LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE); - } - - /** - * Largely for subclasses to add more stuff to this panel, such as more toolbar buttons. - */ - protected HorizontalPanel getRefreshAndSettingsPanel() { - return refreshAndSettingsPanel; - } - - private RaceColumnDTO getRaceByName(String raceName) { - if (getLeaderboard() != null) { - for (RaceColumnDTO race : getLeaderboard().getRaceList()) { - for (FleetDTO fleet : race.getFleets()) { - if (race.getRaceIdentifier(fleet) != null - && raceName.equals(race.getRaceIdentifier(fleet).getRaceName())) { - return race; - } - } - } - } - return null; - } - - private RaceColumnDTO getRaceByColumnName(String columnName) { - if (getLeaderboard() != null) { - for (RaceColumnDTO race : getLeaderboard().getRaceList()) { - if (columnName.equals(race.getRaceColumnName())) { - return race; - } - } - } - return null; - } - - private RaceColumn getRaceColumnByRaceName(String raceName) { - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column column = getLeaderboardTable().getColumn(i); - if (column instanceof RaceColumn) { - RaceColumnDTO raceInLeaderboard = ((RaceColumn) column).getRace(); - for (FleetDTO fleet : raceInLeaderboard.getFleets()) { - final RegattaAndRaceIdentifier raceIdentifier = raceInLeaderboard.getRaceIdentifier(fleet); - if (raceIdentifier != null && raceIdentifier.getRaceName().equals(raceName)) { - return (RaceColumn) column; - } - } - } - } - return null; - } - - private RaceColumn getRaceColumnByRaceColumnName(String raceColumnName) { - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column column = getLeaderboardTable().getColumn(i); - if (column instanceof RaceColumn) { - if (((RaceColumn) column).getRaceColumnName().equals(raceColumnName)) { - return (RaceColumn) column; - } - } - } - return null; - } - - private SafeHtml getPlayPauseImgHtml(PlayStates playState) { - if (playState == PlayStates.Playing) { - return AbstractImagePrototype.create(pauseIcon).getSafeHtml(); - } else { - return AbstractImagePrototype.create(playIcon).getSafeHtml(); - } - } - - private void setDelayInMilliseconds(long delayInMilliseconds) { - // If the timer is currently in a mode that causes null to be used as the leaderboard request time stamp, - // don't let a live play delay change cause another load request by unregistering this panel as timer - // listener before and re-registering it after adjusting the live play delay. - if (useNullAsTimePoint()) { - timer.removeTimeListener(this); - } - timer.setLivePlayDelayInMillis(delayInMilliseconds); - if (useNullAsTimePoint()) { - timer.addTimeListener(this); - } - } - - private boolean isAutoExpandPreSelectedRace() { - return autoExpandPreSelectedRace; - } - - private boolean isAutoExpandLastRaceColumn() { - return autoExpandLastRaceColumn; - } - - private void setAutoExpandPreSelectedRace(boolean autoExpandPreSelectedRace) { - this.autoExpandPreSelectedRace = autoExpandPreSelectedRace; - if (autoExpandPreSelectedRace) { - autoExpandPerformedOnce = false; - } - } - - private boolean isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor() { - return showOverallColumnWithNumberOfRacesCompletedPerCompetitor; - } - - private void setShowOverallColumnWithNumberOfRacesCompletedPerCompetitor( - boolean showOverallColumnWithNumberOfRacesCompletedPerCompetitor) { - this.showOverallColumnWithNumberOfRacesCompletedPerCompetitor = showOverallColumnWithNumberOfRacesCompletedPerCompetitor; - } - - private boolean isShowAddedScores() { - return showAddedScores; - } - - private void setShowAddedScores(boolean showAddedScores) { - this.showAddedScores = showAddedScores; - } - - private boolean isShowCompetitorSailId() { - return showCompetitorSailId; - } - - private void setShowCompetitorSailId(boolean showCompetitorSailId) { - this.showCompetitorSailId = showCompetitorSailId; - } - - private boolean isShowCompetitorFullName() { - return showCompetitorFullName; - } - - private void setShowCompetitorFullName(boolean showCompetitorFullName) { - this.showCompetitorFullName = showCompetitorFullName; - } - - /** - * The time point for which the leaderboard currently shows results. In {@link PlayModes#Replay replay mode} this is - * the {@link #timer}'s time point. In {@link PlayModes#Live live mode} the {@link #timer}'s time is quantized to - * the closest full second to increase the likelihood of cache hits in the back end. - */ - protected Date getLeaderboardDisplayDate() { - return timer.getTime(); - } - - /** - * adds the column to the right end of the {@link #getLeaderboardTable() leaderboard table} and sets - * the column style according to the {@link LeaderboardSortableColumnWithMinMax#getColumnStyle() column's style - * definition}. - */ - protected void addColumn(AbstractSortableColumnWithMinMax column) { - leaderboardTable.addColumn(column, column.getHeader(), column.getComparator(), - column.getPreferredSortingOrder().isAscending()); - String columnStyle = column.getColumnStyle(); - if (columnStyle != null) { - getLeaderboardTable().addColumnStyleName(getLeaderboardTable().getColumnCount() - 1, columnStyle); - } - } - - protected void insertColumn(int beforeIndex, AbstractSortableColumnWithMinMax column) { - // remove column styles of those columns whose index will shift right by - // one: - removeColumnStyles(beforeIndex); - getLeaderboardTable().insertColumn(beforeIndex, column, column.getHeader(), column.getComparator(), - column.getPreferredSortingOrder().isAscending()); - addColumnStyles(beforeIndex); - } - - private void addColumnStyles(int startColumn) { - for (int i = startColumn; i < getLeaderboardTable().getColumnCount(); i++) { - AbstractSortableColumnWithMinMax columnToRemoveStyleFor = (AbstractSortableColumnWithMinMax) getLeaderboardTable() - .getColumn(i); - String columnStyle = columnToRemoveStyleFor.getColumnStyle(); - if (columnStyle != null) { - getLeaderboardTable().addColumnStyleName(i, columnStyle); - } - } - } - - private void removeColumnStyles(int startColumn) { - for (int i = startColumn; i < getLeaderboardTable().getColumnCount(); i++) { - AbstractSortableColumnWithMinMax columnToRemoveStyleFor = (AbstractSortableColumnWithMinMax) getLeaderboardTable() - .getColumn(i); - String columnStyle = columnToRemoveStyleFor.getColumnStyle(); - if (columnStyle != null) { - getLeaderboardTable().removeColumnStyleName(i, columnStyle); - } - } - } - - /** - * removes the column specified by columnIndex from the {@link #getLeaderboardTable() leaderboard - * table} and fixes the column styles again (see {@link #addColumnStyles(int)}). - */ - protected void removeColumn(int columnIndex) { - Column c = getLeaderboardTable().getColumn(columnIndex); - if (c instanceof ExpandableSortableColumn) { - ExpandableSortableColumn expandableColumn = (ExpandableSortableColumn) c; - if (expandableColumn.isExpanded()) { - // remove expanded child columns from the leaderboard... - expandableColumn.changeExpansionState(/* expand */ false); - // them remember that column c was expanded: - expandableColumn.setExpanded(true); - } - } - removeColumnStyles(/* startColumn */columnIndex); - getLeaderboardTable().removeColumn(columnIndex); - addColumnStyles(/* startColumn */columnIndex); - } - - protected void removeColumn(Column c) { - int columnIndex = getLeaderboardTable().getColumnIndex(c); - if (columnIndex != -1) { - removeColumn(columnIndex); - } - } - - public void loadCompleteLeaderboard(final boolean showProgress) { - final Date date = getLeaderboardDisplayDate(); - if (needsDataLoading()) { - if (showProgress) { - addBusyTask(); - } - GetLeaderboardByNameAction getLeaderboardByNameAction = new GetLeaderboardByNameAction(sailingService, - getLeaderboardName(), useNullAsTimePoint() ? null : date, - /* namesOfRaceColumnsForWhichToLoadLegDetails */getNamesOfExpandedRaceColumns(), - shallAddOverallDetails(), /* previousLeaderboard */ - getLeaderboard(), isFillTotalPointsUncorrected(), timer, errorReporter, stringMessages); - this.asyncActionsExecutor.execute(getLeaderboardByNameAction, LOAD_LEADERBOARD_DATA_CATEGORY, - new AsyncCallback() { - @Override - public void onSuccess(LeaderboardDTO result) { - currentSettings = LeaderboardSettingsFactory.getInstance() - .createNewSettingsWithCustomDefaults( - new LeaderboardSettings(result.getNamesOfRaceColumns()), currentSettings); - try { - updateLeaderboard(result); - // reapply, as columns might have changed - updateSettings(currentSettings); - } finally { - if (showProgress) { - removeBusyTask(); - } - } - } - - @Override - public void onFailure(Throwable caught) { - if (showProgress) { - removeBusyTask(); - } - getErrorReporter().reportError( - "Error trying to obtain leaderboard contents: " + caught.getMessage(), - true /* silentMode */); - } - }); - } - } - - protected boolean isFillTotalPointsUncorrected() { - return false; - } - - /** - * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(boolean) loading the leaderboard - * contents}, null is used as time point. The condition for this is encapsulated in this method so - * others can find out. For example, when a time change is signaled due to local offset / delay adjustments, no - * additional call to {@link #loadCompleteLeaderboard(boolean)} would be required as null will be - * passed in any case, not being affected by local time offsets. - */ - private boolean useNullAsTimePoint() { - return timer.getPlayMode() == PlayModes.Live; - } - - private boolean needsDataLoading() { - return isVisible(); - } - - /** - * Determine from column expansion state which races need their leg details - */ - private Collection getNamesOfExpandedRaceColumns() { - Collection namesOfExpandedRaceColumns = new ArrayList(); - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column column = getLeaderboardTable().getColumn(i); - if (column instanceof RaceColumn) { - RaceColumn raceColumn = (RaceColumn) column; - if (raceColumn.isExpanded()) { - namesOfExpandedRaceColumns.add(raceColumn.getRaceColumnName()); - } - } - } - return namesOfExpandedRaceColumns; - } - - private boolean shallAddOverallDetails() { - return !selectedOverallDetailColumns.isEmpty(); - } - - private void applyTop30FilterIfCompetitorSizeGreaterEqual40(LeaderboardDTO leaderboard) { - int maxRaceRank = 30; - if (leaderboard.competitors.size() >= 40) { - CompetitorRaceRankFilter raceRankFilter = new CompetitorRaceRankFilter(); - raceRankFilter.setLeaderboardFetcher(this); - raceRankFilter.setSelectedRace(preSelectedRace); - raceRankFilter.setQuickRankProvider(this.competitorFilterPanel.getQuickRankProvider()); - raceRankFilter.setOperator(new BinaryOperator(BinaryOperator.Operators.LessThanEquals)); - raceRankFilter.setValue(maxRaceRank); - FilterSet> activeFilterSet = competitorSelectionProvider - .getOrCreateCompetitorsFilterSet(stringMessages.topNCompetitorsByRaceRank(maxRaceRank)); - activeFilterSet.addFilter(raceRankFilter); - competitorSelectionProvider.setCompetitorsFilterSet(activeFilterSet); - } - } - - /** - * Assigns leaderboard to {@link #leaderboard} and updates the UI accordingly. Also updates the min/max - * values on the columns. - */ - public void updateLeaderboard(LeaderboardDTO leaderboard) { - if (leaderboard != null) { - Collection> columnsToCollapseAndExpandAgain = getExpandedRaceColumnsWhoseDisplayedLegCountChanged( - leaderboard); - for (RaceColumn columnToCollapseAndExpandAgain : columnsToCollapseAndExpandAgain) { - columnToCollapseAndExpandAgain.changeExpansionState(/* expand */ false); - } - competitorSelectionProvider.setCompetitors(leaderboard.competitors, /* listenersNotToNotify */this); - if (!initialCompetitorFilterHasBeenApplied) { - applyTop30FilterIfCompetitorSizeGreaterEqual40(leaderboard); - initialCompetitorFilterHasBeenApplied = true; - } - raceColumnSelection.autoUpdateRaceColumnSelectionForUpdatedLeaderboard(getLeaderboard(), leaderboard); - setLeaderboard(leaderboard); - adjustColumnLayout(leaderboard); - updateRaceColumnDTOsToRaceColumns(leaderboard); - // for (RaceColumn columnToCollapseAndExpandAgain : columnsToCollapseAndExpandAgain) { - // columnToCollapseAndExpandAgain.changeExpansionState(/* expand */ true); - // } - adjustDelayToLive(); - final Map rowsToDisplay = getRowsToDisplay(); - Set rowsToAdd = new HashSet<>(rowsToDisplay.values()); - Map rowsToUpdate = new HashMap<>(); - synchronized (getData().getList()) { - int index = 0; - for (Iterator i = getData().getList().iterator(); i.hasNext();) { - LeaderboardRowDTO oldRow = i.next(); - LeaderboardRowDTO newRow = rowsToDisplay.get(oldRow.competitor); - if (newRow != null) { - rowsToUpdate.put(index++, newRow); // update row in place, preserving its selection state - rowsToAdd.remove(newRow); // no need to add this row when it was updated in-place - } else { - i.remove(); // old row's competitor not found in new rows' competitors; remove old row from - // table - } - } - for (Entry updateEntry : rowsToUpdate.entrySet()) { - LeaderboardRowDTO oldElement = getData().getList().set(updateEntry.getKey(), - updateEntry.getValue()); - leaderboardSelectionModel.setSelected(oldElement, false); // make sure the old element is no longer - // part of the selection - updateSelection(updateEntry.getValue()); - } - for (LeaderboardRowDTO rowToAdd : rowsToAdd) { - getData().getList().add(rowToAdd); - updateSelection(rowToAdd); - } - } - RaceColumn lastRaceColumn = null; - for (int i = getLeaderboardTable().getColumnCount() - 1; i >= 0; i--) { - if (getLeaderboardTable().getColumn(i) instanceof RaceColumn) { - lastRaceColumn = (RaceColumn) getLeaderboardTable().getColumn(i); - break; - } - } - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - AbstractSortableColumnWithMinMax c = (AbstractSortableColumnWithMinMax) getLeaderboardTable() - .getColumn(i); - c.updateMinMax(); - // Toggle pre-selected race, if the setting is set and it isn't open yet, or the last race column if - // that was requested - if ((!autoExpandPerformedOnce && isAutoExpandPreSelectedRace() && c instanceof RaceColumn - && ((RaceColumn) c).getRace().hasTrackedRace(preSelectedRace)) - || (isAutoExpandLastRaceColumn() && c == lastRaceColumn)) { - ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; - if (!expandableSortableColumn.isExpanded()) { - expandableSortableColumn.changeExpansionState(/* expand */ true); - autoExpandPerformedOnce = true; - } - } - if (c instanceof RaceColumn && ((RaceColumn) c).getRace().hasTrackedRace(preSelectedRace)) { - RaceColumnDTO raceColumn = ((RaceColumn) c).getRace(); - informLeaderboardUpdateListenersAboutRaceSelected(preSelectedRace, raceColumn); - } - } - if (leaderboardTable.getCurrentlySortedColumn() != null) { - leaderboardTable.sort(); - } else { - AbstractSortableColumnWithMinMax columnToSortFor = getDefaultSortColumn(); - leaderboardTable.sortColumn(columnToSortFor, columnToSortFor.getPreferredSortingOrder().isAscending()); - } - - if (!isEmbedded) { - updateToolbar(leaderboard); - } - informLeaderboardUpdateListenersAboutLeaderboardUpdated(leaderboard); - } - } - - protected void updateToolbar(LeaderboardDTO leaderboard) { - scoreCorrectionCommentLabel.setText(leaderboard.getComment() != null ? leaderboard.getComment() : ""); - if (leaderboard.getTimePointOfLastCorrectionsValidity() != null) { - Date lastCorrectionDate = leaderboard.getTimePointOfLastCorrectionsValidity(); - String lastUpdate = DateAndTimeFormatterUtil.formatLongDateAndTimeGMT(lastCorrectionDate); - scoreCorrectionLastUpdateTimeLabel.setText(stringMessages.lastScoreUpdate() + ": " + lastUpdate); - } else { - scoreCorrectionLastUpdateTimeLabel.setText(""); - } - - boolean hasLiveRace = !leaderboard.getLiveRaces(timer.getLiveTimePointInMillis()).isEmpty(); - liveRaceLabel.setText(hasLiveRace ? getLiveRacesText() : ""); - scoreCorrectionLastUpdateTimeLabel.setVisible(!hasLiveRace); - liveRaceLabel.setVisible(hasLiveRace); - } - - /** - * Adjusts the row's selection in the {@link #leaderboardSelectionModel} so it matches its selection state in the - * {@link #competitorSelectionProvider}. - */ - private void updateSelection(LeaderboardRowDTO row) { - final boolean shallBeSelected = competitorSelectionProvider.isSelected(row.competitor); - if (leaderboardAsTableSelectionModelRegistration != null) { - // suspend selection events while actively adjusting the leaderboardSelectionModel to match the - // competitorSelectionProvider - leaderboardAsTableSelectionModelRegistration.removeHandler(); - leaderboardAsTableSelectionModelRegistration = null; - } - if (leaderboardSelectionModel.isSelected(row) != shallBeSelected) { - leaderboardSelectionModel.setSelected(row, shallBeSelected); - } - // register the selection change handler again - leaderboardAsTableSelectionModelRegistration = leaderboardTable.getSelectionModel() - .addSelectionChangeHandler(selectionChangeHandler); - } - - /** - * The race columns hold a now outdated copy of a {@link RaceColumnDTO} which needs to be updated from the - * {@link LeaderboardDTO} just received - */ - private void updateRaceColumnDTOsToRaceColumns(LeaderboardDTO leaderboard) { - for (RaceColumnDTO newRace : leaderboard.getRaceList()) { - RaceColumn raceColumn = getRaceColumnByRaceColumnName(newRace.getName()); - if (raceColumn != null) { - raceColumn.setRace(newRace); - } - } - } - - /** - * Due to a course change, a race may change its number of legs. All expanded race columns that show leg columns and - * whose leg count changed need to be collapsed before the leaderboard is replaced, and expanded afterwards again. - * Race columns whose toggling is {@link ExpandableSortableColumn#isTogglingInProcess() currently in progress} are - * not considered because their new state will be considered after replacing anyhow. - * - * @param newLeaderboard - * the new leaderboard before assigning to {@link #leaderboard} - * @return the columns that were collapsed in this step and that shall be expanded again after the leaderboard has - * been replaced - */ - private Collection> getExpandedRaceColumnsWhoseDisplayedLegCountChanged( - LeaderboardDTO newLeaderboard) { - Set> result = new HashSet>(); - if (selectedRaceDetails.contains(DetailType.DISPLAY_LEGS)) { - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column c = getLeaderboardTable().getColumn(i); - if (c instanceof RaceColumn) { - RaceColumn rc = (RaceColumn) c; - // If the new leaderboard no longer contains the column, getLegCount will return -1, causing the - // column - // to be collapsed if it was expanded. This is correct because otherwise, removing it would no - // longer - // know the correct leg count. - if (!rc.isTogglingInProcess() && rc.isExpanded()) { - int oldLegCount = getLeaderboard().getLegCount(rc.getRaceColumnName(), preSelectedRace); - int newLegCount = newLeaderboard.getLegCount(rc.getRaceColumnName(), preSelectedRace); - if (oldLegCount != newLegCount) { - result.add(rc); - } - } - } - } - } - return result; - } - - /** - * Based on the {@link LeaderboardDTO#getDelayToLiveInMillisForLatestRace()} for the race that has the latest - * {@link RaceColumnDTO#getStartDate(FleetDTO) start time}, automatically adjusts the delay accordingly unless the - * {@link #adjustTimerDelay} flag is false - */ - private void adjustDelayToLive() { - if (adjustTimerDelay) { - if (leaderboard.getDelayToLiveInMillisForLatestRace() != null) { - setDelayInMilliseconds(leaderboard.getDelayToLiveInMillisForLatestRace()); - } - } - } - - /** - * Extracts the rows to display of the leaderboard. These are all {@link AbstractLeaderboardDTO#rows - * rows} in case {@link #preSelectedRace} is null, or only the rows of the competitors who scored in - * the race identified by {@link #preSelectedRace} otherwise. - */ - @Override - public Map getRowsToDisplay() { - Map result; - Iterable allFilteredCompetitors = competitorSelectionProvider.getFilteredCompetitors(); - result = new HashMap(); - if (preSelectedRace == null) { - for (CompetitorDTO competitor : leaderboard.rows.keySet()) { - if (Util.contains(allFilteredCompetitors, competitor)) { - result.put(competitor, leaderboard.rows.get(competitor)); - } - } - } else { - for (CompetitorDTO competitorInPreSelectedRace : getCompetitors(preSelectedRace)) { - if (Util.contains(allFilteredCompetitors, competitorInPreSelectedRace)) { - result.put(competitorInPreSelectedRace, leaderboard.rows.get(competitorInPreSelectedRace)); - } - } - } - return result; - } - - /** - * The {@link LeaderboardDTO} holds {@link LeaderboardDTO#getRaceList() races} as {@link RaceColumnDTO} objects. - * Those map their fleet names to the {@link RegattaAndRaceIdentifier} which identifies the tracked race - * representing the fleet race within the race column. - *

- * - * On the other hand, a {@link LeaderboardRowDTO} has {@link LeaderboardEntryDTO}, keyed by race column name. The - * entry DTOs, in turn, store the {@link RaceIdentifier} of the race in which the respective competitor achieved the - * score. - *

- * - * With this information it is possible to identify the competitors who participated in a particular tracked race, - * as identified through the race parameter. - * - * @return all competitors for which the {@link #getLeaderboard() leaderboard} has an entry whose - * {@link LeaderboardEntryDTO#race} equals race - */ - public Iterable getCompetitors(RaceIdentifier race) { - Set result = new HashSet(); - if (getLeaderboard() != null) { - for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { - if (raceColumn.hasTrackedRace(race)) { - for (Map.Entry e : getLeaderboard().rows.entrySet()) { - LeaderboardEntryDTO entry = e.getValue().fieldsByRaceColumnName - .get(raceColumn.getRaceColumnName()); - if (entry != null && entry.race != null && entry.race.equals(race)) { - result.add(e.getKey()); - } - } - } - } - } - return result; - } - - private AbstractSortableColumnWithMinMax getDefaultSortColumn() { - AbstractSortableColumnWithMinMax defaultSortColumn = null; - if (raceNameForDefaultSorting != null) { - defaultSortColumn = getRaceColumnByRaceName(raceNameForDefaultSorting); - } - if (defaultSortColumn == null) { - defaultSortColumn = getRankColumn(); - } - return defaultSortColumn; - } - - private TotalRankColumn getRankColumn() { - return totalRankColumn; - } - - protected void setLeaderboard(LeaderboardDTO leaderboard) { - this.leaderboard = leaderboard; - } - - @Override - public LeaderboardDTO getLeaderboard() { - return leaderboard; - } - - private void adjustColumnLayout(LeaderboardDTO leaderboard) { - int columnIndex = 0; - columnIndex = ensureRaceRankColumn(columnIndex); - columnIndex = ensureSelectionCheckboxColumn(columnIndex); - columnIndex = ensureRankColumn(columnIndex); - columnIndex = ensureSailIDAndCompetitorColumn(columnIndex); - columnIndex = updateCarryColumn(leaderboard, columnIndex); - adjustOverallDetailColumns(leaderboard, columnIndex); - // first remove race columns no longer needed: - removeUnusedRaceColumns(leaderboard); - if (leaderboard != null) { - createMissingAndAdjustExistingRaceColumns(leaderboard); - ensureTotalsColumn(); - updateTotalRacesSailedColumn(); - } - } - - /** - * Ensures that the columns requested by {@link #selectedOverallDetailColumns} are in the table. Assumes that if - * there are any existing overall details columns, they start at indexOfFirstOverallDetailsColumn and - * are in the order defined by {@link #getAvailableOverallDetailColumnTypes()}. - * - * @param indexOfFirstOverallDetailsColumn - * tells the column index for the first overall details column - */ - private void adjustOverallDetailColumns(LeaderboardDTO leaderboard, int indexOfFirstOverallDetailsColumn) { - List> overallDetailColumnsToShow = new ArrayList>(); - // ensure the ordering in overallDetailColumnsToShow conforms to the ordering of - // getAvailableOverallDetailColumnTypes() - for (DetailType overallDetailType : getAvailableOverallDetailColumnTypes()) { - if (selectedOverallDetailColumns.contains(overallDetailType) - && overallDetailColumnMap.containsKey(overallDetailType)) { - overallDetailColumnsToShow.add(overallDetailColumnMap.get(overallDetailType)); - } - } - int currentColumnIndex = indexOfFirstOverallDetailsColumn; - int i = 0; // index into overallDetailColumnToShow - Column currentColumn = currentColumnIndex < getLeaderboardTable().getColumnCount() - ? getLeaderboardTable().getColumn(currentColumnIndex) : null; - // repeat until no more column to check for removal and no more column left to check for need to insert - while (i < overallDetailColumnsToShow.size() || overallDetailColumnMap.values().contains(currentColumn)) { - if (i < overallDetailColumnsToShow.size() && currentColumn == overallDetailColumnsToShow.get(i)) { - // found selected column in table; all good, advance both "pointers" - i++; - currentColumnIndex++; - currentColumn = getLeaderboardTable().getColumn(currentColumnIndex); - } else if (i < overallDetailColumnsToShow.size()) { - // selected column is missing; insert - insertColumn(currentColumnIndex++, overallDetailColumnsToShow.get(i++)); - } else { - // based on the while's condition, currentColumn is an overallDetailsColumnMap value; - // based on the previous if's failed condition, it is not selected. Remove: - removeColumn(currentColumnIndex); - currentColumn = getLeaderboardTable().getColumn(currentColumnIndex); - } - } - } - - /** - * If header information doesn't match the race column's actual state (tracked races attached meaning expandable; - * medal race), the column is removed and inserted again - * - * @param raceColumn - * the raceColumn to correct. - * @param selectedRaceColumn - * the new race column data for raceColumn - */ - private void correctColumnData(RaceColumn raceColumn, RaceColumnDTO race) { - int columnIndex = getRaceColumnPosition(raceColumn); - if (raceColumn.isExpansionEnabled() != race.hasTrackedRaces() - || race.isMedalRace() != raceColumn.isMedalRace()) { - if (raceColumn.isExpanded()) { - raceColumn.changeExpansionState(/* expand */ false); // remove children from table - } - removeColumn(columnIndex); - insertColumn(columnIndex, createRaceColumn(race)); - } - } - - /** - * Removes all RaceColumns, starting at count {@link raceColumnStartIndex raceColumnStartIndex} - * - * @param raceColumnStartIndex - * The index of the race column should be deleted from. - * @param raceName - * The name of the racing column until the table should be cleared. - */ - private void removeRaceColumnFromRaceColumnStartIndexBeforeRace(int raceColumnStartIndex, RaceColumnDTO race) { - int counter = 0; - for (int leaderboardposition = 0; leaderboardposition < getLeaderboardTable() - .getColumnCount(); leaderboardposition++) { - Column c = getLeaderboardTable().getColumn(leaderboardposition); - if (c instanceof RaceColumn) { - RaceColumn raceColumn = (RaceColumn) c; - if (!raceColumn.getRaceColumnName().equals(race.getRaceColumnName())) { - if (raceColumnStartIndex == counter) { - removeColumn(raceColumn); - } - } else { - return; - } - counter++; - } - } - } - - /** - * Gets a ColumnPosition of a raceColumn - * - * @param raceColumn - * The column for which the position is to be found in the leaderboard table - * @return the position. Returns -1 if raceColumn not existing in leaderboardTable. - */ - private int getRaceColumnPosition(RaceColumn raceColumn) { - for (int leaderboardposition = 0; leaderboardposition < getLeaderboardTable() - .getColumnCount(); leaderboardposition++) { - Column c = getLeaderboardTable().getColumn(leaderboardposition); - if (c instanceof RaceColumn) { - RaceColumn rc = (RaceColumn) c; - if (rc.equals(raceColumn)) { - return leaderboardposition; - } - } - } - return -1; - } - - /** - * This method returns the position where a race column should get inserted. - * - * @param raceName - * the name of the race to insert - * @param listpos - * the position of the race in the {@link selectedRaceColumns selectedRaceColumns} - * @return the position of a race column right before which to insert the race column so that it is the - * listposth race in the table or -1 if no such race column was found, e.g., in case the column - * needs to be inserted as the last race in the table - */ - private int getColumnPositionToInsert(RaceColumnDTO race, int listpos) { - int raceColumnCounter = 0; - int noRaceColumnCounter = 0; - boolean raceColumnFound = false; - for (int leaderboardPosition = 0; !raceColumnFound - && leaderboardPosition < getLeaderboardTable().getColumnCount(); leaderboardPosition++) { - Column c = getLeaderboardTable().getColumn(leaderboardPosition); - if (c instanceof RaceColumn) { - // RaceColumn raceColumn = (RaceColumn) c; - if (raceColumnCounter == listpos) { - raceColumnFound = true; - } else { - raceColumnCounter++; - } - } else { - noRaceColumnCounter++; - } - } - if (raceColumnFound) { - return raceColumnCounter + noRaceColumnCounter; - } else { - return -1; - } - } - - /** - * Removes all columns of type {@link RaceColumn} from the leaderboard table if their name is not in the list of - * names of the selectedRaceColumns. - */ - private void removeRaceColumnsNotSelected(Iterable selectedRaceColumns) { - Set selectedRaceColumnNames = new HashSet(); - for (RaceColumnDTO selectedRaceColumn : selectedRaceColumns) { - selectedRaceColumnNames.add(selectedRaceColumn.getRaceColumnName()); - } - List> columnsToRemove = new ArrayList>(); - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column c = getLeaderboardTable().getColumn(i); - if (c instanceof RaceColumn && (leaderboard == null - || !selectedRaceColumnNames.contains(((RaceColumn) c).getRaceColumnName()))) { - columnsToRemove.add(c); - } - } - for (Column c : columnsToRemove) { - removeColumn(c); - } - } - - /** - * Existing and matching race columns may still need to be removed, re-created and inserted because the "tracked" - * property may have changed, changing the columns expandability. - */ - private void createMissingAndAdjustExistingRaceColumns(LeaderboardDTO leaderboard) { - // Correct order of races in selectedRaceColum - Iterable correctedOrderSelectedRaces = raceColumnSelection - .getSelectedRaceColumnsOrderedAsInLeaderboard(leaderboard); - removeRaceColumnsNotSelected(correctedOrderSelectedRaces); - for (int selectedRaceCount = 0; selectedRaceCount < Util - .size(correctedOrderSelectedRaces); selectedRaceCount++) { - RaceColumnDTO selectedRaceColumn = Util.get(correctedOrderSelectedRaces, selectedRaceCount); - final RaceColumn raceColumn = selectedRaceColumn == null ? null - : getRaceColumnByRaceColumnName(selectedRaceColumn.getName()); - if (raceColumn != null) { - // remove all raceColumns, starting at a specific selectedRaceCount, up to but excluding the selected - // raceName with the result that selectedRace is at position selectedRaceCount afterwards - removeRaceColumnFromRaceColumnStartIndexBeforeRace(selectedRaceCount, selectedRaceColumn); - correctColumnData(raceColumn, selectedRaceColumn); - } else { - // get correct position to insert the column - int positionToInsert = getColumnPositionToInsert(selectedRaceColumn, selectedRaceCount); - if (positionToInsert != -1) { - insertColumn(positionToInsert, createRaceColumn(selectedRaceColumn)); - } else { - // Add the raceColumn with addRaceColumn, if no RaceColumn is existing in leaderboard - addRaceColumn(createRaceColumn(selectedRaceColumn)); - } - } - } - } - - protected RaceColumn createRaceColumn(RaceColumnDTO raceInLeaderboard) { - TextRaceColumn textRaceColumn = new TextRaceColumn(raceInLeaderboard, shallExpandRaceColumn(raceInLeaderboard), - SortingOrder.ASCENDING, RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE); - return textRaceColumn; - } - - /** - * To be expandable, a race column needs to have one or more tracked races associated and needs to have at least - * some GPS data available. Wind data is not required for expandability because several metrics can reasonably be - * determined even without reliable wind information. - */ - private boolean shallExpandRaceColumn(RaceColumnDTO raceColumnDTO) { - return showRaceDetails && raceColumnDTO.hasTrackedRaces() && raceColumnDTO.hasGPSData(); - } - - private void removeUnusedRaceColumns(LeaderboardDTO leaderboard) { - List> columnsToRemove = new ArrayList>(); - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column c = getLeaderboardTable().getColumn(i); - if (c instanceof RaceColumn && (leaderboard == null - || !leaderboard.raceListContains(((RaceColumn) c).getRaceColumnName()))) { - columnsToRemove.add(c); - } - } - // Tricky issue: if the race column is currently expanded, we can't know anymore how many detail columns - // there are because the updated LeaderboardDTO object doesn't contain the race anymore. We have to - // collapse and remove all LegColumns following the RaceColumn - for (Column c : columnsToRemove) { - removeColumn(c); - } - } - - /** - * If the last column is the totals column, remove it. Add the race column as the last column. - */ - private void addRaceColumn(RaceColumn raceColumn) { - if (getLeaderboardTable() - .getColumn(getLeaderboardTable().getColumnCount() - 1) instanceof TotalNetPointssColumn) { - removeColumn(getLeaderboardTable().getColumnCount() - 1); - } - addColumn(raceColumn); - } - - /** - * The regatta rank column shall be displayed if an only if the {@link DetailType#REGATTA_RANK} detail is selected - * in the {@link #selectedOverallDetailColumns}. It will then be displayed after the selection checkbox column (if - * any) and before the sail number / competitor name columns. - */ - private boolean isShowRegattaRankColumn() { - return selectedOverallDetailColumns.contains(DetailType.REGATTA_RANK); - } - - /** - * @param rankColumnIndex - * the column index (0-based) where to put the rank column, if needed - * @return the column index (0-based) for the next column; will equal rankColumnIndex if the rank - * column is not {@link #isShowRegattaRankColumn() supposed to be shown}, or one greater otherwise. - */ - private int ensureRankColumn(int rankColumnIndex) { - final int indexOfNextColumn = rankColumnIndex + (isShowRegattaRankColumn() ? 1 : 0); - if (getLeaderboardTable().getColumnCount() > rankColumnIndex) { - if (isShowRegattaRankColumn()) { - if (getLeaderboardTable().getColumn(rankColumnIndex) != getRankColumn()) { - insertColumn(rankColumnIndex, getRankColumn()); - } // else, the column is needed and is already in place - } else { - if (getLeaderboardTable().getColumn(rankColumnIndex) == getRankColumn()) { - removeColumn(rankColumnIndex); - } - } - } else { - if (isShowRegattaRankColumn()) { - insertColumn(rankColumnIndex, getRankColumn()); - } - } - return indexOfNextColumn; - } - - private int ensureRaceRankColumn(int rankColumnIndex) { - boolean required = isShowRaceRankColumn() && preSelectedRace != null; - final int indexOfNextColumn = required ? 1 : 0; - if (getLeaderboardTable().getColumnCount() > rankColumnIndex) { - if (required) { - if (getLeaderboardTable().getColumn(rankColumnIndex) != getRaceRankColumn()) { - insertColumn(rankColumnIndex, getRaceRankColumn()); - } - } else { - if (getLeaderboardTable().getColumn(rankColumnIndex) == getRaceRankColumn()) { - removeColumn(rankColumnIndex); - } - } - } else { - if (required) { - insertColumn(rankColumnIndex, getRaceRankColumn()); - } - } - return indexOfNextColumn; - } - - private AbstractSortableColumnWithMinMax getRaceRankColumn() { - return raceRankColumn; - } - - private boolean isShowRaceRankColumn() { - return showRaceRankColumn; - } - - /** - * @param selectionCheckboxColumnIndex - * the column index (0-based) where to put the selection checkbox column, if needed - * @return the column index (0-based) for the next column; will equal selectionCheckboxColumnIndex if - * the selection checkbox column is not {@link #showSelectionCheckbox supposed to be shown}, or one greater - * otherwise. - */ - private int ensureSelectionCheckboxColumn(int selectionCheckboxColumnIndex) { - final int indexOfNextColumn = selectionCheckboxColumnIndex + (showSelectionCheckbox ? 1 : 0); - if (getLeaderboardTable().getColumnCount() > selectionCheckboxColumnIndex) { - if (showSelectionCheckbox) { - if (getLeaderboardTable().getColumn(selectionCheckboxColumnIndex) != selectionCheckboxColumn) { - insertColumn(selectionCheckboxColumnIndex, selectionCheckboxColumn); - } // else, the column is needed and is already in place - } else { - if (getLeaderboardTable().getColumn(selectionCheckboxColumnIndex) == selectionCheckboxColumn) { - removeColumn(selectionCheckboxColumnIndex); - } - } - } else { - if (showSelectionCheckbox) { - insertColumn(selectionCheckboxColumnIndex, selectionCheckboxColumn); - } - } - return indexOfNextColumn; - } - - /** - * @return the 0-based index for the next column - */ - private int ensureSailIDAndCompetitorColumn(int columnIndexWhereToInsertTheNextColumn) { - if (isShowCompetitorSailId()) { - if (getLeaderboardTable().getColumnCount() <= columnIndexWhereToInsertTheNextColumn - || !(getLeaderboardTable() - .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof SailIDColumn)) { - insertColumn(columnIndexWhereToInsertTheNextColumn, - new SailIDColumn(new CompetitorFetcher() { - @Override - public CompetitorDTO getCompetitor(LeaderboardRowDTO t) { - return t.competitor; - } - })); - } - columnIndexWhereToInsertTheNextColumn++; - } else { - if (getLeaderboardTable().getColumnCount() > columnIndexWhereToInsertTheNextColumn && getLeaderboardTable() - .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof SailIDColumn) { - removeColumn(columnIndexWhereToInsertTheNextColumn); - } - } - if (isShowCompetitorFullName()) { - if (getLeaderboardTable().getColumnCount() <= columnIndexWhereToInsertTheNextColumn - || !(getLeaderboardTable() - .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof CompetitorColumn)) { - insertColumn(columnIndexWhereToInsertTheNextColumn, createCompetitorColumn()); - } - columnIndexWhereToInsertTheNextColumn++; - } else { - if (getLeaderboardTable().getColumnCount() > columnIndexWhereToInsertTheNextColumn && getLeaderboardTable() - .getColumn(columnIndexWhereToInsertTheNextColumn) instanceof CompetitorColumn) { - removeColumn(columnIndexWhereToInsertTheNextColumn); - } - } - return columnIndexWhereToInsertTheNextColumn; - } - - protected CompetitorColumn createCompetitorColumn() { - return new CompetitorColumn(new CompetitorColumnBase(this, stringMessages, - new CompetitorFetcher() { - @Override - public CompetitorDTO getCompetitor(LeaderboardRowDTO t) { - return t.competitor; - } - })); - } - - private void ensureTotalsColumn() { - // add a totals column on the right - if (getLeaderboardTable().getColumnCount() == 0 || !(getLeaderboardTable() - .getColumn(getLeaderboardTable().getColumnCount() - 1) instanceof TotalNetPointssColumn)) { - addColumn(new TotalNetPointssColumn(TOTAL_COLUMN_STYLE)); - } - } - - private void ensureTotalRacesSailedColumn() { - // add a totals column on the right - if (getLeaderboardTable().getColumnCount() == 0 || !(getLeaderboardTable() - .getColumn(getLeaderboardTable().getColumnCount() - 2) instanceof TotalRacesCompletedColumn)) { - insertColumn(getLeaderboardTable().getColumnCount() - 1, new TotalRacesCompletedColumn(TOTAL_COLUMN_STYLE)); - } - } - - private void ensureNoTotalRacesSailedColumn() { - if ((getLeaderboardTable() - .getColumn(getLeaderboardTable().getColumnCount() - 2) instanceof TotalRacesCompletedColumn)) { - removeColumn(getLeaderboardTable().getColumnCount() - 2); - } - } - - protected void updateTotalRacesSailedColumn() { - final boolean showTotalRacesCompletedColumn = isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(); - if (showTotalRacesCompletedColumn) { - ensureTotalRacesSailedColumn(); - } else { - ensureNoTotalRacesSailedColumn(); - } - } - - /** - * If the leaderboard {@link LeaderboardDTO#hasCarriedPoints has carried points} and if column #1 - * (second column, right of the competitor column) does not exist or is not of type {@link CarryColumn}, all columns - * starting from #1 will be removed and a {@link CarryColumn} will be added. If the leaderboard has no carried - * points but the display still shows a carry column, the column is removed. - * - * @param zeroBasedIndexOfCarryColumn - * the 0-based column index where to put the carry column, if any - * @return the 0-based index of the next column following this column if the carry column is to be shown, or - * zeroBasedIndexOfCarryColumn otherwise - */ - protected int updateCarryColumn(LeaderboardDTO leaderboard, int zeroBasedIndexOfCarryColumn) { - final boolean needsCarryColumn = leaderboard != null && leaderboard.hasCarriedPoints; - if (needsCarryColumn) { - ensureCarryColumn(zeroBasedIndexOfCarryColumn); - } else { - ensureNoCarryColumn(zeroBasedIndexOfCarryColumn); - } - return needsCarryColumn ? zeroBasedIndexOfCarryColumn + 1 : zeroBasedIndexOfCarryColumn; - } - - private void ensureNoCarryColumn(int zeroBasedIndexOfCarryColumn) { - if (getLeaderboardTable().getColumnCount() > zeroBasedIndexOfCarryColumn - && getLeaderboardTable().getColumn(zeroBasedIndexOfCarryColumn) instanceof CarryColumn) { - removeColumn(zeroBasedIndexOfCarryColumn); - } - } - - protected void ensureCarryColumn(int zeroBasedIndexOfCarryColumn) { - if (getLeaderboardTable().getColumnCount() <= zeroBasedIndexOfCarryColumn - || !(getLeaderboardTable().getColumn(zeroBasedIndexOfCarryColumn) instanceof CarryColumn)) { - while (getLeaderboardTable().getColumnCount() > zeroBasedIndexOfCarryColumn) { - removeColumn(zeroBasedIndexOfCarryColumn); - } - addColumn(createCarryColumn()); - } - } - - protected CarryColumn createCarryColumn() { - return new CarryColumn(); - } - - public SortedCellTable getLeaderboardTable() { - return leaderboardTable; - } - - public SailingServiceAsync getSailingService() { - return sailingService; - } - - protected String getLeaderboardName() { - return leaderboardName; - } - - protected void setLeaderboardName(String leaderboardName) { - this.leaderboardName = leaderboardName; - } - - protected ErrorReporter getErrorReporter() { - return errorReporter; - } - - protected ListDataProvider getData() { - return getLeaderboardTable().getDataProvider(); - } - - /** - * @param newTime - * ignored; may be null. The time for loading the leaderboard is determined using - * {@link #getLeaderboardDisplayDate()}. - */ - @Override - public void timeChanged(Date newTime, Date oldTime) { - loadCompleteLeaderboard(/* showProgress */ false); - } - - @Override - public void playStateChanged(PlayStates playState, PlayModes playMode) { - if (!isEmbedded) { - playPause.setHTML(getPlayPauseImgHtml(playState)); - playPause.setTitle(playState == PlayStates.Playing ? stringMessages.pauseAutomaticRefresh() - : stringMessages.autoRefresh()); - } - } - - @Override - public void playSpeedFactorChanged(double newPlaySpeedFactor) { - // nothing to do - } - - private List getTrackedRacesIdentifiers() { - List result = new ArrayList(); - for (RaceColumnDTO raceColumn : getLeaderboard().getRaceList()) { - for (FleetDTO fleet : raceColumn.getFleets()) { - if (raceColumn.getRaceIdentifier(fleet) != null) { - result.add(raceColumn.getRaceIdentifier(fleet)); - } - } - } - return result; - } - - @Override - public boolean hasSettings() { - return true; - } - - @Override - public boolean hasToolbar() { - return false; - } - - @Override - public SettingsDialogComponent getSettingsDialogComponent() { - return new LeaderboardSettingsDialogComponent(getSettings(), leaderboard.getNamesOfRaceColumns(), - stringMessages); - } - - @Override - public LeaderboardSettings getSettings() { - Iterable selectedRaceColumns = raceColumnSelection - .getSelectedRaceColumnsOrderedAsInLeaderboard(leaderboard); - List namesOfRaceColumnsToShow = new ArrayList<>(); - for (RaceColumnDTO raceColumn : selectedRaceColumns) { - namesOfRaceColumnsToShow.add(raceColumn.getName()); - } - LeaderboardSettings leaderboardSettings = new LeaderboardSettings( - Collections.unmodifiableList(selectedManeuverDetails), Collections.unmodifiableList(selectedLegDetails), - Collections.unmodifiableList(selectedRaceDetails), - Collections.unmodifiableList(selectedOverallDetailColumns), namesOfRaceColumnsToShow, - /* namesOfRacesToShow */ null, raceColumnSelection.getNumberOfLastRaceColumnsToShow(), - autoExpandPreSelectedRace, timer.getRefreshInterval(), /* nameOfRaceToSort */ null, - /* sortAscending */ true, /* updateUponPlayStateChange */ true, raceColumnSelection.getType(), - isShowAddedScores(), isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(), - isShowCompetitorSailId(), isShowCompetitorFullName(), isShowRaceRankColumn(), isShowCompetitorNationality); - return LeaderboardSettingsFactory.getInstance().keepDefaults(currentSettings, leaderboardSettings); - } - - @Override - public String getLocalizedShortName() { - return stringMessages.leaderboard(); - } - - private LeaderboardRowDTO getRow(String competitorIdAsString) { - synchronized (getData().getList()) { - for (LeaderboardRowDTO row : getData().getList()) { - if (row.competitor.getIdAsString().equals(competitorIdAsString)) { - return row; - } - } - } - return null; - } - - @Override - public void addedToSelection(CompetitorDTO competitor) { - LeaderboardRowDTO row = getRow(competitor.getIdAsString()); - if (row != null) { - leaderboardSelectionModel.setSelected(row, true); - } - } - - @Override - public void removedFromSelection(CompetitorDTO competitor) { - LeaderboardRowDTO row = getRow(competitor.getIdAsString()); - if (row != null) { - leaderboardSelectionModel.setSelected(row, false); - } - } - - private Iterable getSelectedRows() { - return leaderboardSelectionModel.getSelectedSet(); - } - - @Override - public void competitorsListChanged(Iterable competitors) { - setFilterControlStatus(); - if (timer.isInitialized()) { - timeChanged(timer.getTime(), null); - } - } - - @Override - public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { - setFilterControlStatus(); - updateLeaderboard(getLeaderboard()); - } - - @Override - public void filterChanged(FilterSet> oldFilterSet, - FilterSet> newFilterSet) { - // nothing to do; if the list of filtered competitors has changed, a separate call to - // filteredCompetitorsListChanged will occur - setFilterControlStatus(); - } - - public RaceColumnSelection getRaceColumnSelection() { - return raceColumnSelection; - } - - public void removeAllListeners() { - if (raceTimesInfoProviderListener != null) { - getRaceTimesInfoProvider().removeRaceTimesInfoProviderListener(raceTimesInfoProviderListener); - } - if (raceColumnSelection != null && raceColumnSelection.getType() == RaceColumnSelectionStrategies.LAST_N) { - getRaceTimesInfoProvider() - .removeRaceTimesInfoProviderListener((LastNRacesColumnSelection) raceColumnSelection); - } - if (timer != null) { - timer.removeTimeListener(this); - } - if (leaderboardUpdateListener != null) { - leaderboardUpdateListener.clear(); - } - } - - protected Timer getTimer() { - return timer; - } - - protected void blurFocusedElementAfterSelectionChange() { - // now "blur" the selected leaderboard element because it seems to cause the cell table to scroll to its top; - // see bug 2093. - blur(); - final ScheduledCommand blurCommand = new ScheduledCommand() { - @Override - public void execute() { - blur(); - } - }; - Scheduler.get().scheduleDeferred(blurCommand); - } - - private void blur() { - if (elementToBlur != null) { - elementToBlur.blur(); - } - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) { - timeChanged(getLeaderboardDisplayDate(), null); - } - } - - @Override - public void addLeaderboardUpdateListener(LeaderboardUpdateListener listener) { - this.leaderboardUpdateListener.add(listener); - } - - @Override - public void removeLeaderboardUpdateListener(LeaderboardUpdateListener listener) { - this.leaderboardUpdateListener.remove(listener); - } - - protected void informLeaderboardUpdateListenersAboutLeaderboardUpdated(LeaderboardDTO leaderboard) { - for (LeaderboardUpdateListener listener : new ArrayList( - this.leaderboardUpdateListener)) { - listener.updatedLeaderboard(leaderboard); - } - } - - protected void informLeaderboardUpdateListenersAboutRaceSelected(RaceIdentifier raceIdentifier, - RaceColumnDTO raceColumn) { - for (LeaderboardUpdateListener listener : this.leaderboardUpdateListener) { - listener.currentRaceSelected(raceIdentifier, raceColumn); - } - } - - public boolean hasLiveRace() { - return getLeaderboard().hasLiveRace(timer.getLiveTimePointInMillis()); - } - - public String getLiveRacesText() { - String result = ""; - List> liveRaces = leaderboard.getLiveRaces(timer.getLiveTimePointInMillis()); - boolean isMeta = leaderboard.type.isMetaLeaderboard(); - if (!liveRaces.isEmpty()) { - if (liveRaces.size() == 1) { - String text = getLiveRaceText(liveRaces.get(0), isMeta); - result = isMeta ? stringMessages.regattaIsLive(text) : stringMessages.raceIsLive(text); - } else { - String names = ""; - for (Pair liveRace : liveRaces) { - names += getLiveRaceText(liveRace, isMeta) + ", "; - } - // remove last ", " - names = names.substring(0, names.length() - 2); - result = isMeta ? stringMessages.regattasAreLive(names) : stringMessages.racesAreLive(names); - } - } - return result; - } - - private String getLiveRaceText(Pair liveRace, boolean isMeta) { - String raceName = liveRace.getA().getRaceColumnName(), fleetName = liveRace.getB().getName(); - boolean isDefaultFleet = LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName); - return raceName + ((isDefaultFleet || isMeta) ? "" : (" (" + liveRace.getB().getName() + ")")); - } - - @Override - public String getDependentCssClassName() { - return "leaderboard"; - } - - @Override - public void addBusyStateChangeListener(BusyStateChangeListener listener) { - busyStateChangeListeners.add(listener); - } - - @Override - public void removeBusyStateChangeListener(BusyStateChangeListener listener) { - busyStateChangeListeners.remove(listener); - } - - @Override - public boolean isBusy() { - return busyIndicator.isBusy(); - } - - private void setBusyState(boolean isBusy) { - if (busyIndicator.isBusy() != isBusy) { - busyIndicator.setBusy(isBusy); - for (BusyStateChangeListener listener : busyStateChangeListeners) { - listener.onBusyStateChange(isBusy); - } - } - } - - @Override - public void addBusyTask() { - busyTaskCounter++; - if (busyTaskCounter == 1) { - setBusyState(true); - } - } - - @Override - public void removeBusyTask() { - busyTaskCounter--; - if (busyTaskCounter == 0) { - setBusyState(false); - } - } - - @Override - public String getId() { - return LeaderboardPanelLifecycle.ID; - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java index d432df66dbb..912545ff66d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java @@ -61,7 +61,7 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.CompetitorColumnBase; import com.sap.sailing.gwt.ui.leaderboard.CompetitorFetcher; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSortableColumnWithMinMax; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.ErrorReporter; @@ -72,13 +72,13 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialog; import com.sap.sse.gwt.client.useragent.UserAgentDetails; /** - * An editable version of the {@link LeaderboardPanel} which allows a user to enter carried / accumulated + * An editable version of the {@link ClassicLeaderboardPanel} which allows a user to enter carried / accumulated * points and fix individual race scores. * * @author Axel Uhl (d043530) * */ -public class EditableLeaderboardPanel extends LeaderboardPanel { +public class EditableLeaderboardPanel extends ClassicLeaderboardPanel { private static EditableLeaderboardResources resources = GWT.create(EditableLeaderboardResources.class); final DateBox lastScoreCorrectionTimeBox; 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 bdec28a4646..eb678be3934 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 @@ -16,7 +16,7 @@ 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.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; import com.sap.sse.gwt.client.player.Timer; @@ -42,7 +42,7 @@ import com.sap.sse.gwt.client.player.Timer; * {@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 + * it using the {@link #getLeaderboardPanel()}.{@link ClassicLeaderboardPanel#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}. * @@ -53,7 +53,7 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI private Timer timer; private RegattaAndRaceIdentifier raceIdentifier; private RaceTimePanel raceTimePanel; - private LeaderboardPanel leaderboardPanel; + private ClassicLeaderboardPanel leaderboardPanel; private RaceBoardPanel raceBoardPanel; private Map raceTimesInfo; @@ -91,7 +91,7 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI return raceTimePanel; } - protected LeaderboardPanel getLeaderboardPanel() { + protected ClassicLeaderboardPanel getLeaderboardPanel() { return leaderboardPanel; } @@ -161,7 +161,7 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI getTimer().getTime(), raceColumnNameAsList, /* addOverallDetails */ false, getLeaderboard(), /* fillTotalPointsUncorrected */ false, /* timerToAdjustOffsetIn */ getTimer(), /* errorReporter */ null, StringMessages.INSTANCE); - getLeaderboardPanel().getExecutor().execute(getLeaderboardByNameAction, LeaderboardPanel.LOAD_LEADERBOARD_DATA_CATEGORY, callback); + getLeaderboardPanel().getExecutor().execute(getLeaderboardByNameAction, ClassicLeaderboardPanel.LOAD_LEADERBOARD_DATA_CATEGORY, callback); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index acf2a5546bc..874c47dac09 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -76,7 +76,7 @@ import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapResources; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings; import com.sap.sailing.gwt.ui.leaderboard.CompetitorFilterPanel; import com.sap.sailing.gwt.ui.leaderboard.ExplicitRaceColumnSelectionWithPreselectedRace; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; +import com.sap.sailing.gwt.ui.leaderboard.ClassicLeaderboardPanel; import com.sap.sailing.gwt.ui.raceboard.RaceBoardResources.RaceBoardMainCss; import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sse.common.Util; @@ -129,7 +129,7 @@ public class RaceBoardPanel private final RegattaAndRaceIdentifier selectedRaceIdentifier; private final String leaderboardName; - private final LeaderboardPanel leaderboardPanel; + private final ClassicLeaderboardPanel leaderboardPanel; private WindChart windChart; private MultiCompetitorRaceChart competitorChart; private MediaPlayerManagerComponent mediaPlayerManagerComponent; @@ -445,7 +445,7 @@ public class RaceBoardPanel } } - private LeaderboardPanel createLeaderboardPanel(RaceBoardPerspectiveLifecycle lifecycle, + private ClassicLeaderboardPanel createLeaderboardPanel(RaceBoardPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, String leaderboardName, String leaderboardGroupName, CompetitorFilterPanel competitorSearchTextBox, boolean isScreenLargeEnoughToInitiallyDisplayLeaderboard) { @@ -462,7 +462,7 @@ public class RaceBoardPanel /* don't showCompetitorFullNameColumn in case screen is so small that we don't * even display the leaderboard initially */ isScreenLargeEnoughToInitiallyDisplayLeaderboard,raceColumn.getNumberOfLastRaceColumnsToShow(),raceColumn.getType()); leaderboardSettings = LeaderboardSettingsFactory.getInstance().overrideDefaultValuesWithNewDefaults(leaderboardSettings, defaultLeaderboardSettingsForCurrentPlayMode); - return new LeaderboardPanel(this, getComponentContext(), sailingService, asyncActionsExecutor, + return new ClassicLeaderboardPanel(this, getComponentContext(), sailingService, asyncActionsExecutor, leaderboardSettings, selectedRaceIdentifier != null, selectedRaceIdentifier, competitorSelectionProvider, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, @@ -477,7 +477,7 @@ public class RaceBoardPanel componentViewer.forceLayout(); } - LeaderboardPanel getLeaderboardPanel() { + ClassicLeaderboardPanel getLeaderboardPanel() { return leaderboardPanel; }