mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-28 14:57:12 +00:00
Merge branch 'master' into bug3344
Conflicts: java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java
This commit is contained in:
commit
34740f12ab
30 files changed
+5346
-5099
No files matched your search
+1
-1
@@ -20,7 +20,7 @@ public class LeaderboardPanelMock extends LeaderboardPanel {
|
||||
String leaderboardName, ErrorReporter errorReporter,
|
||||
StringMessages stringMessages) {
|
||||
super(sailingService, new AsyncActionsExecutor(), LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true), new CompetitorSelectionModel(
|
||||
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true, /* showCompetitorSailIdColumn */ true, /* showCompetitorFullNameColumn */ true), new CompetitorSelectionModel(
|
||||
/* hasMultiSelection */true), leaderboardName, errorReporter, stringMessages, new UserAgentDetails("gecko1_8"), /* showRaceDetails */ true);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,13 +98,13 @@ body, table td, select, button {
|
||||
padding: 49px 0 0 80px;
|
||||
width: 300px;
|
||||
color: #7c7c7c;
|
||||
background: url(images/important-message.png) left center no-repeat;
|
||||
background: url(images/important-message.png) left center no-repeat;
|
||||
}
|
||||
|
||||
.LeaderboardHeader {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: 400px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-family: 'Open Sans', Arial, Verdana, sans-serif;
|
||||
font-size: 18px;
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
|
||||
RegattaRaceInformation-Header
|
||||
RaceName-Label
|
||||
RaceSeriesAndFleet-Label
|
||||
|
||||
RegattaAndRaceTime-Header???
|
||||
RegattaName-Anchor
|
||||
*/
|
||||
|
||||
|
||||
@@ -87,6 +94,14 @@ table {
|
||||
padding-top: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.CombinedWindPanelParentDiv.indentsmall,
|
||||
.TrueNorthIndicatorPanelParentDiv.indentsmall {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.CombinedWindPanelParentDiv.indentbig,
|
||||
.TrueNorthIndicatorPanelParentDiv.indentbig {
|
||||
padding-left: 45px;
|
||||
}
|
||||
|
||||
.MapSimulationLegendParentDiv {
|
||||
padding-bottom: 10px;
|
||||
@@ -573,7 +588,8 @@ img.openColumn {
|
||||
url(images/viewicons/@1x/img/SAP_RV_Leaderboard_GHOSTED.png),
|
||||
url(images/arrows_top_black_small.png);
|
||||
background-repeat: no-repeat, no-repeat;
|
||||
background-position: 4% 50%, 96% 50%
|
||||
background-position: 4% 50%, 96% 50%;
|
||||
background-position: 5px center, calc(100% - 6px) center;
|
||||
}
|
||||
|
||||
.gwt-SplitLayoutPanel-EastToggleButton-Closed-leaderboard {
|
||||
@@ -582,7 +598,8 @@ img.openColumn {
|
||||
url(images/viewicons/@1x/img/SAP_RV_Leaderboard_INACTIVE.png),
|
||||
url(images/arrows_bottom_black_small.png);
|
||||
background-repeat: no-repeat, no-repeat;
|
||||
background-position: 4% 50%, 96% 50%
|
||||
background-position: 4% 50%, 96% 50%;
|
||||
background-position: 5px center, calc(100% - 6px) center;
|
||||
}
|
||||
|
||||
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed-media,
|
||||
@@ -664,6 +681,32 @@ img.openColumn {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.compactHeader .RegattaRaceInformation-Header {
|
||||
left: 110px;
|
||||
}
|
||||
.compactHeader .RegattaRaceInformation-Header .RaceName-Label {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
.compactHeader .RegattaRaceInformation-Header .RaceSeriesAndFleet-Label {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.compactHeader .RegattaAndRaceTime-Header {
|
||||
top: 11px;
|
||||
}
|
||||
.compactHeader .RegattaAndRaceTime-Header .RegattaName-Anchor {
|
||||
width: 115px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
padding: 2px 0 2px 20px;
|
||||
background-position: 4px -1px;
|
||||
}
|
||||
|
||||
.VideoPopup-Close-Button {
|
||||
border: none;
|
||||
}
|
||||
|
||||
+4
-4
@@ -107,7 +107,7 @@ public class AutoPlayController implements RaceTimesInfoProviderListener {
|
||||
asyncActionsExecutor = new AsyncActionsExecutor();
|
||||
showWindChart = false;
|
||||
leaderboard = null;
|
||||
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true);
|
||||
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true, /* showCompetitorSailIdColumn */ true, /* showCompetitorFullNameColumn */ true);
|
||||
|
||||
leaderboardTimer = new Timer(PlayModes.Live, /* delayBetweenAutoAdvancesInMilliseconds */1000l);
|
||||
leaderboardTimer.setLivePlayDelayInMillis(delayToLiveInMillis);
|
||||
@@ -170,10 +170,10 @@ public class AutoPlayController implements RaceTimesInfoProviderListener {
|
||||
RaceSelectionModel raceSelectionModel = new RaceSelectionModel();
|
||||
List<RegattaAndRaceIdentifier> singletonList = Collections.singletonList(raceToShow);
|
||||
raceSelectionModel.setSelection(singletonList);
|
||||
|
||||
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, userService, asyncActionsExecutor,
|
||||
competitorsAndTheirBoats, raceboardTimer, raceSelectionModel, leaderboardName, null, /* event */null, raceboardViewConfig,
|
||||
errorReporter, StringMessages.INSTANCE, userAgent, raceTimesInfoProvider, /* showMapControls */false);
|
||||
competitorsAndTheirBoats, raceboardTimer, raceSelectionModel, leaderboardName, null, /* event */ null, raceboardViewConfig,
|
||||
errorReporter, StringMessages.INSTANCE, userAgent, raceTimesInfoProvider, /* showMapControls */ false,
|
||||
/* isScreenLargeEnoughToOfferChartSupport */ true);
|
||||
return raceBoardPanel;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ public class EventParamUtils {
|
||||
.createNewDefaultSettings(null, null,
|
||||
/* overallDetails */overallDetails, null,
|
||||
/* autoExpandFirstRace */false, refreshIntervalMillis, numberOfLastRacesToShow,
|
||||
raceColumnSelectionStrategy);
|
||||
raceColumnSelectionStrategy, /* showCompetitorSailIdColumns */ true, /*showCompetitorFullNameColumn*/ true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
-2
@@ -44,7 +44,6 @@ import com.sap.sailing.gwt.home.shared.places.fakeseries.SeriesDefaultPlace;
|
||||
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.refresh.ErrorAndBusyClientFactory;
|
||||
import com.sap.sailing.gwt.ui.raceboard.RaceBoardViewConfiguration;
|
||||
import com.sap.sailing.gwt.ui.shared.util.NullSafeComparableComparator;
|
||||
|
||||
public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> extends AbstractActivity implements Presenter {
|
||||
@@ -248,7 +247,6 @@ public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> ex
|
||||
}
|
||||
linkParams.put("raceName", trackedRaceName);
|
||||
linkParams.put("regattaName", regattaName);
|
||||
linkParams.put(RaceBoardViewConfiguration.PARAM_VIEW_MODE, "simple");
|
||||
// TODO this must only be forwarded if there is a logged-on user
|
||||
// linkParams.put(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, "true");
|
||||
return linkParams;
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ public class ExperimentalFeatures {
|
||||
/**
|
||||
* Enables/disables the link to the race board on mobile races view (competition format), in case of a tracked race
|
||||
*/
|
||||
public static final boolean ENABLE_RACE_VIEWER_LINK_ON_MOBILE = false;
|
||||
public static final boolean ENABLE_RACE_VIEWER_LINK_ON_MOBILE = true;
|
||||
/**
|
||||
* Provide selection to filter regattas by boat category on mobile multiregatta event overview
|
||||
*/
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ public class RegattaOverviewPanel extends SimplePanel {
|
||||
if (leaderboardsTabPanel != null) {
|
||||
if (showLeaderboard) {
|
||||
final CompetitorSelectionModel competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */ true);
|
||||
final LeaderboardSettings leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true);
|
||||
final LeaderboardSettings leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true, /* showCompetitorSailIdColumn */ true, /* showCompetitorFullNameColumn */ true);
|
||||
sailingService.getLeaderboardsByEvent(eventDTO, new MarkedAsyncCallback<List<StrippedLeaderboardDTO>>(
|
||||
new AsyncCallback<List<StrippedLeaderboardDTO>>() {
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
|
||||
private boolean redrawAllMarkersPendingForMinMaxBeingInitialized;
|
||||
|
||||
public RaceTimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages,
|
||||
RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible) {
|
||||
super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible);
|
||||
RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible, boolean isScreenLargeEnoughToOfferChartSupport) {
|
||||
super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible, isScreenLargeEnoughToOfferChartSupport);
|
||||
this.raceTimesInfoProvider = raceTimesInfoProvider;
|
||||
selectedRace = null;
|
||||
autoAdjustPlayMode = true;
|
||||
|
||||
+10
-2
@@ -89,7 +89,13 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
private static ClientResources resources = GWT.create(ClientResources.class);
|
||||
protected static TimePanelCss timePanelCss = TimePanelCssResources.INSTANCE.css();
|
||||
|
||||
public TimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages, boolean canReplayWhileLiveIsPossible) {
|
||||
/**
|
||||
* @param isScreenLargeEnoughToOfferChartSupport
|
||||
* if <code>true</code>, the right padding will be set such that the time panel lines up with charts such
|
||||
* as the competitor chart or the wind chart shown above it
|
||||
*/
|
||||
public TimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages,
|
||||
boolean canReplayWhileLiveIsPossible, boolean isScreenLargeEnoughToOfferChartSupport) {
|
||||
this.timer = timer;
|
||||
this.timeRangeProvider = timeRangeProvider;
|
||||
this.stringMessages = stringMessages;
|
||||
@@ -105,7 +111,9 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
timePanelSliderFlowWrapper = new FlowPanel();
|
||||
timePanelSlider.setStyleName("timePanelSlider");
|
||||
timePanelSlider.getElement().getStyle().setPaddingLeft(66, Unit.PX);
|
||||
timePanelSlider.getElement().getStyle().setPaddingRight(66, Unit.PX);
|
||||
if (isScreenLargeEnoughToOfferChartSupport) {
|
||||
timePanelSlider.getElement().getStyle().setPaddingRight(66, Unit.PX);
|
||||
}
|
||||
timePanelSliderFlowWrapper.add(timePanelSlider);
|
||||
|
||||
playSpeedImg = resources.timesliderPlaySpeedIcon();
|
||||
|
||||
+2636
-2609
File diff suppressed because it is too large.
Load diff
+3
-2
@@ -1,5 +1,6 @@
|
||||
.competitorFilterContainer {
|
||||
width: 400px;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
height: 2.14em;
|
||||
background-color: #FFFFFF;
|
||||
margin-left: 0px;
|
||||
@@ -22,7 +23,7 @@
|
||||
.searchInput {
|
||||
font-size: .933333333333333em;
|
||||
height: 2.142857142857143em;
|
||||
width: 280px;
|
||||
width: 70%;
|
||||
border: none;
|
||||
background-color: #eaeaea;
|
||||
padding: 0;
|
||||
|
||||
+1
-1
@@ -258,7 +258,7 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
|
||||
result = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null,
|
||||
/* overallDetails */ overallDetails, null,
|
||||
/* autoExpandFirstRace */false, refreshIntervalMillis, numberOfLastRacesToShow,
|
||||
raceColumnSelectionStrategy);
|
||||
raceColumnSelectionStrategy, /* showCompetitorSailIdColumns */ true, /*showCompetitorFullNameColumn*/ true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+13
-3
@@ -344,7 +344,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
private Element elementToBlur;
|
||||
private boolean showSelectionCheckbox;
|
||||
|
||||
private List<LeaderboardUpdateListener> leaderboardUpdateListener;
|
||||
private final List<LeaderboardUpdateListener> leaderboardUpdateListener;
|
||||
|
||||
private boolean initialCompetitorFilterHasBeenApplied = false;
|
||||
private final boolean showCompetitorFilterStatus;
|
||||
@@ -668,7 +668,14 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
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();
|
||||
sb.appendHtmlConstant("<div style=\"border-bottom: 2px solid " + competitorColor + ";\">");
|
||||
}
|
||||
|
||||
if (flagImageURL != null && !flagImageURL.isEmpty()) {
|
||||
sb.appendHtmlConstant("<img src=\"" + flagImageURL + "\" width=\"18px\" height=\"12px\" title=\"" + competitor.getName() + "\"/>");
|
||||
sb.appendHtmlConstant(" ");
|
||||
@@ -685,6 +692,9 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
}
|
||||
}
|
||||
sb.appendEscaped(competitor.getSailID());
|
||||
if (showBoatColor) {
|
||||
sb.appendHtmlConstant("</div>");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3178,7 +3188,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
if (timer != null) {
|
||||
timer.removeTimeListener(this);
|
||||
}
|
||||
if(leaderboardUpdateListener != null) {
|
||||
if (leaderboardUpdateListener != null) {
|
||||
leaderboardUpdateListener.clear();
|
||||
}
|
||||
}
|
||||
|
||||
+14
-10
@@ -78,11 +78,11 @@ public class LeaderboardSettingsFactory {
|
||||
/* refresh interval */ null, /* name of race to sort */ nameOfRaceToSort,
|
||||
/* ascending */ true, /* updateUponPlayStateChange */ true, raceColumnSelection.getType(),
|
||||
/*showAddedScores*/ false, /*showOverallRacesCompleted*/ false,
|
||||
/*showCompetitorSailIdColumns*/showCompetitorSailIdColumn, /*showCompetitorFullNameColumn*/showCompetitorNameColumn);
|
||||
showCompetitorSailIdColumn, showCompetitorNameColumn);
|
||||
break;
|
||||
case Replay:
|
||||
settings = createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, nameOfRaceToSort, /* autoExpandFirstRace */
|
||||
nameOfRaceColumnToShow != null, showRegattaRank);
|
||||
nameOfRaceColumnToShow != null, showRegattaRank, showCompetitorSailIdColumn, showCompetitorNameColumn);
|
||||
break;
|
||||
}
|
||||
return settings;
|
||||
@@ -127,28 +127,32 @@ public class LeaderboardSettingsFactory {
|
||||
* such a meta leaderboard exists; if multiple such meta leaderboards exist, they are all shown
|
||||
*/
|
||||
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
|
||||
List<String> namesOfRacesToShow, String nameOfRaceToSort, boolean autoExpandPreSelectedRace, Boolean showRegattaRank) {
|
||||
List<String> namesOfRacesToShow, String nameOfRaceToSort, boolean autoExpandPreSelectedRace, Boolean showRegattaRank,
|
||||
boolean showCompetitorSailIdColumn, boolean showCompetitorFullNameColumn) {
|
||||
final List<DetailType> overallDetails = createOverallDetailsForShowRegattaRank(showRegattaRank);
|
||||
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, overallDetails,
|
||||
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null);
|
||||
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null,
|
||||
showCompetitorSailIdColumn, showCompetitorFullNameColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #createNewDefaultSettings(List, List, String, boolean, boolean)}, only that an additional refresh interval for auto-refresh
|
||||
* Like {@link #createNewDefaultSettings(List, List, String, boolean, boolean, boolean, boolean)}, only that an additional refresh interval for auto-refresh
|
||||
* may be specified; if <code>null</code>, no auto-refresh shall be performed
|
||||
*/
|
||||
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
|
||||
private LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
|
||||
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
|
||||
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis) {
|
||||
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, boolean showCompetitorSailIdColumns, boolean showCompetitorFullNameColumn) {
|
||||
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, overallDetailsToShow,
|
||||
nameOfRaceToSort, autoExpandPreSelectedRace, refreshIntervalMillis,
|
||||
/* numberOfLastRacesToShow */null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT);
|
||||
/* numberOfLastRacesToShow */null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT,
|
||||
showCompetitorSailIdColumns, showCompetitorFullNameColumn);
|
||||
}
|
||||
|
||||
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
|
||||
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
|
||||
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, Integer numberOfLastRacesToShow,
|
||||
RaceColumnSelectionStrategies raceColumnSelectionStrategy) {
|
||||
RaceColumnSelectionStrategies raceColumnSelectionStrategy, boolean showCompetitorSailIdColumns,
|
||||
boolean showCompetitorFullNameColumn) {
|
||||
if (namesOfRaceColumnsToShow != null && namesOfRacesToShow != null) {
|
||||
throw new IllegalArgumentException("Can specify race columns either by column or by race name, not both");
|
||||
}
|
||||
@@ -168,7 +172,7 @@ public class LeaderboardSettingsFactory {
|
||||
autoExpandPreSelectedRace, refreshIntervalMillis, /* sort by column */ nameOfRaceToSort,
|
||||
/* ascending */ true, /* updateUponPlayStateChange */ true, raceColumnSelectionStrategy,
|
||||
/*showAddedScores*/ false, /*showOverallRacesCompleted*/ false,
|
||||
/*showCompetitorSailIdColumns*/true, /*showCompetitorFullNameColumn*/true);
|
||||
showCompetitorSailIdColumns, showCompetitorFullNameColumn);
|
||||
}
|
||||
|
||||
public LeaderboardSettings mergeLeaderboardSettings(LeaderboardSettings settingsWithRaceSelection, LeaderboardSettings settingsWithDetails) {
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ public class LeaderboardUrlConfigurationDialog extends SettingsDialog<Leaderboar
|
||||
}
|
||||
LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
namesOfRaceColumnsToShow, /* namesOfRacesToShow */null, /* nameOfRaceToSort */null, /* autoExpandPreSelectedRace */
|
||||
false, /* showRegattaRank */ true);
|
||||
false, /* showRegattaRank */ true, /* showCompetitorSailIdColumn */ true, /* showCompetitorFullNameColumn */ true);
|
||||
leaderboardSettingsDialogComponent = new LeaderboardSettingsDialogComponent(settings.getManeuverDetailsToShow(),
|
||||
settings.getLegDetailsToShow(), settings.getRaceDetailsToShow(), settings.getOverallDetailsToShow(), raceList,
|
||||
/* select all races by default */ raceList, new ExplicitRaceColumnSelection(),
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ public class MultiLeaderboardPanel extends AbstractLazyComponent<LeaderboardSett
|
||||
private LeaderboardSettings getOrCreateLeaderboardSettings(String leaderboardName, LeaderboardSettings currentLeaderboardSettings) {
|
||||
LeaderboardSettings newLeaderboardSettings = leaderboardNamesAndSettings.get(leaderboardName);
|
||||
if(newLeaderboardSettings == null) {
|
||||
newLeaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, false, /* showRegattaRank */ true);
|
||||
newLeaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, false, /* showRegattaRank */ true, /* showCompetitorSailIdColumn */ true, /* showCompetitorFullNameColumn */ true);
|
||||
}
|
||||
if(currentLeaderboardSettings != null) {
|
||||
newLeaderboardSettings = LeaderboardSettingsFactory.getInstance().mergeLeaderboardSettings(newLeaderboardSettings, currentLeaderboardSettings);
|
||||
|
||||
+1
-1
@@ -584,7 +584,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
|
||||
String leaderboardName, String leaderboardGroupName, final ErrorReporter errorReporter,
|
||||
final StringMessages stringMessages, UserAgentDetails userAgent) {
|
||||
super(sailingService, asyncActionsExecutor, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
/* racesToShow */ null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true),
|
||||
/* racesToShow */ null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false, /* showRegattaRank */ true, /* showCompetitorSailIdColumn */ true, /* showCompetitorFullNameColumn */ true),
|
||||
new CompetitorSelectionModel(/* hasMultiSelection */true),
|
||||
leaderboardName, errorReporter, stringMessages, userAgent, /* showRaceDetails */ true);
|
||||
suppressedCompetitorsShown = new ListDataProvider<CompetitorDTO>(new ArrayList<CompetitorDTO>());
|
||||
|
||||
+2
-1
@@ -151,7 +151,8 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint
|
||||
// Use a TimePanel to manage wind chart zoom, although the TimePanel itself is not being displayed;
|
||||
// let the time panel always return to "live" mode.
|
||||
final TimePanel<TimePanelSettings> timePanel = new TimePanel<TimePanelSettings>(
|
||||
timer, timeRangeWithZoomProvider, getStringMessages(), /* canReplayWhileLive */ false) {
|
||||
timer, timeRangeWithZoomProvider, getStringMessages(), /* canReplayWhileLive */ false,
|
||||
/* isScreenLargeEnoughToOfferChartSupport set to true iff wind chart will be displayed */ showWindChart) {
|
||||
protected boolean isLiveModeToBeMadePossible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
+205
-197
@@ -1,197 +1,205 @@
|
||||
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
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.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.rpc.ServiceDefTarget;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.DockLayoutPanel;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.RootLayoutPanel;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint;
|
||||
import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel;
|
||||
import com.sap.sailing.gwt.ui.client.MediaService;
|
||||
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
|
||||
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO;
|
||||
import com.sap.sse.gwt.client.EntryPointHelper;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
||||
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
|
||||
|
||||
public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
|
||||
private RaceWithCompetitorsDTO selectedRace;
|
||||
|
||||
private static final String PARAM_REGATTA_NAME = "regattaName";
|
||||
private static final String PARAM_RACE_NAME = "raceName";
|
||||
private static final String PARAM_LEADERBOARD_NAME = "leaderboardName";
|
||||
private static final String PARAM_LEADERBOARD_GROUP_NAME = "leaderboardGroupName";
|
||||
private static final String PARAM_EVENT_ID = "eventId";
|
||||
|
||||
private String regattaName;
|
||||
private String raceName;
|
||||
private String leaderboardName;
|
||||
private String leaderboardGroupName;
|
||||
private UUID eventId;
|
||||
private RaceBoardViewConfiguration raceboardViewConfig;
|
||||
|
||||
private final MediaServiceAsync mediaService = GWT.create(MediaService.class);
|
||||
|
||||
@Override
|
||||
protected void doOnModuleLoad() {
|
||||
super.doOnModuleLoad();
|
||||
EntryPointHelper.registerASyncService((ServiceDefTarget) mediaService, RemoteServiceMappingConstants.mediaServiceRemotePath);
|
||||
// read mandatory parameters
|
||||
regattaName = Window.Location.getParameter(PARAM_REGATTA_NAME);
|
||||
raceName = Window.Location.getParameter(PARAM_RACE_NAME);
|
||||
String leaderboardNameParamValue = Window.Location.getParameter(PARAM_LEADERBOARD_NAME);
|
||||
String leaderboardGroupNameParamValue = Window.Location.getParameter(PARAM_LEADERBOARD_GROUP_NAME);
|
||||
if (leaderboardNameParamValue != null && !leaderboardNameParamValue.isEmpty()) {
|
||||
leaderboardName = leaderboardNameParamValue;
|
||||
}
|
||||
if (leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) {
|
||||
leaderboardGroupName = leaderboardGroupNameParamValue;
|
||||
}
|
||||
String eventIdParamValue = Window.Location.getParameter(PARAM_EVENT_ID);
|
||||
if (eventIdParamValue != null && !eventIdParamValue.isEmpty()) {
|
||||
eventId = UUID.fromString(eventIdParamValue);
|
||||
}
|
||||
if (leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) {
|
||||
leaderboardGroupName = leaderboardGroupNameParamValue;
|
||||
}
|
||||
if (regattaName == null || regattaName.isEmpty() || raceName == null || raceName.isEmpty() ||
|
||||
leaderboardName == null || leaderboardName.isEmpty()) {
|
||||
createErrorPage("This page requires a valid regatta name, race name and leaderboard name.");
|
||||
return;
|
||||
}
|
||||
|
||||
// read optional parameters
|
||||
boolean showLeaderboard = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_LEADERBOARD, true /* default*/);
|
||||
boolean showWindChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_WINDCHART, false /* default*/);
|
||||
boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default*/);
|
||||
boolean showViewStreamlets = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_STREAMLETS, false /* default*/);
|
||||
boolean showViewStreamletColors = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_STREAMLET_COLORS, false /* default*/);
|
||||
boolean showViewSimulation = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_SIMULATION, true /* default*/);
|
||||
String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/);
|
||||
final boolean canReplayWhileLiveIsPossible = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, false);
|
||||
final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, true);
|
||||
final String defaultMedia = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_DEFAULT_MEDIA, null /* default*/);
|
||||
final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, true /* default*/);
|
||||
raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard,
|
||||
showWindChart, showCompetitorsChart, showViewStreamlets, showViewStreamletColors, showViewSimulation, canReplayWhileLiveIsPossible, autoSelectMedia, defaultMedia);
|
||||
|
||||
sailingService.getRaceboardData(regattaName, raceName, leaderboardName, leaderboardGroupName, eventId, new AsyncCallback<RaceboardDataDTO>() {
|
||||
@Override
|
||||
public void onSuccess(RaceboardDataDTO result) {
|
||||
checkUrlParameters(result, canReplayWhileLiveIsPossible, showMapControls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
reportError("Error trying to create the raceboard: " + caught.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createErrorPage(String message) {
|
||||
final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX);
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService());
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
RootLayoutPanel.get().add(vp);
|
||||
vp.addNorth(logoAndTitlePanel, 100);
|
||||
vp.add(new Label(message));
|
||||
}
|
||||
|
||||
private void checkUrlParameters(RaceboardDataDTO raceboardData, boolean canReplayWhileLiveIsPossible, boolean showMapControls) {
|
||||
if (!raceboardData.isValidLeaderboard()) {
|
||||
createErrorPage(getStringMessages().noSuchLeaderboard());
|
||||
return;
|
||||
}
|
||||
if (eventId != null && !raceboardData.isValidEvent()) {
|
||||
createErrorPage(getStringMessages().noSuchEvent());
|
||||
}
|
||||
if (leaderboardGroupName != null) {
|
||||
if(!raceboardData.isValidLeaderboardGroup()) {
|
||||
createErrorPage(getStringMessages().leaderboardNotContainedInLeaderboardGroup(leaderboardName, leaderboardGroupName));
|
||||
return;
|
||||
}
|
||||
if (eventId != null && raceboardData.isValidLeaderboardGroup() && !raceboardData.isValidEvent()) {
|
||||
createErrorPage(getStringMessages().leaderboardGroupNotContainedInEvent(leaderboardGroupName, eventId.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (raceboardData.getRace() == null) {
|
||||
createErrorPage("Could not obtain a race with name " + raceName + " for a regatta with name " + regattaName);
|
||||
return;
|
||||
}
|
||||
selectedRace = raceboardData.getRace();
|
||||
Window.setTitle(selectedRace.getName());
|
||||
RaceSelectionModel raceSelectionModel = new RaceSelectionModel();
|
||||
List<RegattaAndRaceIdentifier> singletonList = Collections.singletonList(selectedRace.getRaceIdentifier());
|
||||
raceSelectionModel.setSelection(singletonList);
|
||||
Timer timer = new Timer(PlayModes.Replay, 1000l);
|
||||
AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
|
||||
RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, this, singletonList, 5000l /* requestInterval*/);
|
||||
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, getUserService(), asyncActionsExecutor,
|
||||
raceboardData.getCompetitorAndTheirBoats(), timer, raceSelectionModel, leaderboardName, leaderboardGroupName, eventId,
|
||||
raceboardViewConfig, RaceBoardEntryPoint.this, getStringMessages(), userAgent, raceTimesInfoProvider, showMapControls);
|
||||
|
||||
createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig);
|
||||
}
|
||||
|
||||
private FlowPanel createTimePanel(RaceBoardPanel raceBoardPanel) {
|
||||
FlowPanel timeLineInnerBgPanel = new FlowPanel();
|
||||
timeLineInnerBgPanel.addStyleName("timeLineInnerBgPanel");
|
||||
timeLineInnerBgPanel.add(raceBoardPanel.getTimePanel());
|
||||
|
||||
FlowPanel timeLineInnerPanel = new FlowPanel();
|
||||
timeLineInnerPanel.add(timeLineInnerBgPanel);
|
||||
timeLineInnerPanel.addStyleName("timeLineInnerPanel");
|
||||
|
||||
FlowPanel timelinePanel = new FlowPanel();
|
||||
timelinePanel.add(timeLineInnerPanel);
|
||||
timelinePanel.addStyleName("timeLinePanel");
|
||||
|
||||
return timelinePanel;
|
||||
}
|
||||
|
||||
private void createRaceBoardInOneScreenMode(final RaceBoardPanel raceBoardPanel,
|
||||
RaceBoardViewConfiguration raceboardViewConfiguration) {
|
||||
final DockLayoutPanel p = new DockLayoutPanel(Unit.PX);
|
||||
RootLayoutPanel.get().add(p);
|
||||
final FlowPanel timePanel = createTimePanel(raceBoardPanel);
|
||||
final Button toggleButton = raceBoardPanel.getTimePanel().getAdvancedToggleButton();
|
||||
toggleButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
boolean advancedModeShown = raceBoardPanel.getTimePanel().toggleAdvancedMode();
|
||||
if (advancedModeShown) {
|
||||
p.setWidgetSize(timePanel, 96);
|
||||
toggleButton.removeStyleDependentName("Closed");
|
||||
toggleButton.addStyleDependentName("Open");
|
||||
} else {
|
||||
p.setWidgetSize(timePanel, 67);
|
||||
toggleButton.addStyleDependentName("Closed");
|
||||
toggleButton.removeStyleDependentName("Open");
|
||||
}
|
||||
}
|
||||
});
|
||||
p.addSouth(timePanel, 67);
|
||||
p.add(raceBoardPanel);
|
||||
p.addStyleName("dockLayoutPanel");
|
||||
}
|
||||
}
|
||||
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Document;
|
||||
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.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.rpc.ServiceDefTarget;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.DockLayoutPanel;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.RootLayoutPanel;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint;
|
||||
import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel;
|
||||
import com.sap.sailing.gwt.ui.client.MediaService;
|
||||
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
|
||||
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceWithCompetitorsDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO;
|
||||
import com.sap.sse.gwt.client.EntryPointHelper;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
||||
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
|
||||
|
||||
public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
|
||||
private RaceWithCompetitorsDTO selectedRace;
|
||||
|
||||
private static final String PARAM_REGATTA_NAME = "regattaName";
|
||||
private static final String PARAM_RACE_NAME = "raceName";
|
||||
private static final String PARAM_LEADERBOARD_NAME = "leaderboardName";
|
||||
private static final String PARAM_LEADERBOARD_GROUP_NAME = "leaderboardGroupName";
|
||||
private static final String PARAM_EVENT_ID = "eventId";
|
||||
|
||||
private String regattaName;
|
||||
private String raceName;
|
||||
private String leaderboardName;
|
||||
private String leaderboardGroupName;
|
||||
private UUID eventId;
|
||||
private RaceBoardViewConfiguration raceboardViewConfig;
|
||||
|
||||
private final MediaServiceAsync mediaService = GWT.create(MediaService.class);
|
||||
|
||||
@Override
|
||||
protected void doOnModuleLoad() {
|
||||
super.doOnModuleLoad();
|
||||
EntryPointHelper.registerASyncService((ServiceDefTarget) mediaService, RemoteServiceMappingConstants.mediaServiceRemotePath);
|
||||
// read mandatory parameters
|
||||
regattaName = Window.Location.getParameter(PARAM_REGATTA_NAME);
|
||||
raceName = Window.Location.getParameter(PARAM_RACE_NAME);
|
||||
String leaderboardNameParamValue = Window.Location.getParameter(PARAM_LEADERBOARD_NAME);
|
||||
String leaderboardGroupNameParamValue = Window.Location.getParameter(PARAM_LEADERBOARD_GROUP_NAME);
|
||||
if (leaderboardNameParamValue != null && !leaderboardNameParamValue.isEmpty()) {
|
||||
leaderboardName = leaderboardNameParamValue;
|
||||
}
|
||||
if (leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) {
|
||||
leaderboardGroupName = leaderboardGroupNameParamValue;
|
||||
}
|
||||
String eventIdParamValue = Window.Location.getParameter(PARAM_EVENT_ID);
|
||||
if (eventIdParamValue != null && !eventIdParamValue.isEmpty()) {
|
||||
eventId = UUID.fromString(eventIdParamValue);
|
||||
}
|
||||
if (leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) {
|
||||
leaderboardGroupName = leaderboardGroupNameParamValue;
|
||||
}
|
||||
if (regattaName == null || regattaName.isEmpty() || raceName == null || raceName.isEmpty() ||
|
||||
leaderboardName == null || leaderboardName.isEmpty()) {
|
||||
createErrorPage("This page requires a valid regatta name, race name and leaderboard name.");
|
||||
return;
|
||||
}
|
||||
|
||||
// read optional parameters
|
||||
boolean showLeaderboard = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_LEADERBOARD, true /* default*/);
|
||||
boolean showWindChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_WINDCHART, false /* default*/);
|
||||
boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default*/);
|
||||
boolean showViewStreamlets = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_STREAMLETS, false /* default*/);
|
||||
boolean showViewStreamletColors = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_STREAMLET_COLORS, false /* default*/);
|
||||
boolean showViewSimulation = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_SIMULATION, true /* default*/);
|
||||
String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/);
|
||||
final boolean canReplayWhileLiveIsPossible = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, false);
|
||||
final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, true);
|
||||
final String defaultMedia = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_DEFAULT_MEDIA, null /* default*/);
|
||||
final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, true /* default*/);
|
||||
raceboardViewConfig = new RaceBoardViewConfiguration(activeCompetitorsFilterSetName, showLeaderboard,
|
||||
showWindChart, showCompetitorsChart, showViewStreamlets, showViewStreamletColors, showViewSimulation, canReplayWhileLiveIsPossible, autoSelectMedia, defaultMedia);
|
||||
|
||||
sailingService.getRaceboardData(regattaName, raceName, leaderboardName, leaderboardGroupName, eventId, new AsyncCallback<RaceboardDataDTO>() {
|
||||
@Override
|
||||
public void onSuccess(RaceboardDataDTO result) {
|
||||
// Determine if the screen is large enough to display charts such as the competitor chart or the wind chart.
|
||||
// This decision is made once based on the initial screen height. Resizing the window afterwards will have
|
||||
// no impact on the chart support, i.e. they are available/unavailable based on the initial decision.
|
||||
boolean isScreenLargeEnoughToOfferChartSupport = Document.get().getClientHeight() >= 600;
|
||||
checkUrlParameters(result, canReplayWhileLiveIsPossible, showMapControls,
|
||||
isScreenLargeEnoughToOfferChartSupport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
reportError("Error trying to create the raceboard: " + caught.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createErrorPage(String message) {
|
||||
final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX);
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService());
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
RootLayoutPanel.get().add(vp);
|
||||
vp.addNorth(logoAndTitlePanel, 100);
|
||||
vp.add(new Label(message));
|
||||
}
|
||||
|
||||
private void checkUrlParameters(RaceboardDataDTO raceboardData, boolean canReplayWhileLiveIsPossible, boolean showMapControls,
|
||||
boolean isScreenLargeEnoughToOfferChartSupport) {
|
||||
if (!raceboardData.isValidLeaderboard()) {
|
||||
createErrorPage(getStringMessages().noSuchLeaderboard());
|
||||
return;
|
||||
}
|
||||
if (eventId != null && !raceboardData.isValidEvent()) {
|
||||
createErrorPage(getStringMessages().noSuchEvent());
|
||||
}
|
||||
if (leaderboardGroupName != null) {
|
||||
if(!raceboardData.isValidLeaderboardGroup()) {
|
||||
createErrorPage(getStringMessages().leaderboardNotContainedInLeaderboardGroup(leaderboardName, leaderboardGroupName));
|
||||
return;
|
||||
}
|
||||
if (eventId != null && raceboardData.isValidLeaderboardGroup() && !raceboardData.isValidEvent()) {
|
||||
createErrorPage(getStringMessages().leaderboardGroupNotContainedInEvent(leaderboardGroupName, eventId.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (raceboardData.getRace() == null) {
|
||||
createErrorPage("Could not obtain a race with name " + raceName + " for a regatta with name " + regattaName);
|
||||
return;
|
||||
}
|
||||
selectedRace = raceboardData.getRace();
|
||||
Window.setTitle(selectedRace.getName());
|
||||
RaceSelectionModel raceSelectionModel = new RaceSelectionModel();
|
||||
List<RegattaAndRaceIdentifier> singletonList = Collections.singletonList(selectedRace.getRaceIdentifier());
|
||||
raceSelectionModel.setSelection(singletonList);
|
||||
Timer timer = new Timer(PlayModes.Replay, 1000l);
|
||||
AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
|
||||
RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, this, singletonList, 5000l /* requestInterval*/);
|
||||
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, getUserService(), asyncActionsExecutor,
|
||||
raceboardData.getCompetitorAndTheirBoats(), timer, raceSelectionModel, leaderboardName, leaderboardGroupName, eventId,
|
||||
raceboardViewConfig, RaceBoardEntryPoint.this, getStringMessages(), userAgent, raceTimesInfoProvider, showMapControls,
|
||||
isScreenLargeEnoughToOfferChartSupport);
|
||||
|
||||
createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig);
|
||||
}
|
||||
|
||||
private FlowPanel createTimePanel(RaceBoardPanel raceBoardPanel) {
|
||||
FlowPanel timeLineInnerBgPanel = new ResizableFlowPanel();
|
||||
timeLineInnerBgPanel.addStyleName("timeLineInnerBgPanel");
|
||||
timeLineInnerBgPanel.add(raceBoardPanel.getTimePanel());
|
||||
|
||||
FlowPanel timeLineInnerPanel = new ResizableFlowPanel();
|
||||
timeLineInnerPanel.add(timeLineInnerBgPanel);
|
||||
timeLineInnerPanel.addStyleName("timeLineInnerPanel");
|
||||
|
||||
FlowPanel timelinePanel = new ResizableFlowPanel();
|
||||
timelinePanel.add(timeLineInnerPanel);
|
||||
timelinePanel.addStyleName("timeLinePanel");
|
||||
|
||||
return timelinePanel;
|
||||
}
|
||||
|
||||
private void createRaceBoardInOneScreenMode(final RaceBoardPanel raceBoardPanel,
|
||||
RaceBoardViewConfiguration raceboardViewConfiguration) {
|
||||
final DockLayoutPanel p = new DockLayoutPanel(Unit.PX);
|
||||
RootLayoutPanel.get().add(p);
|
||||
final FlowPanel timePanel = createTimePanel(raceBoardPanel);
|
||||
final Button toggleButton = raceBoardPanel.getTimePanel().getAdvancedToggleButton();
|
||||
toggleButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
boolean advancedModeShown = raceBoardPanel.getTimePanel().toggleAdvancedMode();
|
||||
if (advancedModeShown) {
|
||||
p.setWidgetSize(timePanel, 96);
|
||||
toggleButton.removeStyleDependentName("Closed");
|
||||
toggleButton.addStyleDependentName("Open");
|
||||
} else {
|
||||
p.setWidgetSize(timePanel, 67);
|
||||
toggleButton.addStyleDependentName("Closed");
|
||||
toggleButton.removeStyleDependentName("Open");
|
||||
}
|
||||
}
|
||||
});
|
||||
p.addSouth(timePanel, 67);
|
||||
p.add(raceBoardPanel);
|
||||
p.addStyleName("dockLayoutPanel");
|
||||
}
|
||||
}
|
||||
+483
-437
@@ -1,437 +1,483 @@
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.dom.client.Document;
|
||||
import com.google.gwt.dom.client.Style.Unit;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.MenuBar;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.UIObject;
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.BoatDTO;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceDTO;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorColorProvider;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorColorProviderImpl;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
|
||||
import com.sap.sailing.gwt.ui.client.GlobalNavigationPanel;
|
||||
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener;
|
||||
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent;
|
||||
import com.sap.sailing.gwt.ui.client.media.PopupPositionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.EditMarkPassingsPanel;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorRaceChart;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.WindChart;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.WindChartSettings;
|
||||
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.client.shared.racemap.RaceMap;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapResources;
|
||||
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.LeaderboardSettings;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsFactory;
|
||||
import com.sap.sse.common.filter.FilterSet;
|
||||
import com.sap.sse.common.settings.AbstractSettings;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.URLEncoder;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
import com.sap.sse.gwt.client.player.TimeRangeWithZoomModel;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
import com.sap.sse.gwt.client.shared.components.Component;
|
||||
import com.sap.sse.gwt.client.shared.components.ComponentViewer;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
|
||||
import com.sap.sse.gwt.client.useragent.UserAgentDetails;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
/**
|
||||
* A view showing a list of components visualizing a race from the regattas announced by calls to {@link #fillRegattas(List)}.
|
||||
* The race selection is provided by a {@link RaceSelectionProvider} for which this is a {@link RaceSelectionChangeListener listener}.
|
||||
* {@link RaceIdentifier}-based race selection changes are converted to {@link RaceDTO} objects using the {@link #racesByIdentifier}
|
||||
* map maintained during {@link #fillRegattas(List)}. The race selection provider is expected to be single selection only.
|
||||
*
|
||||
* @author Frank Mittag, Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class RaceBoardPanel extends SimplePanel implements RaceSelectionChangeListener, LeaderboardUpdateListener, PopupPositionProvider {
|
||||
private final SailingServiceAsync sailingService;
|
||||
private final MediaServiceAsync mediaService;
|
||||
private final UUID eventId;
|
||||
private final StringMessages stringMessages;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final RaceBoardViewConfiguration raceboardViewConfiguration;
|
||||
private String raceBoardName;
|
||||
|
||||
private final List<ComponentViewer> componentViewers;
|
||||
private RaceTimePanel timePanel;
|
||||
private final Timer timer;
|
||||
private final RaceSelectionProvider raceSelectionProvider;
|
||||
private final UserAgentDetails userAgent;
|
||||
private final CompetitorSelectionProvider competitorSelectionProvider;
|
||||
private final TimeRangeWithZoomModel timeRangeWithZoomModel;
|
||||
private final RegattaAndRaceIdentifier selectedRaceIdentifier;
|
||||
|
||||
private final String leaderboardName;
|
||||
private final LeaderboardPanel leaderboardPanel;
|
||||
private WindChart windChart;
|
||||
private MultiCompetitorRaceChart competitorChart;
|
||||
private EditMarkPassingsPanel editMarkPassingPanel;
|
||||
|
||||
/**
|
||||
* The component viewer in <code>ONESCREEN</code> view mode. <code>null</code> if in <code>CASCADE</code> view mode
|
||||
*/
|
||||
private SideBySideComponentViewer leaderboardAndMapViewer;
|
||||
|
||||
private final AsyncActionsExecutor asyncActionsExecutor;
|
||||
|
||||
private final RaceTimesInfoProvider raceTimesInfoProvider;
|
||||
private RaceMap raceMap;
|
||||
|
||||
private final FlowPanel raceInformationHeader;
|
||||
private final FlowPanel regattaAndRaceTimeInformationHeader;
|
||||
private boolean currentRaceHasBeenSelectedOnce;
|
||||
|
||||
private static final RaceMapResources raceMapResources = GWT.create(RaceMapResources.class);
|
||||
|
||||
/**
|
||||
* @param eventId
|
||||
* an optional event that can be used for "back"-navigation in case the race board shows a race in the
|
||||
* context of an event; may be <code>null</code>.
|
||||
*/
|
||||
public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService,
|
||||
UserService userService, AsyncActionsExecutor asyncActionsExecutor, Map<CompetitorDTO, BoatDTO> competitorsAndTheirBoats,
|
||||
Timer timer, RaceSelectionProvider theRaceSelectionProvider, String leaderboardName,
|
||||
String leaderboardGroupName, UUID eventId, RaceBoardViewConfiguration raceboardViewConfiguration,
|
||||
ErrorReporter errorReporter, final StringMessages stringMessages,
|
||||
UserAgentDetails userAgent, RaceTimesInfoProvider raceTimesInfoProvider, boolean showMapControls) {
|
||||
this.sailingService = sailingService;
|
||||
this.mediaService = mediaService;
|
||||
this.stringMessages = stringMessages;
|
||||
this.raceboardViewConfiguration = raceboardViewConfiguration;
|
||||
this.raceSelectionProvider = theRaceSelectionProvider;
|
||||
this.raceTimesInfoProvider = raceTimesInfoProvider;
|
||||
this.errorReporter = errorReporter;
|
||||
this.userAgent = userAgent;
|
||||
this.timer = timer;
|
||||
this.eventId = eventId;
|
||||
this.currentRaceHasBeenSelectedOnce = false;
|
||||
this.leaderboardName = leaderboardName;
|
||||
raceSelectionProvider.addRaceSelectionChangeListener(this);
|
||||
selectedRaceIdentifier = raceSelectionProvider.getSelectedRaces().iterator().next();
|
||||
this.setRaceBoardName(selectedRaceIdentifier.getRaceName());
|
||||
this.asyncActionsExecutor = asyncActionsExecutor;
|
||||
FlowPanel mainPanel = new FlowPanel();
|
||||
mainPanel.setSize("100%", "100%");
|
||||
setWidget(mainPanel);
|
||||
raceInformationHeader = new FlowPanel();
|
||||
raceInformationHeader.setStyleName("RegattaRaceInformation-Header");
|
||||
regattaAndRaceTimeInformationHeader = new FlowPanel();
|
||||
regattaAndRaceTimeInformationHeader.setStyleName("RegattaAndRaceTime-Header");
|
||||
timeRangeWithZoomModel = new TimeRangeWithZoomModel();
|
||||
componentViewers = new ArrayList<ComponentViewer>();
|
||||
final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndTheirBoats);
|
||||
competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider);
|
||||
|
||||
raceMapResources.combinedWindPanelStyle().ensureInjected();
|
||||
raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer,
|
||||
competitorSelectionProvider, stringMessages, showMapControls, getConfiguration().isShowViewStreamlets(), getConfiguration().isShowViewStreamletColors(), getConfiguration().isShowViewSimulation(),
|
||||
selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle(), /* showHeaderPanel */ true);
|
||||
CompetitorFilterPanel competitorSearchTextBox = new CompetitorFilterPanel(competitorSelectionProvider, stringMessages, raceMap,
|
||||
new LeaderboardFetcher() {
|
||||
@Override
|
||||
public LeaderboardDTO getLeaderboard() {
|
||||
return leaderboardPanel.getLeaderboard();
|
||||
}
|
||||
}, selectedRaceIdentifier);
|
||||
raceMap.getLeftHeaderPanel().add(raceInformationHeader);
|
||||
raceMap.getRightHeaderPanel().add(regattaAndRaceTimeInformationHeader);
|
||||
|
||||
leaderboardPanel = createLeaderboardPanel(leaderboardName, leaderboardGroupName, competitorSearchTextBox);
|
||||
leaderboardPanel.setTitle(stringMessages.leaderboard());
|
||||
leaderboardPanel.getElement().getStyle().setMarginLeft(6, Unit.PX);
|
||||
leaderboardPanel.getElement().getStyle().setMarginTop(10, Unit.PX);
|
||||
createOneScreenView(leaderboardName, leaderboardGroupName, eventId, mainPanel, showMapControls, raceMap, userService); // initializes the raceMap field
|
||||
leaderboardPanel.addLeaderboardUpdateListener(this);
|
||||
// in case the URL configuration contains the name of a competitors filter set we try to activate it
|
||||
// FIXME the competitorsFilterSets has now moved to CompetitorSearchTextBox (which should probably be renamed); pass on the parameters to the LeaderboardPanel and see what it does with it
|
||||
if (raceboardViewConfiguration.getActiveCompetitorsFilterSetName() != null) {
|
||||
for (FilterSet<CompetitorDTO, FilterWithUI<CompetitorDTO>> filterSet : competitorSearchTextBox.getCompetitorsFilterSets()
|
||||
.getFilterSets()) {
|
||||
if (filterSet.getName().equals(raceboardViewConfiguration.getActiveCompetitorsFilterSetName())) {
|
||||
competitorSearchTextBox.getCompetitorsFilterSets().setActiveFilterSet(filterSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
timePanel = new RaceTimePanel(timer, timeRangeWithZoomModel, stringMessages, raceTimesInfoProvider,
|
||||
raceboardViewConfiguration.isCanReplayDuringLiveRaces());
|
||||
timeRangeWithZoomModel.addTimeZoomChangeListener(timePanel);
|
||||
raceTimesInfoProvider.addRaceTimesInfoProviderListener(timePanel);
|
||||
raceSelectionProvider.addRaceSelectionChangeListener(timePanel);
|
||||
timePanel.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param event an optional event; may be <code>null</code> or else can be used to show some context information in
|
||||
* the {@link GlobalNavigationPanel}.
|
||||
*/
|
||||
private void createOneScreenView(String leaderboardName, String leaderboardGroupName, UUID event, FlowPanel mainPanel,
|
||||
boolean showMapControls, RaceMap raceMap, UserService userService) {
|
||||
// create the default leaderboard and select the right race
|
||||
raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceMap);
|
||||
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
|
||||
List<Component<?>> components = new ArrayList<Component<?>>();
|
||||
competitorChart = new MultiCompetitorRaceChart(sailingService, asyncActionsExecutor, competitorSelectionProvider, raceSelectionProvider,
|
||||
timer, timeRangeWithZoomModel, stringMessages, errorReporter, true, true, leaderboardGroupName, leaderboardName);
|
||||
competitorChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||
competitorChart.getEntryWidget().setTitle(stringMessages.competitorCharts());
|
||||
competitorChart.setVisible(false);
|
||||
components.add(competitorChart);
|
||||
windChart = new WindChart(sailingService, raceSelectionProvider, timer, timeRangeWithZoomModel, new WindChartSettings(),
|
||||
stringMessages, asyncActionsExecutor, errorReporter, /* compactChart */ true);
|
||||
windChart.setVisible(false);
|
||||
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||
windChart.getEntryWidget().setTitle(stringMessages.windChart());
|
||||
components.add(windChart);
|
||||
editMarkPassingPanel = new EditMarkPassingsPanel(sailingService, selectedRaceIdentifier,
|
||||
stringMessages, competitorSelectionProvider, errorReporter, timer);
|
||||
editMarkPassingPanel.setLeaderboard(leaderboardPanel.getLeaderboard());
|
||||
editMarkPassingPanel.getEntryWidget().setTitle(stringMessages.editMarkPassings());
|
||||
components.add(editMarkPassingPanel);
|
||||
boolean autoSelectMedia = getConfiguration().isAutoSelectMedia();
|
||||
MediaPlayerManagerComponent mediaPlayerManagerComponent = new MediaPlayerManagerComponent(
|
||||
selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, userService, stringMessages,
|
||||
errorReporter, userAgent, this, autoSelectMedia);
|
||||
leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, mediaPlayerManagerComponent, components, stringMessages, userService, editMarkPassingPanel);
|
||||
componentViewers.add(leaderboardAndMapViewer);
|
||||
for (ComponentViewer componentViewer : componentViewers) {
|
||||
mainPanel.add(componentViewer.getViewerWidget());
|
||||
}
|
||||
boolean showLeaderboard = getConfiguration().isShowLeaderboard();
|
||||
if (Document.get().getClientWidth() <= 1024) {
|
||||
showLeaderboard = false;
|
||||
}
|
||||
setLeaderboardVisible(showLeaderboard);
|
||||
setWindChartVisible(getConfiguration().isShowWindChart());
|
||||
setCompetitorChartVisible(getConfiguration().isShowCompetitorsChart());
|
||||
// make sure to load leaderboard data for filtering to work
|
||||
if (!showLeaderboard) {
|
||||
leaderboardPanel.setVisible(true);
|
||||
leaderboardPanel.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private <SettingsType extends AbstractSettings> void addSettingsMenuItem(MenuBar settingsMenu, final Component<SettingsType> component) {
|
||||
if (component.hasSettings()) {
|
||||
settingsMenu.addItem(component.getLocalizedShortName(), new Command() {
|
||||
public void execute() {
|
||||
new SettingsDialog<SettingsType>(component, stringMessages).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private LeaderboardPanel createLeaderboardPanel(String leaderboardName, String leaderboardGroupName, CompetitorFilterPanel competitorSearchTextBox) {
|
||||
LeaderboardSettings leaderBoardSettings = LeaderboardSettingsFactory.getInstance()
|
||||
.createNewSettingsForPlayMode(timer.getPlayMode(),
|
||||
/* nameOfRaceToSort */ selectedRaceIdentifier.getRaceName(),
|
||||
/* nameOfRaceColumnToShow */ null, /* nameOfRaceToShow */ selectedRaceIdentifier.getRaceName(),
|
||||
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier), /* showRegattaRank */ false,
|
||||
/*showCompetitorSailIdColumn*/true, /*showCompetitorFullNameColumn*/true);
|
||||
return new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderBoardSettings, selectedRaceIdentifier != null, selectedRaceIdentifier,
|
||||
competitorSelectionProvider, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages,
|
||||
userAgent, /* showRaceDetails */ true, competitorSearchTextBox,
|
||||
/* showSelectionCheckbox */ true, raceTimesInfoProvider, /* autoExpandLastRaceColumn */ false,
|
||||
/* don't adjust the timer's delay from the leaderboard; control it solely from the RaceTimesInfoProvider */ false,
|
||||
/*autoApplyTopNFilter*/ false, /*showCompetitorFilterStatus*/ false);
|
||||
}
|
||||
|
||||
private void setComponentVisible(ComponentViewer componentViewer, Component<?> component, boolean visible) {
|
||||
component.setVisible(visible);
|
||||
componentViewer.forceLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collapsable panel for the leaderboard open or close, if in <code>CASCADE</code> view mode.<br />
|
||||
* Displays or hides the leaderboard, if in <code>ONESCREEN</code> view mode.<br /><br />
|
||||
*
|
||||
* The race board should be completely rendered before this method is called, or a few exceptions could be thrown.
|
||||
*
|
||||
* @param visible <code>true</code> if the leaderboard shall be open/visible
|
||||
*/
|
||||
public void setLeaderboardVisible(boolean visible) {
|
||||
setComponentVisible(leaderboardAndMapViewer, leaderboardPanel, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collapsable panel for the wind chart open or close, if in <code>CASCADE</code> view mode.<br />
|
||||
* Displays or hides the wind chart, if in <code>ONESCREEN</code> view mode.<br /><br />
|
||||
*
|
||||
* The race board should be completely rendered before this method is called, or a few exceptions could be thrown.
|
||||
*
|
||||
* @param visible <code>true</code> if the wind chart shall be open/visible
|
||||
*/
|
||||
public void setWindChartVisible(boolean visible) {
|
||||
setComponentVisible(leaderboardAndMapViewer, windChart, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collapsable panel for the competitor chart open or close, if in <code>CASCADE</code> view mode.<br />
|
||||
* Displays or hides the competitor chart, if in <code>ONESCREEN</code> view mode.<br /><br />
|
||||
*
|
||||
* The race board should be completely rendered before this method is called, or a few exceptions could be thrown.
|
||||
*
|
||||
* @param visible <code>true</code> if the competitor chart shall be open/visible
|
||||
*/
|
||||
public void setCompetitorChartVisible(boolean visible) {
|
||||
setComponentVisible(leaderboardAndMapViewer, competitorChart, visible);
|
||||
}
|
||||
|
||||
public RaceTimePanel getTimePanel() {
|
||||
return timePanel;
|
||||
}
|
||||
|
||||
protected SailingServiceAsync getSailingService() {
|
||||
return sailingService;
|
||||
}
|
||||
|
||||
protected String getRaceBoardName() {
|
||||
return raceBoardName;
|
||||
}
|
||||
|
||||
protected void setRaceBoardName(String raceBoardName) {
|
||||
this.raceBoardName = raceBoardName;
|
||||
}
|
||||
|
||||
protected ErrorReporter getErrorReporter() {
|
||||
return errorReporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRaceSelectionChange(List<RegattaAndRaceIdentifier> selectedRaces) {
|
||||
}
|
||||
|
||||
public RaceBoardViewConfiguration getConfiguration() {
|
||||
return raceboardViewConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
leaderboardAndMapViewer.setLeftComponentWidth(leaderboardPanel.getContentPanel().getOffsetWidth());
|
||||
editMarkPassingPanel.setLeaderboard(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) {
|
||||
if (!currentRaceHasBeenSelectedOnce) {
|
||||
FleetDTO fleet = raceColumn.getFleet(raceIdentifier);
|
||||
String seriesName = raceColumn.getSeriesName();
|
||||
if (LeaderboardNameConstants.DEFAULT_SERIES_NAME.equals(seriesName)) {
|
||||
seriesName = "";
|
||||
}
|
||||
String fleetForRaceName = fleet==null?"":fleet.getName();
|
||||
if (fleetForRaceName.equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) {
|
||||
fleetForRaceName = "";
|
||||
} else {
|
||||
fleetForRaceName = " - "+fleetForRaceName;
|
||||
}
|
||||
final Label raceNameLabel = new Label(stringMessages.race() + " " + raceColumn.getRaceColumnName());
|
||||
raceNameLabel.setStyleName("RaceName-Label");
|
||||
final Label raceAdditionalInformationLabel = new Label(seriesName + fleetForRaceName);
|
||||
raceAdditionalInformationLabel.setStyleName("RaceSeriesAndFleet-Label");
|
||||
raceInformationHeader.clear();
|
||||
raceInformationHeader.add(raceNameLabel);
|
||||
raceInformationHeader.add(raceAdditionalInformationLabel);
|
||||
final Anchor regattaNameAnchor = new Anchor(raceIdentifier.getRegattaName());
|
||||
if (eventId != null) {
|
||||
// we don't use the EntryPointLinkFactory here, because of lacking support for Places
|
||||
String debugParam = Window.Location.getParameter("gwt.codesvr");
|
||||
String link = "/gwt/Home.html";
|
||||
if (debugParam != null && !debugParam.isEmpty()) {
|
||||
link += "?gwt.codesvr=" + debugParam;
|
||||
}
|
||||
link += "#EventPlace:eventId="+eventId.toString();
|
||||
link += "&navigationTab=Regatta&leaderboardName=" + URLEncoder.encode(leaderboardName);
|
||||
regattaNameAnchor.setHref(link);
|
||||
} else {
|
||||
String leaderboardGroupNameParam = Window.Location.getParameter("leaderboardGroupName");
|
||||
if(leaderboardGroupNameParam != null) {
|
||||
Map<String, String> leaderboardGroupLinkParameters = new HashMap<String, String>();
|
||||
leaderboardGroupLinkParameters.put("showRaceDetails", "true");
|
||||
leaderboardGroupLinkParameters.put("leaderboardGroupName", leaderboardGroupNameParam);
|
||||
String leaderBoardGroupLink = EntryPointLinkFactory.createLeaderboardGroupLink(leaderboardGroupLinkParameters);
|
||||
regattaNameAnchor.setHref(leaderBoardGroupLink);
|
||||
} else {
|
||||
// fallback
|
||||
regattaNameAnchor.setHref("javascript:window.history.back();");
|
||||
}
|
||||
}
|
||||
regattaNameAnchor.setStyleName("RegattaName-Anchor");
|
||||
|
||||
// TODO: Strange behavior... check
|
||||
// Window.addResizeHandler(new ResizeHandler() {
|
||||
// @Override
|
||||
// public void onResize(ResizeEvent event) {
|
||||
// int headerPanelWidth = raceMap.getRightHeaderPanel().getOffsetWidth() - 150; // 150px is the width of the sapLogo
|
||||
// int raceNameAndFleetLabelWidth = raceInformationHeader.getOffsetWidth();
|
||||
// int regattaAnchorWidth = regattaNameAnchor.getOffsetWidth();
|
||||
// boolean overlap = raceNameAndFleetLabelWidth + regattaAnchorWidth > headerPanelWidth;
|
||||
// raceInformationHeader.setVisible(!overlap);
|
||||
// }
|
||||
// });
|
||||
|
||||
Label raceTimeLabel = computeRaceInformation(raceColumn, fleet);
|
||||
raceTimeLabel.setStyleName("RaceTime-Label");
|
||||
regattaAndRaceTimeInformationHeader.clear();
|
||||
regattaAndRaceTimeInformationHeader.add(regattaNameAnchor);
|
||||
regattaAndRaceTimeInformationHeader.add(raceTimeLabel);
|
||||
currentRaceHasBeenSelectedOnce = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UIObject getXPositionUiObject() {
|
||||
return timePanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UIObject getYPositionUiObject() {
|
||||
return timePanel;
|
||||
}
|
||||
|
||||
private Label computeRaceInformation(RaceColumnDTO raceColumn, FleetDTO fleet) {
|
||||
Label raceInformationLabel = new Label();
|
||||
raceInformationLabel.setStyleName("Race-Time-Label");
|
||||
DateTimeFormat formatter = DateTimeFormat.getFormat("E d/M/y");
|
||||
raceInformationLabel.setText(formatter.format(raceColumn.getStartDate(fleet)));
|
||||
return raceInformationLabel;
|
||||
}
|
||||
}
|
||||
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gwt.core.client.Scheduler;
|
||||
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.dom.client.Document;
|
||||
import com.google.gwt.dom.client.Style.Unit;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.MenuBar;
|
||||
import com.google.gwt.user.client.ui.RequiresResize;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.UIObject;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.BoatDTO;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceDTO;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorColorProvider;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorColorProviderImpl;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
|
||||
import com.sap.sailing.gwt.ui.client.GlobalNavigationPanel;
|
||||
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener;
|
||||
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent;
|
||||
import com.sap.sailing.gwt.ui.client.media.PopupPositionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.EditMarkPassingsPanel;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorRaceChart;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.WindChart;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.WindChartSettings;
|
||||
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.client.shared.racemap.RaceMap;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapResources;
|
||||
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.LeaderboardSettings;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsFactory;
|
||||
import com.sap.sse.common.filter.FilterSet;
|
||||
import com.sap.sse.common.settings.AbstractSettings;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.URLEncoder;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
import com.sap.sse.gwt.client.player.TimeRangeWithZoomModel;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
import com.sap.sse.gwt.client.shared.components.Component;
|
||||
import com.sap.sse.gwt.client.shared.components.ComponentViewer;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
|
||||
import com.sap.sse.gwt.client.useragent.UserAgentDetails;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
|
||||
/**
|
||||
* A view showing a list of components visualizing a race from the regattas announced by calls to {@link #fillRegattas(List)}.
|
||||
* The race selection is provided by a {@link RaceSelectionProvider} for which this is a {@link RaceSelectionChangeListener listener}.
|
||||
* {@link RaceIdentifier}-based race selection changes are converted to {@link RaceDTO} objects using the {@link #racesByIdentifier}
|
||||
* map maintained during {@link #fillRegattas(List)}. The race selection provider is expected to be single selection only.
|
||||
*
|
||||
* @author Frank Mittag, Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class RaceBoardPanel extends SimplePanel implements RaceSelectionChangeListener,
|
||||
LeaderboardUpdateListener, PopupPositionProvider, RequiresResize {
|
||||
private final SailingServiceAsync sailingService;
|
||||
private final MediaServiceAsync mediaService;
|
||||
private final UUID eventId;
|
||||
private final StringMessages stringMessages;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final RaceBoardViewConfiguration raceboardViewConfiguration;
|
||||
private String raceBoardName;
|
||||
|
||||
private final List<ComponentViewer> componentViewers;
|
||||
private RaceTimePanel timePanel;
|
||||
private final Timer timer;
|
||||
private final RaceSelectionProvider raceSelectionProvider;
|
||||
private final UserAgentDetails userAgent;
|
||||
private final CompetitorSelectionProvider competitorSelectionProvider;
|
||||
private final TimeRangeWithZoomModel timeRangeWithZoomModel;
|
||||
private final RegattaAndRaceIdentifier selectedRaceIdentifier;
|
||||
|
||||
private final String leaderboardName;
|
||||
private final LeaderboardPanel leaderboardPanel;
|
||||
private WindChart windChart;
|
||||
private MultiCompetitorRaceChart competitorChart;
|
||||
private EditMarkPassingsPanel editMarkPassingPanel;
|
||||
|
||||
/**
|
||||
* The component viewer in <code>ONESCREEN</code> view mode. <code>null</code> if in <code>CASCADE</code> view mode
|
||||
*/
|
||||
private SideBySideComponentViewer leaderboardAndMapViewer;
|
||||
|
||||
private final AsyncActionsExecutor asyncActionsExecutor;
|
||||
|
||||
private final RaceTimesInfoProvider raceTimesInfoProvider;
|
||||
private RaceMap raceMap;
|
||||
|
||||
private final FlowPanel raceInformationHeader;
|
||||
private final FlowPanel regattaAndRaceTimeInformationHeader;
|
||||
private boolean currentRaceHasBeenSelectedOnce;
|
||||
|
||||
private static final RaceMapResources raceMapResources = GWT.create(RaceMapResources.class);
|
||||
|
||||
/**
|
||||
* @param eventId
|
||||
* an optional event that can be used for "back"-navigation in case the race board shows a race in the
|
||||
* context of an event; may be <code>null</code>.
|
||||
* @param isScreenLargeEnoughToOfferChartSupport
|
||||
* if the screen is large enough to display charts such as the competitor chart or the wind chart, a
|
||||
* padding is provided for the RaceTimePanel that aligns its right border with that of the charts, and
|
||||
* the charts are created. This decision is made once on startup in the {@link RaceBoardEntryPoint} class.
|
||||
*/
|
||||
public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService,
|
||||
UserService userService, AsyncActionsExecutor asyncActionsExecutor, Map<CompetitorDTO, BoatDTO> competitorsAndTheirBoats,
|
||||
Timer timer, RaceSelectionProvider theRaceSelectionProvider, String leaderboardName,
|
||||
String leaderboardGroupName, UUID eventId, RaceBoardViewConfiguration raceboardViewConfiguration,
|
||||
ErrorReporter errorReporter, final StringMessages stringMessages,
|
||||
UserAgentDetails userAgent, RaceTimesInfoProvider raceTimesInfoProvider, boolean showMapControls, boolean isScreenLargeEnoughToOfferChartSupport) {
|
||||
this.sailingService = sailingService;
|
||||
this.mediaService = mediaService;
|
||||
this.stringMessages = stringMessages;
|
||||
this.raceboardViewConfiguration = raceboardViewConfiguration;
|
||||
this.raceSelectionProvider = theRaceSelectionProvider;
|
||||
this.raceTimesInfoProvider = raceTimesInfoProvider;
|
||||
this.errorReporter = errorReporter;
|
||||
this.userAgent = userAgent;
|
||||
this.timer = timer;
|
||||
this.eventId = eventId;
|
||||
this.currentRaceHasBeenSelectedOnce = false;
|
||||
this.leaderboardName = leaderboardName;
|
||||
raceSelectionProvider.addRaceSelectionChangeListener(this);
|
||||
selectedRaceIdentifier = raceSelectionProvider.getSelectedRaces().iterator().next();
|
||||
this.setRaceBoardName(selectedRaceIdentifier.getRaceName());
|
||||
this.asyncActionsExecutor = asyncActionsExecutor;
|
||||
FlowPanel mainPanel = new ResizableFlowPanel();
|
||||
mainPanel.setSize("100%", "100%");
|
||||
setWidget(mainPanel);
|
||||
raceInformationHeader = new FlowPanel();
|
||||
raceInformationHeader.setStyleName("RegattaRaceInformation-Header");
|
||||
regattaAndRaceTimeInformationHeader = new FlowPanel();
|
||||
regattaAndRaceTimeInformationHeader.setStyleName("RegattaAndRaceTime-Header");
|
||||
timeRangeWithZoomModel = new TimeRangeWithZoomModel();
|
||||
componentViewers = new ArrayList<ComponentViewer>();
|
||||
final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndTheirBoats);
|
||||
competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider);
|
||||
|
||||
raceMapResources.combinedWindPanelStyle().ensureInjected();
|
||||
raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer,
|
||||
competitorSelectionProvider, stringMessages, showMapControls, getConfiguration().isShowViewStreamlets(), getConfiguration().isShowViewStreamletColors(), getConfiguration().isShowViewSimulation(),
|
||||
selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle(), /* showHeaderPanel */ true) {
|
||||
private static final String INDENT_SMALL_CONTROL_STYLE = "indentsmall";
|
||||
private static final String INDENT_BIG_CONTROL_STYLE = "indentbig";
|
||||
@Override
|
||||
public void onResize() {
|
||||
super.onResize();
|
||||
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
// Show/hide the leaderboard panels toggle button text based on the race map height
|
||||
leaderboardAndMapViewer.setLeftComponentToggleButtonTextVisibilityAndDraggerPosition(raceMap.getOffsetHeight() > 400);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLeftControlsIndentStyle() {
|
||||
// Calculate style name for left control indent based on race map height an leaderboard panel visibility
|
||||
if (raceMap.getOffsetHeight() <= 300) {
|
||||
return INDENT_BIG_CONTROL_STYLE;
|
||||
}
|
||||
if (leaderboardPanel.isVisible() && raceMap.getOffsetHeight() <= 500) {
|
||||
return INDENT_SMALL_CONTROL_STYLE;
|
||||
}
|
||||
return super.getLeftControlsIndentStyle();
|
||||
}
|
||||
};
|
||||
CompetitorFilterPanel competitorSearchTextBox = new CompetitorFilterPanel(competitorSelectionProvider, stringMessages, raceMap,
|
||||
new LeaderboardFetcher() {
|
||||
@Override
|
||||
public LeaderboardDTO getLeaderboard() {
|
||||
return leaderboardPanel.getLeaderboard();
|
||||
}
|
||||
}, selectedRaceIdentifier);
|
||||
raceMap.getLeftHeaderPanel().add(raceInformationHeader);
|
||||
raceMap.getRightHeaderPanel().add(regattaAndRaceTimeInformationHeader);
|
||||
|
||||
// Determine if the screen is large enough to initially display the leaderboard panel on the left side of the
|
||||
// map based on the initial screen width. Afterwards, the leaderboard panel visibility can be toggled as usual.
|
||||
boolean isScreenLargeEnoughToInitiallyDisplayLeaderboard = Document.get().getClientWidth() >= 1024;
|
||||
leaderboardPanel = createLeaderboardPanel(leaderboardName, leaderboardGroupName, competitorSearchTextBox, isScreenLargeEnoughToInitiallyDisplayLeaderboard);
|
||||
leaderboardPanel.setTitle(stringMessages.leaderboard());
|
||||
leaderboardPanel.getElement().getStyle().setMarginLeft(6, Unit.PX);
|
||||
leaderboardPanel.getElement().getStyle().setMarginTop(10, Unit.PX);
|
||||
createOneScreenView(leaderboardName, leaderboardGroupName, eventId, mainPanel, showMapControls, isScreenLargeEnoughToOfferChartSupport, isScreenLargeEnoughToInitiallyDisplayLeaderboard, raceMap, userService); // initializes the raceMap field
|
||||
leaderboardPanel.addLeaderboardUpdateListener(this);
|
||||
// in case the URL configuration contains the name of a competitors filter set we try to activate it
|
||||
// FIXME the competitorsFilterSets has now moved to CompetitorSearchTextBox (which should probably be renamed); pass on the parameters to the LeaderboardPanel and see what it does with it
|
||||
if (raceboardViewConfiguration.getActiveCompetitorsFilterSetName() != null) {
|
||||
for (FilterSet<CompetitorDTO, FilterWithUI<CompetitorDTO>> filterSet : competitorSearchTextBox.getCompetitorsFilterSets()
|
||||
.getFilterSets()) {
|
||||
if (filterSet.getName().equals(raceboardViewConfiguration.getActiveCompetitorsFilterSetName())) {
|
||||
competitorSearchTextBox.getCompetitorsFilterSets().setActiveFilterSet(filterSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
timePanel = new RaceTimePanel(timer, timeRangeWithZoomModel, stringMessages, raceTimesInfoProvider,
|
||||
raceboardViewConfiguration.isCanReplayDuringLiveRaces(), isScreenLargeEnoughToOfferChartSupport);
|
||||
timeRangeWithZoomModel.addTimeZoomChangeListener(timePanel);
|
||||
raceTimesInfoProvider.addRaceTimesInfoProviderListener(timePanel);
|
||||
raceSelectionProvider.addRaceSelectionChangeListener(timePanel);
|
||||
timePanel.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param event
|
||||
* an optional event; may be <code>null</code> or else can be used to show some context information in
|
||||
* the {@link GlobalNavigationPanel}.
|
||||
* @param isScreenLargeEnoughToOfferChartSupport
|
||||
* if the screen is large enough to display charts such as the competitor chart or the wind chart, a
|
||||
* padding is provided for the RaceTimePanel that aligns its right border with that of the charts, and
|
||||
* the charts are created.
|
||||
* @param isScreenLargeEnoughToInitiallyDisplayLeaderboard TODO
|
||||
*/
|
||||
private void createOneScreenView(String leaderboardName, String leaderboardGroupName, UUID event, FlowPanel mainPanel,
|
||||
boolean showMapControls, boolean isScreenLargeEnoughToOfferChartSupport, boolean isScreenLargeEnoughToInitiallyDisplayLeaderboard, RaceMap raceMap, UserService userService) {
|
||||
// create the default leaderboard and select the right race
|
||||
raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceMap);
|
||||
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
|
||||
List<Component<?>> components = new ArrayList<Component<?>>();
|
||||
if (isScreenLargeEnoughToOfferChartSupport) {
|
||||
competitorChart = new MultiCompetitorRaceChart(sailingService, asyncActionsExecutor, competitorSelectionProvider, raceSelectionProvider,
|
||||
timer, timeRangeWithZoomModel, stringMessages, errorReporter, true, true, leaderboardGroupName, leaderboardName);
|
||||
competitorChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||
competitorChart.getEntryWidget().setTitle(stringMessages.competitorCharts());
|
||||
competitorChart.setVisible(false);
|
||||
components.add(competitorChart);
|
||||
windChart = new WindChart(sailingService, raceSelectionProvider, timer, timeRangeWithZoomModel, new WindChartSettings(),
|
||||
stringMessages, asyncActionsExecutor, errorReporter, /* compactChart */ true);
|
||||
windChart.setVisible(false);
|
||||
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||
windChart.getEntryWidget().setTitle(stringMessages.windChart());
|
||||
components.add(windChart);
|
||||
editMarkPassingPanel = new EditMarkPassingsPanel(sailingService, selectedRaceIdentifier,
|
||||
stringMessages, competitorSelectionProvider, errorReporter, timer);
|
||||
editMarkPassingPanel.setLeaderboard(leaderboardPanel.getLeaderboard());
|
||||
editMarkPassingPanel.getEntryWidget().setTitle(stringMessages.editMarkPassings());
|
||||
components.add(editMarkPassingPanel);
|
||||
}
|
||||
boolean autoSelectMedia = getConfiguration().isAutoSelectMedia();
|
||||
MediaPlayerManagerComponent mediaPlayerManagerComponent = new MediaPlayerManagerComponent(
|
||||
selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, userService, stringMessages,
|
||||
errorReporter, userAgent, this, autoSelectMedia);
|
||||
leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, mediaPlayerManagerComponent, components, stringMessages, userService, editMarkPassingPanel);
|
||||
componentViewers.add(leaderboardAndMapViewer);
|
||||
for (ComponentViewer componentViewer : componentViewers) {
|
||||
mainPanel.add(componentViewer.getViewerWidget());
|
||||
}
|
||||
boolean showLeaderboard = getConfiguration().isShowLeaderboard() && isScreenLargeEnoughToInitiallyDisplayLeaderboard;
|
||||
setLeaderboardVisible(showLeaderboard);
|
||||
if (isScreenLargeEnoughToOfferChartSupport) {
|
||||
setWindChartVisible(getConfiguration().isShowWindChart());
|
||||
setCompetitorChartVisible(getConfiguration().isShowCompetitorsChart());
|
||||
}
|
||||
// make sure to load leaderboard data for filtering to work
|
||||
if (!showLeaderboard) {
|
||||
leaderboardPanel.setVisible(true);
|
||||
leaderboardPanel.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private <SettingsType extends AbstractSettings> void addSettingsMenuItem(MenuBar settingsMenu, final Component<SettingsType> component) {
|
||||
if (component.hasSettings()) {
|
||||
settingsMenu.addItem(component.getLocalizedShortName(), new Command() {
|
||||
public void execute() {
|
||||
new SettingsDialog<SettingsType>(component, stringMessages).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private LeaderboardPanel createLeaderboardPanel(String leaderboardName, String leaderboardGroupName,
|
||||
CompetitorFilterPanel competitorSearchTextBox, boolean isScreenLargeEnoughToInitiallyDisplayLeaderboard) {
|
||||
LeaderboardSettings leaderBoardSettings = LeaderboardSettingsFactory.getInstance()
|
||||
.createNewSettingsForPlayMode(timer.getPlayMode(),
|
||||
/* nameOfRaceToSort */ selectedRaceIdentifier.getRaceName(),
|
||||
/* nameOfRaceColumnToShow */ null, /* nameOfRaceToShow */ selectedRaceIdentifier.getRaceName(),
|
||||
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier), /* showRegattaRank */ false,
|
||||
/*showCompetitorSailIdColumn*/true,
|
||||
/* don't showCompetitorFullNameColumn in case screen is so small that we don't
|
||||
* even display the leaderboard initially */ isScreenLargeEnoughToInitiallyDisplayLeaderboard);
|
||||
return new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderBoardSettings, selectedRaceIdentifier != null, selectedRaceIdentifier,
|
||||
competitorSelectionProvider, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages,
|
||||
userAgent, /* showRaceDetails */ true, competitorSearchTextBox,
|
||||
/* showSelectionCheckbox */ true, raceTimesInfoProvider, /* autoExpandLastRaceColumn */ false,
|
||||
/* don't adjust the timer's delay from the leaderboard; control it solely from the RaceTimesInfoProvider */ false,
|
||||
/*autoApplyTopNFilter*/ false, /*showCompetitorFilterStatus*/ false);
|
||||
}
|
||||
|
||||
private void setComponentVisible(ComponentViewer componentViewer, Component<?> component, boolean visible) {
|
||||
component.setVisible(visible);
|
||||
componentViewer.forceLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collapsable panel for the leaderboard open or close, if in <code>CASCADE</code> view mode.<br />
|
||||
* Displays or hides the leaderboard, if in <code>ONESCREEN</code> view mode.<br /><br />
|
||||
*
|
||||
* The race board should be completely rendered before this method is called, or a few exceptions could be thrown.
|
||||
*
|
||||
* @param visible <code>true</code> if the leaderboard shall be open/visible
|
||||
*/
|
||||
public void setLeaderboardVisible(boolean visible) {
|
||||
setComponentVisible(leaderboardAndMapViewer, leaderboardPanel, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collapsable panel for the wind chart open or close, if in <code>CASCADE</code> view mode.<br />
|
||||
* Displays or hides the wind chart, if in <code>ONESCREEN</code> view mode.<br /><br />
|
||||
*
|
||||
* The race board should be completely rendered before this method is called, or a few exceptions could be thrown.
|
||||
*
|
||||
* @param visible <code>true</code> if the wind chart shall be open/visible
|
||||
*/
|
||||
public void setWindChartVisible(boolean visible) {
|
||||
setComponentVisible(leaderboardAndMapViewer, windChart, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collapsable panel for the competitor chart open or close, if in <code>CASCADE</code> view mode.<br />
|
||||
* Displays or hides the competitor chart, if in <code>ONESCREEN</code> view mode.<br /><br />
|
||||
*
|
||||
* The race board should be completely rendered before this method is called, or a few exceptions could be thrown.
|
||||
*
|
||||
* @param visible <code>true</code> if the competitor chart shall be open/visible
|
||||
*/
|
||||
public void setCompetitorChartVisible(boolean visible) {
|
||||
setComponentVisible(leaderboardAndMapViewer, competitorChart, visible);
|
||||
}
|
||||
|
||||
public RaceTimePanel getTimePanel() {
|
||||
return timePanel;
|
||||
}
|
||||
|
||||
protected SailingServiceAsync getSailingService() {
|
||||
return sailingService;
|
||||
}
|
||||
|
||||
protected String getRaceBoardName() {
|
||||
return raceBoardName;
|
||||
}
|
||||
|
||||
protected void setRaceBoardName(String raceBoardName) {
|
||||
this.raceBoardName = raceBoardName;
|
||||
}
|
||||
|
||||
protected ErrorReporter getErrorReporter() {
|
||||
return errorReporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRaceSelectionChange(List<RegattaAndRaceIdentifier> selectedRaces) {
|
||||
}
|
||||
|
||||
public RaceBoardViewConfiguration getConfiguration() {
|
||||
return raceboardViewConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
leaderboardAndMapViewer.setLeftComponentWidth(leaderboardPanel.getContentPanel().getOffsetWidth());
|
||||
if (editMarkPassingPanel != null) {
|
||||
editMarkPassingPanel.setLeaderboard(leaderboard);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) {
|
||||
if (!currentRaceHasBeenSelectedOnce) {
|
||||
FleetDTO fleet = raceColumn.getFleet(raceIdentifier);
|
||||
String seriesName = raceColumn.getSeriesName();
|
||||
if (LeaderboardNameConstants.DEFAULT_SERIES_NAME.equals(seriesName)) {
|
||||
seriesName = "";
|
||||
}
|
||||
String fleetForRaceName = fleet==null?"":fleet.getName();
|
||||
if (fleetForRaceName.equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) {
|
||||
fleetForRaceName = "";
|
||||
} else {
|
||||
fleetForRaceName = (seriesName.isEmpty() ? "" : " - ") + fleetForRaceName;
|
||||
}
|
||||
final Label raceNameLabel = new Label(stringMessages.race() + " " + raceColumn.getRaceColumnName());
|
||||
raceNameLabel.setStyleName("RaceName-Label");
|
||||
final Label raceAdditionalInformationLabel = new Label(seriesName + fleetForRaceName);
|
||||
raceAdditionalInformationLabel.setStyleName("RaceSeriesAndFleet-Label");
|
||||
raceInformationHeader.clear();
|
||||
raceInformationHeader.add(raceNameLabel);
|
||||
raceInformationHeader.add(raceAdditionalInformationLabel);
|
||||
final Anchor regattaNameAnchor = new Anchor(raceIdentifier.getRegattaName());
|
||||
regattaNameAnchor.setTitle(raceIdentifier.getRegattaName());
|
||||
if (eventId != null) {
|
||||
// we don't use the EntryPointLinkFactory here, because of lacking support for Places
|
||||
String debugParam = Window.Location.getParameter("gwt.codesvr");
|
||||
String link = "/gwt/Home.html";
|
||||
if (debugParam != null && !debugParam.isEmpty()) {
|
||||
link += "?gwt.codesvr=" + debugParam;
|
||||
}
|
||||
link += "#EventPlace:eventId="+eventId.toString();
|
||||
link += "&navigationTab=Regatta&leaderboardName=" + URLEncoder.encode(leaderboardName);
|
||||
regattaNameAnchor.setHref(link);
|
||||
} else {
|
||||
String leaderboardGroupNameParam = Window.Location.getParameter("leaderboardGroupName");
|
||||
if(leaderboardGroupNameParam != null) {
|
||||
Map<String, String> leaderboardGroupLinkParameters = new HashMap<String, String>();
|
||||
leaderboardGroupLinkParameters.put("showRaceDetails", "true");
|
||||
leaderboardGroupLinkParameters.put("leaderboardGroupName", leaderboardGroupNameParam);
|
||||
String leaderBoardGroupLink = EntryPointLinkFactory.createLeaderboardGroupLink(leaderboardGroupLinkParameters);
|
||||
regattaNameAnchor.setHref(leaderBoardGroupLink);
|
||||
} else {
|
||||
// fallback
|
||||
regattaNameAnchor.setHref("javascript:window.history.back();");
|
||||
}
|
||||
}
|
||||
regattaNameAnchor.setStyleName("RegattaName-Anchor");
|
||||
Label raceTimeLabel = computeRaceInformation(raceColumn, fleet);
|
||||
raceTimeLabel.setStyleName("RaceTime-Label");
|
||||
regattaAndRaceTimeInformationHeader.clear();
|
||||
regattaAndRaceTimeInformationHeader.add(regattaNameAnchor);
|
||||
regattaAndRaceTimeInformationHeader.add(raceTimeLabel);
|
||||
currentRaceHasBeenSelectedOnce = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UIObject getXPositionUiObject() {
|
||||
return timePanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UIObject getYPositionUiObject() {
|
||||
return timePanel;
|
||||
}
|
||||
|
||||
private Label computeRaceInformation(RaceColumnDTO raceColumn, FleetDTO fleet) {
|
||||
Label raceInformationLabel = new Label();
|
||||
raceInformationLabel.setStyleName("Race-Time-Label");
|
||||
DateTimeFormat formatter = DateTimeFormat.getFormat("E d/M/y");
|
||||
raceInformationLabel.setText(formatter.format(raceColumn.getStartDate(fleet)));
|
||||
return raceInformationLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize() {
|
||||
Widget child = getWidget();
|
||||
if (child != null && child instanceof RequiresResize) {
|
||||
((RequiresResize) child).onResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.RequiresResize;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.WidgetCollection;
|
||||
|
||||
/**
|
||||
* Utility class extending {@link FlowPanel} and implementing {@link RequiresResize} interface. All
|
||||
* {@link #getChildren() children} also implementing {@link RequiresResize} are informed by calling their
|
||||
* {@link RequiresResize#onResize()} method.
|
||||
*/
|
||||
class ResizableFlowPanel extends FlowPanel implements RequiresResize {
|
||||
@Override
|
||||
public void onResize() {
|
||||
WidgetCollection children = getChildren();
|
||||
for (Widget widget : children) {
|
||||
if (widget instanceof RequiresResize) {
|
||||
((RequiresResize) widget).onResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+334
-305
@@ -1,305 +1,334 @@
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.user.client.ui.AbsolutePanel;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
|
||||
import com.google.gwt.user.client.ui.LayoutPanel;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.RequiresResize;
|
||||
import com.google.gwt.user.client.ui.ScrollPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.WidgetCollection;
|
||||
import com.sap.sailing.domain.common.security.Roles;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaManagementControl;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManager;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManager.PlayerChangeListener;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaSingleSelectionControl;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.EditMarkPassingsPanel;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.settings.AbstractSettings;
|
||||
import com.sap.sse.gwt.client.shared.components.Component;
|
||||
import com.sap.sse.gwt.client.shared.components.ComponentViewer;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
|
||||
import com.sap.sse.security.shared.DefaultRoles;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
import com.sap.sse.security.ui.client.UserStatusEventHandler;
|
||||
import com.sap.sse.security.ui.shared.UserDTO;
|
||||
|
||||
/**
|
||||
* Component Viewer that uses a {@link TouchSplitLayoutPanel} to display its components.
|
||||
*
|
||||
* TODO: Refactor to make sure it is really only performing operations that are related to view. Currently it is digging
|
||||
* too deep into components and setting titles or even creating video buttons.
|
||||
*/
|
||||
public class SideBySideComponentViewer implements ComponentViewer, UserStatusEventHandler {
|
||||
|
||||
private static final int DEFAULT_SOUTH_SPLIT_PANEL_HEIGHT = 200;
|
||||
private final int MIN_LEADERBOARD_WIDTH = 432; // works well for 505 and ESS
|
||||
|
||||
/**
|
||||
* Absolute Panel that informs its children about a resize
|
||||
*/
|
||||
class ResizableAbsolutePanel extends AbsolutePanel implements RequiresResize {
|
||||
public void onResize() {
|
||||
WidgetCollection children = getChildren();
|
||||
for (Widget widget : children) {
|
||||
if (widget instanceof RequiresResize) {
|
||||
((RequiresResize) widget).onResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final LeaderboardPanel leftComponent;
|
||||
private final Component<?> rightComponent;
|
||||
private final List<Component<?>> components;
|
||||
private final ScrollPanel leftScrollPanel;
|
||||
private final StringMessages stringMessages;
|
||||
private final Button mediaSelectionButton;
|
||||
private final Button mediaManagementButton;
|
||||
private final EditMarkPassingsPanel markPassingsPanel;
|
||||
|
||||
private LayoutPanel mainPanel;
|
||||
|
||||
private TouchSplitLayoutPanel splitLayoutPanel;
|
||||
private int savedSplitPosition = -1;
|
||||
private boolean layoutForLeftComponentForcedOnce = false;
|
||||
|
||||
public SideBySideComponentViewer(final LeaderboardPanel leftComponentP, final Component<?> rightComponentP,
|
||||
final MediaPlayerManagerComponent mediaPlayerManagerComponent, List<Component<?>> components,
|
||||
final StringMessages stringMessages, UserService userService, EditMarkPassingsPanel markPassingsPanel) {
|
||||
this.stringMessages = stringMessages;
|
||||
this.leftComponent = leftComponentP;
|
||||
this.rightComponent = rightComponentP;
|
||||
this.components = components;
|
||||
this.mediaSelectionButton = createMediaSelectionButton(mediaPlayerManagerComponent);
|
||||
this.mediaManagementButton = createMediaManagementButton(mediaPlayerManagerComponent);
|
||||
this.markPassingsPanel = markPassingsPanel;
|
||||
|
||||
userService.addUserStatusEventHandler(this);
|
||||
|
||||
mediaPlayerManagerComponent.setPlayerChangeListener(new PlayerChangeListener() {
|
||||
|
||||
public void notifyStateChange(){
|
||||
String caption;
|
||||
String tooltip;
|
||||
switch (mediaPlayerManagerComponent.getAssignedMediaTracks().size()) {
|
||||
case 0:
|
||||
caption = stringMessages.mediaNoVideosCaption();
|
||||
tooltip = caption;
|
||||
mediaSelectionButton.setVisible(false);
|
||||
break;
|
||||
case 1:
|
||||
mediaSelectionButton.setVisible(true);
|
||||
if(mediaPlayerManagerComponent.isPlaying()){
|
||||
caption = stringMessages.mediaHideVideoCaption();
|
||||
tooltip = stringMessages.mediaHideVideoTooltip();
|
||||
}
|
||||
else{
|
||||
caption = stringMessages.mediaShowVideoCaption();
|
||||
tooltip = stringMessages.mediaShowVideoTooltip(mediaPlayerManagerComponent.getAssignedMediaTracks().iterator().next().title);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mediaSelectionButton.setVisible(true);
|
||||
caption = stringMessages.mediaSelectVideoCaption(mediaPlayerManagerComponent.getAssignedMediaTracks().size());
|
||||
tooltip = stringMessages.mediaSelectVideoTooltip();
|
||||
break;
|
||||
}
|
||||
if (mediaPlayerManagerComponent.isPlaying()) {
|
||||
mediaSelectionButton.addStyleDependentName("mediaplaying");
|
||||
} else {
|
||||
mediaSelectionButton.removeStyleDependentName("mediaplaying");
|
||||
}
|
||||
mediaSelectionButton.setText(caption);
|
||||
mediaSelectionButton.setTitle(tooltip);
|
||||
mediaManagementButton.setVisible(mediaPlayerManagerComponent.allowsEditing());
|
||||
}
|
||||
|
||||
});
|
||||
this.leftScrollPanel = new ScrollPanel();
|
||||
this.leftScrollPanel.add(leftComponentP.getEntryWidget());
|
||||
this.leftScrollPanel.setTitle(leftComponentP.getEntryWidget().getTitle());
|
||||
this.mainPanel = new LayoutPanel();
|
||||
this.mainPanel.setSize("100%", "100%");
|
||||
this.mainPanel.getElement().getStyle().setMarginTop(-12, Unit.PX);
|
||||
this.mainPanel.setStyleName("SideBySideComponentViewer-MainPanel");
|
||||
this.splitLayoutPanel = new TouchSplitLayoutPanel(/* horizontal splitter width */3, /* vertical splitter height */
|
||||
25);
|
||||
this.mainPanel.add(splitLayoutPanel);
|
||||
|
||||
// initialize components - they need to be added before other widgets to get the right width
|
||||
initializeComponents();
|
||||
|
||||
// initialize the leaderboard component
|
||||
savedSplitPosition = MIN_LEADERBOARD_WIDTH;
|
||||
splitLayoutPanel.insert(leftScrollPanel, leftComponent, Direction.WEST, savedSplitPosition);
|
||||
|
||||
// create a panel that will contain the horizontal toggle buttons
|
||||
ResizableAbsolutePanel panelForMapAndHorizontalToggleButtons = new ResizableAbsolutePanel();
|
||||
panelForMapAndHorizontalToggleButtons.add(rightComponent.getEntryWidget());
|
||||
splitLayoutPanel.insert(panelForMapAndHorizontalToggleButtons, rightComponent, Direction.CENTER, 0);
|
||||
|
||||
// add additional toggle buttons panel that currently only contains the video button
|
||||
List<Pair<Button, String>> additionalVerticalButtons = new ArrayList<Pair<Button, String>>();
|
||||
additionalVerticalButtons.add(new Pair<Button, String>(mediaSelectionButton,
|
||||
mediaPlayerManagerComponent.getDependentCssClassName()));
|
||||
if (/* TODO check for correct role; was: user != null */ true) {
|
||||
additionalVerticalButtons.add(new Pair<Button, String>(mediaManagementButton,
|
||||
"managemedia"));
|
||||
}
|
||||
onUserStatusChange(userService.getCurrentUser());
|
||||
|
||||
// ensure that toggle buttons are positioned right
|
||||
splitLayoutPanel.lastComponentHasBeenAdded(this, panelForMapAndHorizontalToggleButtons,
|
||||
additionalVerticalButtons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the video control button that shows or hides the video popup
|
||||
*
|
||||
* @param userAgent
|
||||
*/
|
||||
private Button createMediaSelectionButton(final MediaPlayerManager mediaPlayerManager) {
|
||||
final Button result = new Button();
|
||||
final MediaSingleSelectionControl multiSelectionControl = new MediaSingleSelectionControl(mediaPlayerManager,
|
||||
result, stringMessages);
|
||||
result.addClickHandler(new ClickHandler() {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
if (mediaPlayerManager.getAssignedMediaTracks().size() == 1) {
|
||||
if (mediaPlayerManager.isPlaying()) {
|
||||
mediaPlayerManager.stopAll();
|
||||
} else {
|
||||
mediaPlayerManager.playDefault();
|
||||
|
||||
}
|
||||
} else {
|
||||
multiSelectionControl.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// hide button initially as we defer showing the button to the asynchroneous
|
||||
// task that gets launched by the media service to get video tracks
|
||||
result.setVisible(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the video control button that shows or hides the video popup
|
||||
*
|
||||
* @param userAgent
|
||||
*/
|
||||
private Button createMediaManagementButton(final MediaPlayerManager mediaPlayerManager) {
|
||||
final Button result = new Button(stringMessages.mediaManageMediaCaption());
|
||||
result.setTitle(stringMessages.mediaManageMediaTooltip());
|
||||
// onClick
|
||||
final MediaManagementControl multiSelectionControl = new MediaManagementControl(mediaPlayerManager,
|
||||
result, stringMessages);
|
||||
result.addClickHandler(new ClickHandler() {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
multiSelectionControl.show();
|
||||
}
|
||||
});
|
||||
|
||||
// hide button initially as we defer showing the button to the asynchroneous
|
||||
// task that gets launched by the media service to get video tracks
|
||||
result.setVisible(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initializeComponents() {
|
||||
for (final Component<?> component : components) {
|
||||
splitLayoutPanel.insert(component.getEntryWidget(), component, Direction.SOUTH, 200);
|
||||
}
|
||||
}
|
||||
|
||||
public <SettingsType extends AbstractSettings> void showSettingsDialog(Component<SettingsType> component) {
|
||||
if (component.hasSettings()) {
|
||||
new SettingsDialog<SettingsType>(component, stringMessages).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the layout of {@link TouchSplitLayoutPanel} or other components change. Controls the visibility
|
||||
* based on the {@link Component}s visibility. Each {@link Component} is in charge to not display any data or update
|
||||
* itself when it is not visible.
|
||||
*/
|
||||
public void forceLayout() {
|
||||
if (!leftComponent.isVisible() && rightComponent.isVisible()) {
|
||||
// the leaderboard is not visible, but the map is
|
||||
if (isWidgetInSplitPanel(leftScrollPanel)) {
|
||||
if (leftScrollPanel.getOffsetWidth() > 0) {
|
||||
savedSplitPosition = leftScrollPanel.getOffsetWidth();
|
||||
}
|
||||
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, /* hidden */true,
|
||||
savedSplitPosition);
|
||||
}
|
||||
} else if (leftComponent.isVisible() && rightComponent.isVisible()) {
|
||||
// the leaderboard and the map are visible
|
||||
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, /* hidden */false, savedSplitPosition);
|
||||
} else if (!leftComponent.isVisible() && !rightComponent.isVisible()) {
|
||||
}
|
||||
|
||||
for (Component<?> component : components) {
|
||||
final boolean isComponentVisible = component.isVisible();
|
||||
splitLayoutPanel.setWidgetVisibility(component.getEntryWidget(), component, !isComponentVisible,
|
||||
DEFAULT_SOUTH_SPLIT_PANEL_HEIGHT);
|
||||
}
|
||||
splitLayoutPanel.forceLayout();
|
||||
}
|
||||
|
||||
private boolean isWidgetInSplitPanel(Widget widget) {
|
||||
int widgetIndex = splitLayoutPanel.getWidgetIndex(widget);
|
||||
return widgetIndex >= 0;
|
||||
}
|
||||
|
||||
public Panel getViewerWidget() {
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
public Component<?> getRootComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getViewerName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setLeftComponentWidth(int width) {
|
||||
// TODO: The information provided by width is wrong
|
||||
// need to find a way to get the correct information
|
||||
if (!layoutForLeftComponentForcedOnce) {
|
||||
savedSplitPosition = MIN_LEADERBOARD_WIDTH;
|
||||
forceLayout();
|
||||
}
|
||||
layoutForLeftComponentForcedOnce = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserStatusChange(UserDTO user) {
|
||||
Button toggleButton = splitLayoutPanel.getAssociatedSplitter(markPassingsPanel).getToggleButton();
|
||||
if (user != null
|
||||
&& (user.hasRole(DefaultRoles.ADMIN.getRolename()) || user.hasRole(Roles.eventmanager.getRolename()))) {
|
||||
toggleButton.setVisible(true);
|
||||
forceLayout();
|
||||
} else {
|
||||
markPassingsPanel.setVisible(false);
|
||||
toggleButton.setVisible(false);
|
||||
forceLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.dom.client.Style;
|
||||
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.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.AbsolutePanel;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
|
||||
import com.google.gwt.user.client.ui.LayoutPanel;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.RequiresResize;
|
||||
import com.google.gwt.user.client.ui.ScrollPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.WidgetCollection;
|
||||
import com.sap.sailing.domain.common.security.Roles;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaManagementControl;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManager;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManager.PlayerChangeListener;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent;
|
||||
import com.sap.sailing.gwt.ui.client.media.MediaSingleSelectionControl;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.EditMarkPassingsPanel;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
import com.sap.sailing.gwt.ui.raceboard.TouchSplitLayoutPanel.Splitter;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.settings.AbstractSettings;
|
||||
import com.sap.sse.gwt.client.shared.components.Component;
|
||||
import com.sap.sse.gwt.client.shared.components.ComponentViewer;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
|
||||
import com.sap.sse.security.shared.DefaultRoles;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
import com.sap.sse.security.ui.client.UserStatusEventHandler;
|
||||
import com.sap.sse.security.ui.shared.UserDTO;
|
||||
|
||||
/**
|
||||
* Component Viewer that uses a {@link TouchSplitLayoutPanel} to display its components.
|
||||
*
|
||||
* TODO: Refactor to make sure it is really only performing operations that are related to view. Currently it is digging
|
||||
* too deep into components and setting titles or even creating video buttons.
|
||||
*/
|
||||
public class SideBySideComponentViewer implements ComponentViewer, UserStatusEventHandler {
|
||||
|
||||
private static final int DEFAULT_SOUTH_SPLIT_PANEL_HEIGHT = 200;
|
||||
private final int MIN_LEADERBOARD_WIDTH = Math.min(432, Window.getClientWidth() - 40); // fallback value "432" works well for 505 and ESS
|
||||
|
||||
/**
|
||||
* Absolute Panel that informs its children about a resize
|
||||
*/
|
||||
class ResizableAbsolutePanel extends AbsolutePanel implements RequiresResize {
|
||||
public void onResize() {
|
||||
WidgetCollection children = getChildren();
|
||||
for (Widget widget : children) {
|
||||
if (widget instanceof RequiresResize) {
|
||||
((RequiresResize) widget).onResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final LeaderboardPanel leftComponent;
|
||||
private final Component<?> rightComponent;
|
||||
private final List<Component<?>> components;
|
||||
private final ScrollPanel leftScrollPanel;
|
||||
private final StringMessages stringMessages;
|
||||
private final Button mediaSelectionButton;
|
||||
private final Button mediaManagementButton;
|
||||
private final EditMarkPassingsPanel markPassingsPanel;
|
||||
|
||||
private LayoutPanel mainPanel;
|
||||
|
||||
private TouchSplitLayoutPanel splitLayoutPanel;
|
||||
private int savedSplitPosition = -1;
|
||||
private boolean layoutForLeftComponentForcedOnce = false;
|
||||
|
||||
public SideBySideComponentViewer(final LeaderboardPanel leftComponentP, final Component<?> rightComponentP,
|
||||
final MediaPlayerManagerComponent mediaPlayerManagerComponent, List<Component<?>> components,
|
||||
final StringMessages stringMessages, UserService userService, EditMarkPassingsPanel markPassingsPanel) {
|
||||
this.stringMessages = stringMessages;
|
||||
this.leftComponent = leftComponentP;
|
||||
this.rightComponent = rightComponentP;
|
||||
this.components = components;
|
||||
this.mediaSelectionButton = createMediaSelectionButton(mediaPlayerManagerComponent);
|
||||
this.mediaManagementButton = createMediaManagementButton(mediaPlayerManagerComponent);
|
||||
this.markPassingsPanel = markPassingsPanel;
|
||||
userService.addUserStatusEventHandler(this);
|
||||
mediaPlayerManagerComponent.setPlayerChangeListener(new PlayerChangeListener() {
|
||||
public void notifyStateChange() {
|
||||
String caption;
|
||||
String tooltip;
|
||||
switch (mediaPlayerManagerComponent.getAssignedMediaTracks().size()) {
|
||||
case 0:
|
||||
caption = stringMessages.mediaNoVideosCaption();
|
||||
tooltip = caption;
|
||||
mediaSelectionButton.setVisible(false);
|
||||
break;
|
||||
case 1:
|
||||
mediaSelectionButton.setVisible(true);
|
||||
if(mediaPlayerManagerComponent.isPlaying()){
|
||||
caption = stringMessages.mediaHideVideoCaption();
|
||||
tooltip = stringMessages.mediaHideVideoTooltip();
|
||||
}
|
||||
else{
|
||||
caption = stringMessages.mediaShowVideoCaption();
|
||||
tooltip = stringMessages.mediaShowVideoTooltip(mediaPlayerManagerComponent.getAssignedMediaTracks().iterator().next().title);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mediaSelectionButton.setVisible(true);
|
||||
caption = stringMessages.mediaSelectVideoCaption(mediaPlayerManagerComponent.getAssignedMediaTracks().size());
|
||||
tooltip = stringMessages.mediaSelectVideoTooltip();
|
||||
break;
|
||||
}
|
||||
if (mediaPlayerManagerComponent.isPlaying()) {
|
||||
mediaSelectionButton.addStyleDependentName("mediaplaying");
|
||||
} else {
|
||||
mediaSelectionButton.removeStyleDependentName("mediaplaying");
|
||||
}
|
||||
mediaSelectionButton.setText(caption);
|
||||
mediaSelectionButton.setTitle(tooltip);
|
||||
mediaManagementButton.setVisible(mediaPlayerManagerComponent.allowsEditing());
|
||||
}
|
||||
});
|
||||
this.leftScrollPanel = new ScrollPanel();
|
||||
this.leftScrollPanel.add(leftComponentP.getEntryWidget());
|
||||
this.leftScrollPanel.setTitle(leftComponentP.getEntryWidget().getTitle());
|
||||
this.mainPanel = new LayoutPanel() {
|
||||
@Override
|
||||
public void onResize() {
|
||||
int leftWidth = leftScrollPanel.getOffsetWidth();
|
||||
// The left scroll panel is potentially resized to ensure it is not too wide when the screen gets narrower,
|
||||
// e.g. when resizing browser window or changing mobile device orientation. An offset of 40px is used, so
|
||||
// the panels size slider and its toggle button is always accessable if it is open.
|
||||
savedSplitPosition = Math.min(leftWidth > 0 ? leftWidth : savedSplitPosition, Window.getClientWidth() - 40);
|
||||
splitLayoutPanel.setWidgetSize(leftScrollPanel, savedSplitPosition);
|
||||
super.onResize();
|
||||
}
|
||||
};
|
||||
this.mainPanel.setSize("100%", "100%");
|
||||
this.mainPanel.getElement().getStyle().setMarginTop(-12, Unit.PX);
|
||||
this.mainPanel.setStyleName("SideBySideComponentViewer-MainPanel");
|
||||
this.splitLayoutPanel = new TouchSplitLayoutPanel(/* horizontal splitter width */3, /* vertical splitter height */ 25);
|
||||
this.mainPanel.add(splitLayoutPanel);
|
||||
|
||||
// initialize components - they need to be added before other widgets to get the right width
|
||||
initializeComponents();
|
||||
|
||||
// initialize the leaderboard component
|
||||
savedSplitPosition = MIN_LEADERBOARD_WIDTH;
|
||||
splitLayoutPanel.insert(leftScrollPanel, leftComponent, Direction.WEST, savedSplitPosition);
|
||||
|
||||
// create a panel that will contain the horizontal toggle buttons
|
||||
ResizableAbsolutePanel panelForMapAndHorizontalToggleButtons = new ResizableAbsolutePanel();
|
||||
panelForMapAndHorizontalToggleButtons.add(rightComponent.getEntryWidget());
|
||||
splitLayoutPanel.insert(panelForMapAndHorizontalToggleButtons, rightComponent, Direction.CENTER, 0);
|
||||
|
||||
// add additional toggle buttons panel that currently only contains the video button
|
||||
List<Pair<Button, String>> additionalVerticalButtons = new ArrayList<Pair<Button, String>>();
|
||||
additionalVerticalButtons.add(new Pair<Button, String>(mediaSelectionButton,
|
||||
mediaPlayerManagerComponent.getDependentCssClassName()));
|
||||
if (/* TODO check for correct role; was: user != null */ true) {
|
||||
additionalVerticalButtons.add(new Pair<Button, String>(mediaManagementButton,
|
||||
"managemedia"));
|
||||
}
|
||||
onUserStatusChange(userService.getCurrentUser());
|
||||
// ensure that toggle buttons are positioned right
|
||||
splitLayoutPanel.lastComponentHasBeenAdded(this, panelForMapAndHorizontalToggleButtons,
|
||||
additionalVerticalButtons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the video control button that shows or hides the video popup
|
||||
*
|
||||
* @param userAgent
|
||||
*/
|
||||
private Button createMediaSelectionButton(final MediaPlayerManager mediaPlayerManager) {
|
||||
final Button result = new Button();
|
||||
final MediaSingleSelectionControl multiSelectionControl = new MediaSingleSelectionControl(mediaPlayerManager,
|
||||
result, stringMessages);
|
||||
result.addClickHandler(new ClickHandler() {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
if (mediaPlayerManager.getAssignedMediaTracks().size() == 1) {
|
||||
if (mediaPlayerManager.isPlaying()) {
|
||||
mediaPlayerManager.stopAll();
|
||||
} else {
|
||||
mediaPlayerManager.playDefault();
|
||||
|
||||
}
|
||||
} else {
|
||||
multiSelectionControl.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// hide button initially as we defer showing the button to the asynchroneous
|
||||
// task that gets launched by the media service to get video tracks
|
||||
result.setVisible(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the video control button that shows or hides the video popup
|
||||
*
|
||||
* @param userAgent
|
||||
*/
|
||||
private Button createMediaManagementButton(final MediaPlayerManager mediaPlayerManager) {
|
||||
final Button result = new Button(stringMessages.mediaManageMediaCaption());
|
||||
result.setTitle(stringMessages.mediaManageMediaTooltip());
|
||||
// onClick
|
||||
final MediaManagementControl multiSelectionControl = new MediaManagementControl(mediaPlayerManager,
|
||||
result, stringMessages);
|
||||
result.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
multiSelectionControl.show();
|
||||
}
|
||||
});
|
||||
// hide button initially as we defer showing the button to the asynchronous
|
||||
// task that gets launched by the media service to get video tracks
|
||||
result.setVisible(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initializeComponents() {
|
||||
for (final Component<?> component : components) {
|
||||
splitLayoutPanel.insert(component.getEntryWidget(), component, Direction.SOUTH, 200);
|
||||
}
|
||||
}
|
||||
|
||||
public <SettingsType extends AbstractSettings> void showSettingsDialog(Component<SettingsType> component) {
|
||||
if (component.hasSettings()) {
|
||||
new SettingsDialog<SettingsType>(component, stringMessages).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the layout of {@link TouchSplitLayoutPanel} or other components change. Controls the visibility
|
||||
* based on the {@link Component}s visibility. Each {@link Component} is in charge to not display any data or update
|
||||
* itself when it is not visible.
|
||||
*/
|
||||
public void forceLayout() {
|
||||
if (!leftComponent.isVisible() && rightComponent.isVisible()) {
|
||||
// the leaderboard is not visible, but the map is
|
||||
if (isWidgetInSplitPanel(leftScrollPanel)) {
|
||||
if (leftScrollPanel.getOffsetWidth() > 0) {
|
||||
savedSplitPosition = Math.min(savedSplitPosition, leftScrollPanel.getOffsetWidth());
|
||||
}
|
||||
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, /* hidden */true,
|
||||
savedSplitPosition);
|
||||
}
|
||||
} else if (leftComponent.isVisible() && rightComponent.isVisible()) {
|
||||
// the leaderboard and the map are visible
|
||||
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, /* hidden */false, savedSplitPosition);
|
||||
} else if (!leftComponent.isVisible() && !rightComponent.isVisible()) {
|
||||
}
|
||||
|
||||
for (Component<?> component : components) {
|
||||
final boolean isComponentVisible = component.isVisible();
|
||||
splitLayoutPanel.setWidgetVisibility(component.getEntryWidget(), component, !isComponentVisible,
|
||||
DEFAULT_SOUTH_SPLIT_PANEL_HEIGHT);
|
||||
}
|
||||
splitLayoutPanel.forceLayout();
|
||||
}
|
||||
|
||||
private boolean isWidgetInSplitPanel(Widget widget) {
|
||||
int widgetIndex = splitLayoutPanel.getWidgetIndex(widget);
|
||||
return widgetIndex >= 0;
|
||||
}
|
||||
|
||||
public Panel getViewerWidget() {
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
public Component<?> getRootComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getViewerName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setLeftComponentWidth(int width) {
|
||||
// TODO: The information provided by width is wrong
|
||||
// need to find a way to get the correct information
|
||||
if (!layoutForLeftComponentForcedOnce) {
|
||||
savedSplitPosition = MIN_LEADERBOARD_WIDTH;
|
||||
forceLayout();
|
||||
}
|
||||
layoutForLeftComponentForcedOnce = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserStatusChange(UserDTO user) {
|
||||
final Splitter associatedSplitter = splitLayoutPanel.getAssociatedSplitter(markPassingsPanel);
|
||||
if (associatedSplitter != null) { // if the panel is not present, the splitter may not be found
|
||||
final Button toggleButton = associatedSplitter.getToggleButton();
|
||||
if (user != null
|
||||
&& (user.hasRole(DefaultRoles.ADMIN.getRolename()) ||
|
||||
user.hasRole(Roles.eventmanager.getRolename()))) {
|
||||
toggleButton.setVisible(true);
|
||||
forceLayout();
|
||||
} else {
|
||||
markPassingsPanel.setVisible(false);
|
||||
toggleButton.setVisible(false);
|
||||
forceLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows/hides the text on left components toggle button by modifying CSS <code>font-size</code> property and adjust
|
||||
* the dragger position by modifying CSS <code>margin-top</code> property.
|
||||
*
|
||||
* @param visible
|
||||
* <code>true</code> to show the button text, <code>false</code> to hide it
|
||||
*/
|
||||
void setLeftComponentToggleButtonTextVisibilityAndDraggerPosition(final boolean visible) {
|
||||
Splitter leftScrollPanelSplitter = splitLayoutPanel.getAssociatedSplitter(leftScrollPanel);
|
||||
if (leftScrollPanelSplitter != null) {
|
||||
Style toggleButtonStyle = leftScrollPanelSplitter.getToggleButton().getElement().getStyle();
|
||||
if (visible) toggleButtonStyle.clearFontSize();
|
||||
else toggleButtonStyle.setFontSize(0, Unit.PX);
|
||||
Style drapperStyle = leftScrollPanelSplitter.getDragger().getElement().getStyle();
|
||||
if (visible) drapperStyle.clearMarginTop();
|
||||
else drapperStyle.setMarginTop(-25, Unit.PX);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-2
@@ -613,7 +613,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel {
|
||||
if (lastVerticalSplitter != null) {
|
||||
Panel panel = createToggleButtonPanel(allVerticalSplitters,
|
||||
"gwt-SplitLayoutPanel-NorthSouthToggleButton-Panel", "gwt-SplitLayoutPanel-NorthSouthToggleButton",
|
||||
cm, additionalVerticalButtonsAndStyles);
|
||||
cm, additionalVerticalButtonsAndStyles); // TODO additional buttons will only be displayed if at least one vertical splitter is... on purpose?
|
||||
lastVerticalSplitter.addToogleButtons(panel);
|
||||
lastVerticalSplitter.setVisible(true);
|
||||
lastVerticalSplitter.setDraggerVisible(false);
|
||||
@@ -621,7 +621,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel {
|
||||
ensureVerticalToggleButtonPosition();
|
||||
if (lastHorizontalSplitter != null) {
|
||||
Panel horizontalButtonsPanel = createToggleButtonPanel(allHorizontalSplitters,
|
||||
"gwt-SplitLayoutPanel-EastToggleButton-Panel", "gwt-SplitLayoutPanel-EastToggleButton", cm, null);
|
||||
"gwt-SplitLayoutPanel-EastToggleButton-Panel", "gwt-SplitLayoutPanel-EastToggleButton", cm, /* additional buttons and styles */ null);
|
||||
panelForHorizontalButtons.add(horizontalButtonsPanel);
|
||||
lastHorizontalSplitter.setVisible(false);
|
||||
lastHorizontalSplitter.setDraggerVisible(false);
|
||||
@@ -842,6 +842,24 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the given {@link Widget} if it is {@link Widget#isVisible() visible}. Otherwise, the given size
|
||||
* will be persisted as the widgets {@link LayoutData#oldSize old size}.
|
||||
*
|
||||
* @param widget the {@link Widget} to resize
|
||||
* @param size the new size
|
||||
*/
|
||||
public void setWidgetSize(Widget widget, int size) {
|
||||
final Splitter splitter = getAssociatedSplitter(widget);
|
||||
if (splitter != null) {
|
||||
if (widget.isVisible()) {
|
||||
splitter.setAssociatedWidgetSize(size, /* defer */false);
|
||||
} else {
|
||||
((LayoutData) widget.getLayoutData()).oldSize = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertIsChild2(Widget widget) {
|
||||
assert (widget == null) || (widget.getParent() == this) : "The specified widget is not a child of this panel";
|
||||
|
||||
+3
-1
@@ -309,7 +309,9 @@ public class SimulatorMainPanel extends SimplePanel {
|
||||
timer.setTime(windParams.getStartTime().getTime());
|
||||
int secondsTimeStep = (int) windParams.getTimeStep().asSeconds();
|
||||
timer.setPlaySpeedFactor(secondsTimeStep);
|
||||
timePanel = new TimePanel<TimePanelSettings>(timer, timeRangeProvider, stringMessages, false);
|
||||
timePanel = new TimePanel<TimePanelSettings>(timer, timeRangeProvider, stringMessages, false,
|
||||
/* isScreenLargeEnoughToOfferChartSupport: no wind or competitor chart is shown; use full horizontal
|
||||
* extension of time panel */ false);
|
||||
busyIndicator = new SimpleBusyIndicator(false, 0.8f);
|
||||
simulatorMap = new SimulatorMap(simulatorSvc, stringMessages, errorReporter, xRes, yRes, border, streamletPars,
|
||||
timer, timePanel, windParams, busyIndicator, mode, this, showMapControls,
|
||||
|
||||
+10
@@ -13,7 +13,17 @@
|
||||
by unticking the corresponding checkbox in the settings of the race map. Furthermore the stroke weight of the wider lines can be
|
||||
configured in the same settings dialog.
|
||||
</li>
|
||||
<li>The race viewer (RaceBoard.html) now intelligently chooses a layout that suits the screen size.
|
||||
When only little horizontal space is available, only the sail number and nationality but not the
|
||||
competitor's long name is displayed by default. When vertical space is scarce, competitor and wind
|
||||
charts are disabled and more horizontal space can be given to the time slider at the bottom. The
|
||||
leaderboard section can then be scaled to smaller widths and will reduce the column widths better
|
||||
than in the past.
|
||||
</li>
|
||||
<li>With the changes described above, the RaceBoard is now also accessible from the races page on mobile devices.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h5 class="articleSubheadline">November 2015</h5>
|
||||
|
||||
<ul class="bulletList">
|
||||
|
||||
+1256
-1256
File diff suppressed because it is too large.
Load diff
@@ -26,7 +26,9 @@ public class TimeSlider extends SliderBar {
|
||||
private List<TickPosition> calculatedTimeTicks;
|
||||
|
||||
private boolean isZoomed;
|
||||
|
||||
|
||||
private int visibleLabelsInterval = 1;
|
||||
|
||||
private final int TICKCOUNT = 10;
|
||||
|
||||
public TimeSlider() {
|
||||
@@ -84,7 +86,7 @@ public class TimeSlider extends SliderBar {
|
||||
int tickLeftOffset = lineLeftOffset + (int) pos - (tickWidth / 2);
|
||||
tickLeftOffset = Math.min(tickLeftOffset, lineLeftOffset + lineWidth - tickWidth);
|
||||
tick.getStyle().setLeft(tickLeftOffset, Unit.PX);
|
||||
tick.getStyle().setVisibility(Visibility.VISIBLE);
|
||||
tick.getStyle().setVisibility(isTickInVisibleRange(tickPosition) ? Visibility.VISIBLE : Visibility.HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +108,7 @@ public class TimeSlider extends SliderBar {
|
||||
int lineWidth = lineElement.getOffsetWidth();
|
||||
// Create the labels or make them visible
|
||||
Double previousValue = null;
|
||||
for (int i = 0; i < calculatedTimeTicks.size(); i++) {
|
||||
for (int i = 0, ticksInVisibleRange = 0; i < calculatedTimeTicks.size(); i++) {
|
||||
TickPosition tickPosition = calculatedTimeTicks.get(i);
|
||||
Element label = null;
|
||||
if (i < tickLabelElements.size()) {
|
||||
@@ -142,7 +144,8 @@ public class TimeSlider extends SliderBar {
|
||||
labelLeftOffset = Math.min(labelLeftOffset, lineLeftOffset + lineWidth - labelWidth);
|
||||
labelLeftOffset = Math.max(labelLeftOffset, lineLeftOffset);
|
||||
label.getStyle().setLeft(labelLeftOffset, Unit.PX);
|
||||
label.getStyle().setVisibility(Visibility.VISIBLE);
|
||||
boolean visible = isTickInVisibleRange(tickPosition) && ticksInVisibleRange++ % visibleLabelsInterval == 0;
|
||||
label.getStyle().setVisibility(visible ? Visibility.VISIBLE : Visibility.HIDDEN);
|
||||
}
|
||||
previousValue = value;
|
||||
}
|
||||
@@ -152,6 +155,11 @@ public class TimeSlider extends SliderBar {
|
||||
tickLabelElements.get(i).getStyle().setDisplay(Display.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTickInVisibleRange(TickPosition tickPosition) {
|
||||
long tickValue = tickPosition.getPosition().getTime();
|
||||
return tickValue >= minValue && tickValue <= maxValue;
|
||||
}
|
||||
|
||||
public void clearMarkersAndLabelsAndTicks() {
|
||||
clearMarkers();
|
||||
@@ -245,4 +253,10 @@ public class TimeSlider extends SliderBar {
|
||||
public void setZoomed(boolean isZoomed) {
|
||||
this.isZoomed = isZoomed;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize() {
|
||||
visibleLabelsInterval = (TICKCOUNT / Math.max(getOffsetWidth() / 35, 1)) + 1;
|
||||
super.onResize();
|
||||
}
|
||||
}
|
||||
+247
-246
@@ -1,246 +1,247 @@
|
||||
package com.sap.sse.gwt.client.controls.slider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This calculator emulates the way the HighCharts charting library is computing the chart ticks
|
||||
* in case of a x-axis based on time values.
|
||||
*/
|
||||
public class TimeTicksCalculator {
|
||||
|
||||
public TimeTicksCalculator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a normalized tick interval for dates. Returns a configuration object with unit range (interval), count and
|
||||
* name. Used to prepare data for getTimeTicks. Previously this logic was part of getTimeTicks, but as getTimeTicks
|
||||
* now runs of segments in stock charts, the normalizing logic was extracted in order to prevent it for running over
|
||||
* again for each segment having the same interval.
|
||||
*/
|
||||
public NormalizedInterval normalizeTimeTickInterval(long tickInterval) {
|
||||
TimeUnits[] units = TimeUnits.values();
|
||||
|
||||
TimeUnits unit = units[units.length - 1]; // default unit is years
|
||||
long interval = unit.unitInMs;
|
||||
int[] multiples = unit.allowedMultiples;
|
||||
int count;
|
||||
|
||||
// loop through the units to find the one that best fits the tickInterval
|
||||
for (int i = 0; i < units.length; i++) {
|
||||
unit = units[i];
|
||||
interval = unit.unitInMs;
|
||||
multiples = unit.allowedMultiples;
|
||||
|
||||
if (i + 1 < units.length) {
|
||||
// lessThan is in the middle between the highest multiple and the next unit.
|
||||
long lessThan = (interval * multiples[multiples.length - 1] + units[i + 1].unitInMs) / 2;
|
||||
|
||||
// break and keep the current unit
|
||||
if (tickInterval <= lessThan) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prevent 2.5 years intervals, though 25, 250 etc. are allowed
|
||||
if (interval == TimeUnits.YEAR.unitInMs && tickInterval < 5 * interval) {
|
||||
multiples = new int[] { 1, 2, 5 };
|
||||
}
|
||||
|
||||
// get the count
|
||||
count = (int) normalizeTickInterval(tickInterval / interval, multiples, 1);
|
||||
|
||||
return new NormalizedInterval(unit.name(), interval, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an interval and normalize it to multiples of 1, 2, 2.5 and 5
|
||||
*
|
||||
* @param {Number} interval
|
||||
* @param {Array} multiples
|
||||
* @param {Number} magnitude
|
||||
* @param {Object} options
|
||||
*/
|
||||
public long normalizeTickInterval(long interval, int[] multiples, int magnitude) {
|
||||
long normalized;
|
||||
|
||||
// round to a tenfold of 1, 2, 2.5 or 5
|
||||
normalized = interval / magnitude;
|
||||
|
||||
// normalize the interval to the nearest multiple
|
||||
for (int i = 0; i < multiples.length; i++) {
|
||||
interval = multiples[i];
|
||||
int addedValue = i + 1 < multiples.length ? multiples[i + 1] : multiples[i];
|
||||
if (normalized <= (multiples[i] + addedValue) / 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// multiply back to the correct magnitude
|
||||
interval *= magnitude;
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
private long makeTime(int year, int month) {
|
||||
return makeTime(year, month, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long makeTime(int year, int month, int date) {
|
||||
return makeTime(year, month, date, 0, 0, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private long makeTime(int year, int month, int date, int hours, int minutes, int seconds) {
|
||||
return new Date(year, month, date, hours, minutes, seconds).getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tick positions to a time unit that makes sense, for example on the first of each month or on every
|
||||
* Monday. Return an array with the time positions. Used in datetime axes as well as for grouping data on a datetime
|
||||
* axis.
|
||||
*
|
||||
* @param {Object} normalizedInterval The interval in axis values (ms) and the count
|
||||
* @param {Number} min The minimum in axis values
|
||||
* @param {Number} max The maximum in axis values
|
||||
* @param {Number} startOfWeek
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public List<TickPosition> calculateTimeTicks(NormalizedInterval normalizedInterval, long min, long max,
|
||||
long startOfWeek) {
|
||||
List<TickPosition> tickPositions = new ArrayList<TickPosition>();
|
||||
int i;
|
||||
boolean useUTC = true;
|
||||
int minYear = 0; // used in months and years as a basis for Date.UTC()
|
||||
Date minDate = new Date(min);
|
||||
long interval = normalizedInterval.unitRange;
|
||||
int count = (int) normalizedInterval.count;
|
||||
|
||||
|
||||
if (interval >= TimeUnits.SECOND.unitInMs) { // second
|
||||
minDate.setTime(min - (min % 1000));
|
||||
minDate.setSeconds(interval >= TimeUnits.MINUTE.unitInMs ? 0 : count
|
||||
* Math.round(minDate.getSeconds() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.MINUTE.unitInMs) { // minute
|
||||
minDate.setMinutes(interval >= TimeUnits.HOUR.unitInMs ? 0 : count
|
||||
* Math.round(minDate.getMinutes() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.HOUR.unitInMs) { // hour
|
||||
minDate.setHours(interval >= TimeUnits.DAY.unitInMs ? 0 : count * Math.round(minDate.getHours() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.DAY.unitInMs) { // day
|
||||
minDate.setDate(interval >= TimeUnits.MONTH.unitInMs ? 1 : count * Math.round(minDate.getDate() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.MONTH.unitInMs) { // month
|
||||
minDate.setMonth(interval >= TimeUnits.YEAR.unitInMs ? 0 : count * Math.round(minDate.getMonth() + 1 / count) - 1);
|
||||
minYear = minDate.getYear() + 1900;
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.YEAR.unitInMs) { // year
|
||||
minYear -= minYear % count;
|
||||
minDate.setYear(minYear - 1900);
|
||||
}
|
||||
|
||||
// week is a special case that runs outside the hierarchy
|
||||
if (interval == TimeUnits.WEEK.unitInMs) {
|
||||
// get start of current week, independent of count
|
||||
long value = minDate.getDate() - minDate.getDay() + startOfWeek;
|
||||
minDate.setDate((int) value);
|
||||
}
|
||||
|
||||
// get tick positions
|
||||
i = 1;
|
||||
minYear = minDate.getYear() + 1900;
|
||||
int minMonth = minDate.getMonth() + 1;
|
||||
int minDateDate = minDate.getDate();
|
||||
long time = minDate.getTime();
|
||||
|
||||
// iterate and add tick positions at appropriate values
|
||||
while (time < max) {
|
||||
if(time >= min)
|
||||
tickPositions.add(new TickPosition(time));
|
||||
|
||||
// if the interval is years, use Date.UTC to increase years
|
||||
if (interval == TimeUnits.YEAR.unitInMs) {
|
||||
time = makeTime(minYear + i * count, 0);
|
||||
|
||||
// if the interval is months, use Date.UTC to increase months
|
||||
} else if (interval == TimeUnits.MONTH.unitInMs) {
|
||||
time = makeTime(minYear, minMonth + i * count);
|
||||
|
||||
// if we're using global time, the interval is not fixed as it jumps
|
||||
// one hour at the DST crossover
|
||||
} else if (!useUTC && (interval == TimeUnits.DAY.unitInMs || interval == TimeUnits.WEEK.unitInMs)) {
|
||||
time = makeTime(minYear, minMonth, minDateDate + i * count * (interval == TimeUnits.DAY.unitInMs ? 1 : 7));
|
||||
|
||||
// else, the interval is fixed and we use simple addition
|
||||
} else {
|
||||
time += interval * count;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// push the last time
|
||||
if(time <= max)
|
||||
tickPositions.add(new TickPosition(time));
|
||||
|
||||
return tickPositions;
|
||||
}
|
||||
|
||||
public class NormalizedInterval {
|
||||
public String unitName;
|
||||
public long unitRange;
|
||||
public int count;
|
||||
|
||||
public NormalizedInterval(String unitName, long unitRange, int count) {
|
||||
this.unitName = unitName;
|
||||
this.unitRange = unitRange;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NormalizedInterval [unitName=" + unitName + ", unitRange=" + unitRange + ", count=" + count + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public class TickPosition {
|
||||
private Date position;
|
||||
|
||||
public TickPosition(long time) {
|
||||
this.position = new Date(time);
|
||||
}
|
||||
|
||||
public Date getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
enum TimeUnits {
|
||||
MILLISECOND(1, new int[] { 1, 2, 5, 10, 20, 25, 50, 100, 200, 500 }),
|
||||
SECOND(1000, new int[] { 1, 2, 5, 10, 15, 30 }),
|
||||
MINUTE(60 * 1000, new int[] { 1, 2, 5, 10, 15, 30 }),
|
||||
HOUR(60 * 60 * 1000, new int[] { 1, 2, 3, 4, 6, 8, 12 }),
|
||||
DAY(24 * 3600000l, new int[] { 1, 2 }),
|
||||
WEEK(7 * 24 * 3600000l, new int[] { 1, 2 }),
|
||||
MONTH(30 * 24 * 3600000l, new int[] { 1, 2, 3, 4, 6 }),
|
||||
YEAR(31556952000l, new int[] {});
|
||||
|
||||
int[] allowedMultiples;
|
||||
|
||||
long unitInMs;
|
||||
|
||||
TimeUnits(long unitInMs, int[] allowedMultiples) {
|
||||
this.unitInMs = unitInMs;
|
||||
this.allowedMultiples = allowedMultiples;
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.sap.sse.gwt.client.controls.slider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This calculator emulates the way the HighCharts charting library is computing the chart ticks
|
||||
* in case of a x-axis based on time values.
|
||||
*/
|
||||
public class TimeTicksCalculator {
|
||||
|
||||
public TimeTicksCalculator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a normalized tick interval for dates. Returns a configuration object with unit range (interval), count and
|
||||
* name. Used to prepare data for getTimeTicks. Previously this logic was part of getTimeTicks, but as getTimeTicks
|
||||
* now runs of segments in stock charts, the normalizing logic was extracted in order to prevent it for running over
|
||||
* again for each segment having the same interval.
|
||||
*/
|
||||
public NormalizedInterval normalizeTimeTickInterval(long tickInterval) {
|
||||
TimeUnits[] units = TimeUnits.values();
|
||||
|
||||
TimeUnits unit = units[units.length - 1]; // default unit is years
|
||||
long interval = unit.unitInMs;
|
||||
int[] multiples = unit.allowedMultiples;
|
||||
int count;
|
||||
|
||||
// loop through the units to find the one that best fits the tickInterval
|
||||
for (int i = 0; i < units.length; i++) {
|
||||
unit = units[i];
|
||||
interval = unit.unitInMs;
|
||||
multiples = unit.allowedMultiples;
|
||||
|
||||
if (i + 1 < units.length) {
|
||||
// lessThan is in the middle between the highest multiple and the next unit.
|
||||
double lessThan = (interval * multiples[multiples.length - 1] + units[i + 1].unitInMs) / 2;
|
||||
|
||||
// break and keep the current unit
|
||||
if (tickInterval <= lessThan) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prevent 2.5 years intervals, though 25, 250 etc. are allowed
|
||||
if (interval == TimeUnits.YEAR.unitInMs && tickInterval < 5 * interval) {
|
||||
multiples = new int[] { 1, 2, 5 };
|
||||
}
|
||||
|
||||
// get the count
|
||||
double temp = ((double) tickInterval) / ((double) interval);
|
||||
count = (int) normalizeTickInterval(temp, multiples, 1);
|
||||
|
||||
return new NormalizedInterval(unit.name(), interval, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an interval and normalize it to multiples of 1, 2, 2.5 and 5
|
||||
*
|
||||
* @param {Number} interval
|
||||
* @param {Array} multiples
|
||||
* @param {Number} magnitude
|
||||
* @param {Object} options
|
||||
*/
|
||||
public long normalizeTickInterval(double interval, int[] multiples, int magnitude) {
|
||||
double normalized;
|
||||
|
||||
// round to a tenfold of 1, 2, 2.5 or 5
|
||||
normalized = interval / magnitude;
|
||||
|
||||
// normalize the interval to the nearest multiple
|
||||
for (int i = 0; i < multiples.length; i++) {
|
||||
interval = multiples[i];
|
||||
int addedValue = i + 1 < multiples.length ? multiples[i + 1] : multiples[i];
|
||||
if (normalized <= (multiples[i] + addedValue) / 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// multiply back to the correct magnitude
|
||||
interval *= magnitude;
|
||||
|
||||
return Math.round(interval);
|
||||
}
|
||||
|
||||
private long makeTime(int year, int month) {
|
||||
return makeTime(year, month, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
private long makeTime(int year, int month, int date) {
|
||||
return makeTime(year, month, date, 0, 0, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private long makeTime(int year, int month, int date, int hours, int minutes, int seconds) {
|
||||
return new Date(year, month, date, hours, minutes, seconds).getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tick positions to a time unit that makes sense, for example on the first of each month or on every
|
||||
* Monday. Return an array with the time positions. Used in datetime axes as well as for grouping data on a datetime
|
||||
* axis.
|
||||
*
|
||||
* @param {Object} normalizedInterval The interval in axis values (ms) and the count
|
||||
* @param {Number} min The minimum in axis values
|
||||
* @param {Number} max The maximum in axis values
|
||||
* @param {Number} startOfWeek
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public List<TickPosition> calculateTimeTicks(NormalizedInterval normalizedInterval, long min, long max,
|
||||
long startOfWeek) {
|
||||
List<TickPosition> tickPositions = new ArrayList<TickPosition>();
|
||||
int i;
|
||||
boolean useUTC = true;
|
||||
int minYear = 0; // used in months and years as a basis for Date.UTC()
|
||||
Date minDate = new Date(min);
|
||||
long interval = normalizedInterval.unitRange;
|
||||
int count = (int) normalizedInterval.count;
|
||||
|
||||
|
||||
if (interval >= TimeUnits.SECOND.unitInMs) { // second
|
||||
minDate.setTime(min - (min % 1000));
|
||||
minDate.setSeconds(interval >= TimeUnits.MINUTE.unitInMs ? 0 : count
|
||||
* Math.round(minDate.getSeconds() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.MINUTE.unitInMs) { // minute
|
||||
minDate.setMinutes(interval >= TimeUnits.HOUR.unitInMs ? 0 : count
|
||||
* Math.round(minDate.getMinutes() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.HOUR.unitInMs) { // hour
|
||||
minDate.setHours(interval >= TimeUnits.DAY.unitInMs ? 0 : count * Math.round(minDate.getHours() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.DAY.unitInMs) { // day
|
||||
minDate.setDate(interval >= TimeUnits.MONTH.unitInMs ? 1 : count * Math.round(minDate.getDate() / count));
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.MONTH.unitInMs) { // month
|
||||
minDate.setMonth(interval >= TimeUnits.YEAR.unitInMs ? 0 : count * Math.round(minDate.getMonth() + 1 / count) - 1);
|
||||
minYear = minDate.getYear() + 1900;
|
||||
}
|
||||
|
||||
if (interval >= TimeUnits.YEAR.unitInMs) { // year
|
||||
minYear -= minYear % count;
|
||||
minDate.setYear(minYear - 1900);
|
||||
}
|
||||
|
||||
// week is a special case that runs outside the hierarchy
|
||||
if (interval == TimeUnits.WEEK.unitInMs) {
|
||||
// get start of current week, independent of count
|
||||
long value = minDate.getDate() - minDate.getDay() + startOfWeek;
|
||||
minDate.setDate((int) value);
|
||||
}
|
||||
|
||||
// get tick positions
|
||||
i = 1;
|
||||
minYear = minDate.getYear() + 1900;
|
||||
int minMonth = minDate.getMonth() + 1;
|
||||
int minDateDate = minDate.getDate();
|
||||
long time = minDate.getTime();
|
||||
|
||||
// iterate and add tick positions at appropriate values
|
||||
while (time < max) {
|
||||
if(time >= min)
|
||||
tickPositions.add(new TickPosition(time));
|
||||
|
||||
// if the interval is years, use Date.UTC to increase years
|
||||
if (interval == TimeUnits.YEAR.unitInMs) {
|
||||
time = makeTime(minYear + i * count, 0);
|
||||
|
||||
// if the interval is months, use Date.UTC to increase months
|
||||
} else if (interval == TimeUnits.MONTH.unitInMs) {
|
||||
time = makeTime(minYear, minMonth + i * count);
|
||||
|
||||
// if we're using global time, the interval is not fixed as it jumps
|
||||
// one hour at the DST crossover
|
||||
} else if (!useUTC && (interval == TimeUnits.DAY.unitInMs || interval == TimeUnits.WEEK.unitInMs)) {
|
||||
time = makeTime(minYear, minMonth, minDateDate + i * count * (interval == TimeUnits.DAY.unitInMs ? 1 : 7));
|
||||
|
||||
// else, the interval is fixed and we use simple addition
|
||||
} else {
|
||||
time += interval * count;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// push the last time
|
||||
if(time <= max)
|
||||
tickPositions.add(new TickPosition(time));
|
||||
|
||||
return tickPositions;
|
||||
}
|
||||
|
||||
public class NormalizedInterval {
|
||||
public String unitName;
|
||||
public long unitRange;
|
||||
public int count;
|
||||
|
||||
public NormalizedInterval(String unitName, long unitRange, int count) {
|
||||
this.unitName = unitName;
|
||||
this.unitRange = unitRange;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NormalizedInterval [unitName=" + unitName + ", unitRange=" + unitRange + ", count=" + count + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public class TickPosition {
|
||||
private Date position;
|
||||
|
||||
public TickPosition(long time) {
|
||||
this.position = new Date(time);
|
||||
}
|
||||
|
||||
public Date getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
enum TimeUnits {
|
||||
MILLISECOND(1l, 1, 2, 5, 10, 20, 25, 50, 100, 200, 500),
|
||||
SECOND(1000l, 1, 2, 5, 10, 15, 30),
|
||||
MINUTE(60 * 1000l, 1, 2, 5, 10, 15, 30),
|
||||
HOUR(60 * 60 * 1000l, 1, 2, 3, 4, 6, 8, 12),
|
||||
DAY(24 * 3600000l, 1, 2),
|
||||
WEEK(7 * 24 * 3600000l, 1, 2),
|
||||
MONTH(30 * 24 * 3600000l, 1, 2, 3, 4, 6),
|
||||
YEAR(31556952000l);
|
||||
|
||||
int[] allowedMultiples;
|
||||
|
||||
long unitInMs;
|
||||
|
||||
TimeUnits(long unitInMs, int... allowedMultiples) {
|
||||
this.unitInMs = unitInMs;
|
||||
this.allowedMultiples = allowedMultiples;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,8 +169,7 @@ body ::-webkit-scrollbar-track {
|
||||
|
||||
.gwt-SplitLayoutPanel-EastToggleButton-Panel {
|
||||
position: absolute;
|
||||
bottom: 77px;
|
||||
margin-left: -35px;
|
||||
bottom: -10px;
|
||||
}
|
||||
|
||||
.gwt-SplitLayoutPanel-EastToggleButton {
|
||||
@@ -181,9 +180,14 @@ body ::-webkit-scrollbar-track {
|
||||
-moz-transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
-webkit-transform-origin: 0 0;
|
||||
-moz-transform-origin: 0 0;
|
||||
-o-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
padding-right: 25px;
|
||||
padding-left: 30px;
|
||||
margin-left: -15px;
|
||||
border-bottom: 1px solid #cecece;
|
||||
border-right: 1px solid #cecece;
|
||||
border-left: 1px solid #cecece;
|
||||
|
||||
Reference in new issue
Block a user