added specific leaderboard views for MetaLeaderboards and normal leaderboards

This commit is contained in:
fmittag committed 2013-05-21 17:50:09 +02:00
1 parent c2fdb1fd6a
commit 2ab3af1405
45 files changed
+1679 -619

No files matched your search

@@ -31,7 +31,8 @@ public enum DetailType implements Serializable {
TOTAL_TIME_SAILED_REACHING_IN_SECONDS(1, ASCENDING),
MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS(1, DESCENDING),
TOTAL_DISTANCE_TRAVELED(0, ASCENDING), TOTAL_AVERAGE_SPEED_OVER_GROUND(2, DESCENDING),
TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING);
TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING),
RACE_TOTAL_POINTS(2, ASCENDING), REGATTA_TOTAL_POINTS(2, ASCENDING);
private int precision;
@@ -0,0 +1,35 @@
package com.sap.sailing.domain.common;
public enum LeaderboardType {
RegattaLeaderboard(false, true), RegattaMetaLeaderboard(true, true),
FlexibleLeaderboard(false, false), FlexibleMetaLeaderboard(true, false);
boolean isRegattaLeaderboard;
boolean isMetaLeaderboard;
LeaderboardType(boolean isMetaLeaderboard, boolean isRegattaLeaderboard) {
this.isMetaLeaderboard = isMetaLeaderboard;
this.isRegattaLeaderboard = isRegattaLeaderboard;
}
public boolean isMetaLeaderboard() {
return isMetaLeaderboard;
}
public boolean isRegattaLeaderboard() {
return isRegattaLeaderboard;
}
public boolean isFlexibleLeaderboard() {
return !isRegattaLeaderboard;
}
public String toString() {
String result = "";
if(isMetaLeaderboard) {
result += "Meta, ";
}
result += isRegattaLeaderboard ? "Regatta" : "Flexible";
return result;
}
}
@@ -18,8 +18,7 @@ public class LeaderboardPanelMock extends LeaderboardPanel {
String leaderboardName, ErrorReporter errorReporter,
StringMessages stringConstants) {
super(sailingService, null, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false,
/* showMetaLeaderboardsOnSamePage */ false), new CompetitorSelectionModel(
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false), new CompetitorSelectionModel(
/* hasMultiSelection */true), leaderboardName, errorReporter, stringConstants, null, /* showRaceDetails */ true);
}
@@ -0,0 +1,209 @@
.gwt-SplitLayoutPanel-HDragger {
width: 20px;
}
.gwt-ToggleButton {
background: url(images/btn-cta-orangeleft.png) center top repeat-x;
padding: 8px;
margin: 0 11px 0 0;
border: 0;
}
.gwt-ToggleButton-down {
background: url(images/btn-cta-orangeleft.png) center -30px repeat-x;
color: #fff;
}
.gwt-SplitLayoutPanel-HDragger {
width: 12px;
}
.gwt-SplitLayoutPanel-HDragger {
background: #cbcaca url(images/thumb_vertical.png) center center no-repeat;
cursor: col-resize;
}
.gwt-SplitLayoutPanel-VDragger {
background: #cbcaca url(images/thumb_horz.png) center center no-repeat;
cursor: row-resize;
}
.gwt-ToggleButton-up-hovering, .gwt-ToggleButton-down-hovering {
background: url(images/btn-cta-orangeleft-down.png) center top repeat-x;
color: #fff;
}
.gwt-MenuBarPopup .menuPopupTopLeft {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupTopCenter {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupTopCenter {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupTopRight {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupMiddleLeft {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupMiddleRight {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupMiddleRight {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupBottomCenter {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupBottomRight {
background: none !important;
}
.gwt-MenuBarPopup .menuPopupBottomLeft {
background: none !important;
}
.gwt-MenuBar-vertical .gwt-MenuItem {
padding: 10px;
padding-bottom: 5px;
padding-top: 5px;
}
.gwt-MenuBar .gwt-MenuItem-selected {
background: none repeat scroll 0 0 #efaa00;
}
.gwt-DialogBox {
-webkit-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);
-moz-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);
box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);
line-height: 1.4;
}
.gwt-DialogBox .Caption {
font-size: 14px;
padding: 14px 10px;
background: url(images/dialogBox-bg.png) 0 0 repeat-x;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topleft: 5px;
-moz-border-radius-topright: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
font-family: UbuntuBold, Verdana, Arial, sans-serif;
}
.gwt-DialogBox .dialogContent {
background: #ffffff;
padding: 10px;
border: 10px solid #a6a6a6;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-bottomright: 5px;
-moz-border-radius-bottomleft: 5px;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}
.gwt-Button, .gwt-Button:visited {
font-family: UbuntuBold, Verdana, Arial, sans-serif;
border: none;
background: url(images/btn-cta-orangeleft.png) center top repeat-x;
display: inline-block;
padding: 2px 10px 4px;
color: #fff;
text-decoration: none;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
cursor: pointer;
height: 24px;
white-space: nowrap;
}
.gwt-Button:active {
top: 1px;
position: relative;
border: none;
}
.gwt-Button:hover {
background: url(images/btn-cta-orangeleft.png) center -30px repeat-x;
cursor: pointer;
}
.gwt-Button[disabled] {
cursor: default;
color: #888;
background: #F1F1F1;
border: 1px solid #919090;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
border: none;
text-shadow: none;
}
.gwt-Button[disabled]:hover {
cursor: default;
color: #888;
background: #F1F1F1;
}
.gwt-Button[disabled]:active {
cursor: default;
color: #888;
background: #F1F1F1;
top: 0;
}
.gwt-ButtonLarge
.gwt-ButtonMedium
.gwt-ButtonSmall
.gwt-DialogBox .boxPanel {
padding: 0 0 10px 0;
}
.gwt-DialogBox .boxPanel:after {
display: inline-block;
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.gwt-DialogBox .settingsDialogLabel {
float: left;
padding: 3px 6px 0 0;
}
.gwt-DialogBox .settingsDialogValue {
float: right;
}
.gwt-DialogBox .additionalWidgets {
padding: 5px 0 0 0;
text-align: right;
}
.gwt-DialogBox .gwt-Label {
line-height: 1.4;
}
+1 -4
View File
@@ -1,5 +1,6 @@
@import url(fontface.css);
@import url(CommonControls.css);
@import url(ViewerToolbar.css);
/** Add css rules here for your application. */
/*
@@ -47,10 +48,6 @@ h1 {
table {
border-collapse: separate; }
.leaderboardMargin {
margin: 5px;
}
.leaderboardContent {
}
@@ -0,0 +1,132 @@
.viewerToolbar {
font-family: 'UbuntuLight', Arial, Verdana, sans-serif;
height: 30px;
margin-top: 10px;
}
.viewerToolbar-toolbaritem {
color: #dddcdc;
text-transform: uppercase;
font-weight: 500;
margin: 0 12px 0 0;
padding: 0 0 5px 0;
}
.viewerToolbar-toolbaritem:hover {
border-bottom: 4px solid #f0ab00;
color: #dddcdc;
text-decoration: none;
}
.viewerToolbar-toolbaritem:active {
border-bottom: 4px solid #f0ab00;
color: #dddcdc;
text-decoration: none;
}
.viewerToolbar-settingsButtonPanel {
float: right;
margin: 9px 12px 0 0;
}
.viewerToolbar-settingsButton {
background: url(images/settings_white.png), url(images/btn-cta-orangeleft.png);
background-position: center, center 0;
background-repeat: no-repeat, repeat-x;
width: 27px;
height: 23px;
margin: 0 12px 0 1px;
-webkit-border-radius: 0px 3px 3px 0px;
border-radius: 0px 3px 3px 0px;
}
.viewerToolbar-settingsButton[disabled] {
background: url(images/settings_white.png) #ababab;
background-position: center;
background-repeat: no-repeat;
}
.viewerToolbar-settingsButton:hover {
background-image: url(images/settings_white.png), url(images/btn-cta-orangeleft.png);
background-position: center, center -30px;
background-repeat: no-repeat, repeat-x;
cursor: pointer;
}
.viewerToolbar-settingsButton[disabled]:hover {
background: url(images/settings_white.png) #ababab;
background-position: center;
background-repeat: no-repeat;
cursor: normal;}
.viewerToolbar-innerElement {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center 0 transparent;
border-bottom-left-radius: 3px;
border-top-left-radius: 3px;
color: #FFFFFF;
cursor: pointer;
font-family: UbuntuBold,Verdana,Arial,sans-serif;
font-size: 14px;
margin: 0 0 0 0;
padding: 2px 7px 2px 3px;
height: 18px;
display: inline-block;
text-decoration: none;
-moz-border-radius: 5px 0 0 5px;
-webkit-border-radius: 5px 0 0 5px;
border-radius: 5px 0 0 5px;
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
}
.viewerToolbar-innerElement:active {
top: 1px;
position: relative;
}
.viewerToolbar-innerElement:hover {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center -30px transparent;
cursor: pointer;
}
.viewerToolbar-innerElement label {
cursor: pointer;
}
.viewerToolbar-standaloneElement {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center 0 transparent;
color: #FFFFFF;
cursor: pointer;
font-family: UbuntuBold,Verdana,Arial,sans-serif;
font-size: 14px;
margin: 0 0 0 0;
padding: 2px 7px 2px 3px;
height: 23px;
display: inline-block;
text-decoration: none;
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25);
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
border-radius: 5px 5px 5px 5px;
-moz-border-radius: 5px 0 0 5px;
-webkit-border-radius: 5px 5px 5px 5px;
}
.viewerToolbar-standaloneElement:active {
top: 1px;
position: relative;
}
.viewerToolbar-standaloneElement:hover {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center -30px transparent;
cursor: pointer;
}
.viewerToolbar-standaloneElement label {
cursor: pointer;
}
+5 -3
View File
@@ -28,9 +28,9 @@ bin.includes = META-INF/,\
com.sap.sailing.gwt.ui.RaceBoard/,\
highcharts/,\
Spectator.css,\
fontface.css,\
fontface.css,\
webfonts/,\
RegattaOverview.css,\
RegattaOverview.css,\
RegattaOverview.html,\
com.sap.sailing.gwt.ui.RegattaOverview/,\
com.sap.sailing.gwt.ui.VideoPopup/,\
@@ -42,5 +42,7 @@ bin.includes = META-INF/,\
com.sap.sailing.gwt.ui.TvView/,\
BrowserUnsupported.html,\
PolarSheets.css,\
PolarSheets.html
PolarSheets.html,\
ViewerToolbar.css,\
CommonGwtControls.css
output.. = WEB-INF/classes/
@@ -0,0 +1,32 @@
package com.sap.sailing.gwt.ui.actions;
import java.util.Date;
import java.util.List;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
public class GetLeaderboardDataEntriesAction extends DefaultAsyncAction<List<Triple<String, List<CompetitorDTO>, List<Double>>>> {
private final SailingServiceAsync sailingService;
private final DetailType detailType;
private final String leaderboardName;
private final Date date;
public GetLeaderboardDataEntriesAction(SailingServiceAsync sailingService, String leaderboardName, Date date, DetailType detailType,
AsyncCallback<List<Triple<String, List<CompetitorDTO>, List<Double>>>> callback) {
super(callback);
this.sailingService = sailingService;
this.leaderboardName = leaderboardName;
this.date = date;
this.detailType = detailType;
}
@Override
public void execute(AsyncActionsExecutor asyncActionsExecutor) {
sailingService.getLeaderboardDataEntriesForAllRaceColumns(leaderboardName, date, detailType,
(AsyncCallback<List<Triple<String, List<CompetitorDTO>, List<Double>>>>) getWrapperCallback(asyncActionsExecutor));
}
}
@@ -228,11 +228,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
TextColumn<StrippedLeaderboardDTO> leaderboardTypeColumn = new TextColumn<StrippedLeaderboardDTO>() {
@Override
public String getValue(StrippedLeaderboardDTO leaderboard) {
String result = leaderboard.isRegattaLeaderboard ? "Regatta" : "Flexible";
if(leaderboard.isMetaLeaderboard) {
result += " , Meta";
}
return result;
return leaderboard.type.toString();
}
};
@@ -264,10 +260,11 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
List<StrippedLeaderboardDTO> otherExistingLeaderboard = new ArrayList<StrippedLeaderboardDTO>();
otherExistingLeaderboard.addAll(availableLeaderboardList);
otherExistingLeaderboard.remove(leaderboardDTO);
if (leaderboardDTO.isMetaLeaderboard) {
if (leaderboardDTO.type.isMetaLeaderboard()) {
Window.alert("This is a meta leaderboard. It can't be changed here.");
} else {
if (leaderboardDTO.isRegattaLeaderboard) {
if (leaderboardDTO.type.isRegattaLeaderboard()) {
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name,
leaderboardDTO.displayName, null, leaderboardDTO.discardThresholds, leaderboardDTO.regattaName, leaderboardDTO.defaultCourseAreaIdAsString);
AbstractLeaderboardDialog dialog = new RegattaLeaderboardEditDialog(Collections
@@ -411,7 +408,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
new DisablableCheckboxCell(new IsEnabled() {
@Override
public boolean isEnabled() {
return getSelectedLeaderboard() != null && !getSelectedLeaderboard().isRegattaLeaderboard;
return getSelectedLeaderboard() != null && !getSelectedLeaderboard().type.isRegattaLeaderboard();
}
})) {
@Override
@@ -480,7 +477,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
addRaceColumnsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (getSelectedLeaderboard().isRegattaLeaderboard) {
if (getSelectedLeaderboard().type.isRegattaLeaderboard()) {
Window.alert(stringMessages.cannotAddRacesToRegattaLeaderboardButOnlyToRegatta());
} else {
addRaceColumnsToLeaderboard();
@@ -771,7 +768,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
}
existingRacesWithoutThisRace.remove(raceColumnWithFleet.getA());
final RaceColumnInLeaderboardDialog raceDialog = new RaceColumnInLeaderboardDialog(existingRacesWithoutThisRace,
raceColumnWithFleet.getA(), getSelectedLeaderboard().isRegattaLeaderboard, stringMessages, new DialogCallback<RaceColumnDescriptor>() {
raceColumnWithFleet.getA(), getSelectedLeaderboard().type.isRegattaLeaderboard(), stringMessages, new DialogCallback<RaceColumnDescriptor>() {
@Override
public void cancel() {
}
@@ -906,12 +903,12 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
}
selectedLeaderBoardPanel.setVisible(true);
selectedLeaderBoardPanel.setCaptionText("Details of leaderboard '" + getSelectedLeaderboard().name + "'");
if (!getSelectedLeaderboard().isMetaLeaderboard) {
if (!getSelectedLeaderboard().type.isMetaLeaderboard()) {
trackedRacesCaptionPanel.setVisible(true);
}
addRaceColumnsButton.setVisible(!getSelectedLeaderboard().isRegattaLeaderboard);
columnMoveUpButton.setVisible(!getSelectedLeaderboard().isRegattaLeaderboard);
columnMoveDownButton.setVisible(!getSelectedLeaderboard().isRegattaLeaderboard);
addRaceColumnsButton.setVisible(!getSelectedLeaderboard().type.isRegattaLeaderboard());
columnMoveUpButton.setVisible(!getSelectedLeaderboard().type.isRegattaLeaderboard());
columnMoveDownButton.setVisible(!getSelectedLeaderboard().type.isRegattaLeaderboard());
} else {
selectedLeaderBoardPanel.setVisible(false);
trackedRacesCaptionPanel.setVisible(false);
@@ -4,8 +4,10 @@ import java.util.ArrayList;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.components.ImagesBarCell;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell {
public static final String ACTION_REMOVE = "ACTION_REMOVE";
@@ -26,8 +28,8 @@ public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell {
List<ImageSpec> result = new ArrayList<ImageSpec>();
result.add(new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())));
result.add(new ImageSpec(ACTION_UNLINK, stringMessages.actionRaceUnlink(), makeImagePrototype(resources.unlinkIcon())));
if (selectedLeaderboardProvider.getSelectedLeaderboard() != null &&
!selectedLeaderboardProvider.getSelectedLeaderboard().isRegattaLeaderboard) {
StrippedLeaderboardDTO selectedLeaderboard = selectedLeaderboardProvider.getSelectedLeaderboard();
if (selectedLeaderboard != null && selectedLeaderboard.type != LeaderboardType.RegattaLeaderboard && selectedLeaderboard.type != LeaderboardType.RegattaMetaLeaderboard) {
// race columns cannot be removed from a regatta leaderboard; they need to be removed from the regatta instead
result.add(new ImageSpec(ACTION_REMOVE, stringMessages.actionRaceRemove(), makeImagePrototype(resources.removeIcon())));
}
@@ -24,6 +24,10 @@ public class DetailTypeFormatter {
return stringMessages.regattaRank();
case OVERALL_RANK:
return stringMessages.overallRank();
case RACE_TOTAL_POINTS:
return stringMessages.totalPoints();
case REGATTA_TOTAL_POINTS:
return stringMessages.totalPoints();
case NUMBER_OF_MANEUVERS:
return stringMessages.numberOfManeuvers();
case CURRENT_SPEED_OVER_GROUND_IN_KNOTS:
@@ -154,6 +158,8 @@ public class DetailTypeFormatter {
case RACE_RANK:
case REGATTA_RANK:
case OVERALL_RANK:
case RACE_TOTAL_POINTS:
case REGATTA_TOTAL_POINTS:
case CURRENT_LEG:
case TACK:
case START_TACK:
@@ -246,6 +252,10 @@ public class DetailTypeFormatter {
return stringMessages.regattaRankTooltip();
case OVERALL_RANK:
return stringMessages.overallRankTooltip();
case RACE_TOTAL_POINTS:
return stringMessages.raceTotalPointsTooltip();
case REGATTA_TOTAL_POINTS:
return stringMessages.regattaTotalPointsTooltip();
case RANK_GAIN:
return stringMessages.rankGainTooltip();
case TACK:
@@ -12,6 +12,7 @@ import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.NoWindException;
@@ -119,7 +120,7 @@ public interface SailingService extends RemoteService {
void removeWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO windDTO);
public List<String> getLeaderboardNames() throws Exception;
public List<String> getLeaderboardNames();
LeaderboardDTO getLeaderboardByName(String leaderboardName, Date date,
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails) throws Exception;
@@ -282,7 +283,8 @@ public interface SailingService extends RemoteService {
List<SwissTimingReplayRaceDTO> listSwissTiminigReplayRaces(String swissTimingUrl);
List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException;
List<Triple<String, List<CompetitorDTO>, List<Double>>> getLeaderboardDataEntriesForAllRaceColumns(String leaderboardName,
Date date, DetailType detailType) throws Exception;
List<String> getOverallLeaderboardNamesContaining(String leaderboardName);
@@ -303,4 +305,8 @@ public interface SailingService extends RemoteService {
List<RegattaOverviewEntryDTO> getRegattaOverviewEntriesForEvent(String eventIdAsString);
EventDTO getEventByIdAsString(String eventIdAsString);
List<Pair<String, String>> getLeaderboardsNamesOfMetaleaderboard(String metaLeaderboardName);
Pair<String, LeaderboardType> checkLeaderboardName(String leaderboardName);
}
@@ -11,6 +11,7 @@ import java.util.Set;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.PolarSheetGenerationTriggerResponse;
@@ -428,9 +429,6 @@ public interface SailingServiceAsync {
void listSwissTiminigReplayRaces(String swissTimingUrl, AsyncCallback<List<SwissTimingReplayRaceDTO>> asyncCallback);
void getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date,
AsyncCallback<List<Pair<String, List<CompetitorDTO>>>> callback);
void getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
long stepSize, DetailType detailType, String leaderboarGroupName, String leaderboardName, AsyncCallback<CompetitorsRaceDataDTO> callback);
@@ -456,6 +454,14 @@ public interface SailingServiceAsync {
void getRegattaOverviewEntriesForEvent(String eventIdAsString, AsyncCallback<List<RegattaOverviewEntryDTO>> asyncCallback);
void getEventByIdAsString(String eventIdAsString, AsyncCallback<EventDTO> asyncCallback);
void getLeaderboardDataEntriesForAllRaceColumns(String leaderboardName, Date date, DetailType detailType,
AsyncCallback<List<Triple<String, List<CompetitorDTO>, List<Double>>>> callback);
void getLeaderboardsNamesOfMetaleaderboard(String metaLeaderboardName,
AsyncCallback<List<Pair<String, String>>> callback);
void checkLeaderboardName(String leaderboardName, AsyncCallback<Pair<String, LeaderboardType>> callback);
}
@@ -156,7 +156,6 @@ public interface StringMessages extends Messages {
String in();
String secondsUnit();
String metersUnit();
String failedToLoadRaceData();
String close();
String compareCompetitors();
String description();
@@ -437,11 +436,14 @@ public interface StringMessages extends Messages {
String legend();
String live();
String swissTimingArchiveConnector();
String errorFetchingRankingChartData(String message);
String errorFetchingChartData(String message);
String loadingCompetitorData();
String rankNAfterRace(long l, String string);
String afterRace();
String competitorRegattaDataAfterRaceN(String detailType, String raceColumnName);
String showRankChart();
String rankChart();
String totalPoints();
String raceTotalPointsTooltip();
String regattaTotalPointsTooltip();
String overallLeaderboardSelection();
String showOverallLeaderboardOnSamePage();
String errorTryingToObtainOverallLeaderboards(String message);
@@ -171,7 +171,6 @@ distance=Distance
in=in
secondsUnit=s
metersUnit=m
failedToLoadRaceData=Failed to laod race data.
close=Close
compareCompetitors=Compare competitors
description=Description
@@ -475,10 +474,13 @@ pattern=Pattern
legend=Legend
live=live
swissTimingArchiveConnector=SwissTiming Archived Events
errorFetchingRankingChartData=Error fetching ranking chart data: {0}
errorFetchingChartData=Error fetching chart data: {0}
loadingCompetitorData=Loading competitor data...
rankNAfterRace=Rank {0} after race {1}
afterRace=after race
competitorRegattaDataAfterRaceN={0} after race {1}
rankChart=Ranking
totalPoints=Total points
raceTotalPointsTooltip=Competitor''s total points in the race
regattaTotalPointsTooltip=Competitor''s total points in the regatta
showRankChart=Show rank chart
overallLeaderboardSelection=Overall Leaderboard
showOverallLeaderboardOnSamePage=Show overall leaderboard on same page
@@ -173,7 +173,6 @@ distance=Strecke
in=in
secondsUnit=s
metersUnit=m
failedToLoadRaceData=Renndaten konnten nicht geladen werden.
close=Schließen
compareCompetitors=Teilnehmer vergleichen
description=Beschreibung
@@ -476,10 +475,13 @@ pattern=Muster
legend=Legende
live=live
swissTimingArchiveConnector=SwissTiming Historische Veranstaltungen
errorFetchingRankingChartData=Fehler beim Laden der Daten für das Ranglisten-Chart: {0}
errorFetchingChartData=Fehler beim Laden der Chartdaten: {0}
loadingCompetitorData=Lade Teilnehmer-Daten
rankNAfterRace=Rang {0} nach Rennen {1}
afterRace=nach Rennen
competitorRegattaDataAfterRaceN={0} nach Rennen {1}
rankChart=Ranking
totalPoints=Gesamtpunktzahl
raceTotalPointsTooltip=Punktzahl des Teilnehmers in dem Rennen
regattaTotalPointsTooltip=Punktzahl des Teilnehmers in der Regatta
showRankChart=Rang-Diagramm anzeigen
overallLeaderboardSelection=Gesamtergebnisse
showOverallLeaderboardOnSamePage=Gesamtergebnisse auf derselben Seite anzeigen
@@ -0,0 +1,378 @@
package com.sap.sailing.gwt.ui.client.shared.charts;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.moxieapps.gwt.highcharts.client.Axis;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.ChartTitle;
import org.moxieapps.gwt.highcharts.client.Color;
import org.moxieapps.gwt.highcharts.client.Credits;
import org.moxieapps.gwt.highcharts.client.Point;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.ToolTip;
import org.moxieapps.gwt.highcharts.client.ToolTipData;
import org.moxieapps.gwt.highcharts.client.ToolTipFormatter;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter;
import org.moxieapps.gwt.highcharts.client.labels.XAxisLabels;
import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker.Symbol;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.actions.GetLeaderboardDataEntriesAction;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.DetailTypeFormatter;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeListener;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
import com.sap.sailing.gwt.ui.client.shared.components.AbstractLazyComponent;
import com.sap.sailing.gwt.ui.client.shared.components.Component;
/**
* A base class for a leaderboard chart showing competitor data for all race columns of a leaderboard.
*/
public abstract class AbstractCompetitorLeaderboardChart<SettingsType> extends AbstractLazyComponent<SettingsType> implements Component<SettingsType>,
CompetitorSelectionChangeListener, RequiresResize, TimeListener {
private static final int LINE_WIDTH = 1;
protected final CompetitorSelectionProvider competitorSelectionProvider;
protected final Map<CompetitorDTO, Series> competitorSeries;
protected Chart chart;
protected final Timer timer;
private DetailType selectedDetailType;
private final SailingServiceAsync sailingService;
private final AsyncActionsExecutor asyncActionsExecutor;
private final ErrorReporter errorReporter;
private final List<String> raceColumnNames;
private final List<String> raceColumnNamesWithData;
protected final String leaderboardName;
protected final StringMessages stringMessages;
public AbstractCompetitorLeaderboardChart(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, String leaderboardName,
DetailType detailType, CompetitorSelectionProvider competitorSelectionProvider, Timer timer,
final StringMessages stringMessages, ErrorReporter errorReporter) {
this.sailingService = sailingService;
this.asyncActionsExecutor = asyncActionsExecutor;
this.competitorSelectionProvider = competitorSelectionProvider;
this.stringMessages = stringMessages;
this.errorReporter = errorReporter;
this.timer = timer;
this.leaderboardName = leaderboardName;
this.selectedDetailType = detailType;
competitorSeries = new HashMap<CompetitorDTO, Series>();
raceColumnNames = new ArrayList<String>();
raceColumnNamesWithData = new ArrayList<String>();
}
@Override
public Widget createWidget() {
setSize("100%", "100%");
chart = createChart();
setSelectedDetailType(selectedDetailType);
timer.addTimeListener(this);
competitorSelectionProvider.addCompetitorSelectionChangeListener(this);
return chart;
}
protected boolean isCompetitorVisible(CompetitorDTO competitor) {
return Util.isEmpty(competitorSelectionProvider.getSelectedCompetitors()) || competitorSelectionProvider.isSelected(competitor);
}
private Chart createChart() {
Chart chart = new Chart()
.setPersistent(true)
.setMarginLeft(40)
.setMarginRight(40)
.setWidth100()
.setBorderColor(new Color("#CACACA"))
.setBorderWidth(1)
.setBorderRadius(0)
.setBackgroundColor(new Color("#EBEBEB"))
.setPlotBorderWidth(0)
.setCredits(new Credits().setEnabled(false))
.setChartTitle(new ChartTitle().setText(stringMessages.rank()))
.setLinePlotOptions(new LinePlotOptions().setLineWidth(LINE_WIDTH).setMarker(
new Marker().setEnabled(true).setHoverState(
new Marker().setEnabled(true).setRadius(4))).setShadow(false)
.setHoverStateLineWidth(LINE_WIDTH));
ChartUtil.useCheckboxesToShowAndHide(chart);
chart.getXAxis().setType(Axis.Type.LINEAR);
chart.getXAxis().setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() {
@Override
public String format(AxisLabelsData axisLabelsData) {
Double valueAsDouble = axisLabelsData.getValueAsDouble();
return raceColumnNamesWithData.get(valueAsDouble.intValue());
}
}));
chart.getYAxis().setAllowDecimals(false).setStartOnTick(true).setEndOnTick(true);
chart.getYAxis().setShowFirstLabel(false).setShowLastLabel(false);
// chart.getYAxis().setMinorTickInterval(null).setTickInterval(1).setTickPixelInterval(100);
chart.getXAxis().setTickInterval(1);
return chart;
}
protected void setSelectedDetailType(DetailType newSelectedDetailType) {
boolean oldReversedYAxis = hasReversedYAxis(this.selectedDetailType);
this.selectedDetailType = newSelectedDetailType;
// TODO There is a bug in the highcharts library which prevents to change the reverse property of the YAxis
// Because we need this functionality we need to recreate the chart each time the YAxis changes
if (oldReversedYAxis != hasReversedYAxis(selectedDetailType)) {
chart = createChart();
}
chart.setTitle(new ChartTitle().setText(DetailTypeFormatter.format(selectedDetailType)), null);
final String unit = DetailTypeFormatter.getUnit(getSelectedDetailType());
final String label = unit.isEmpty() ? "" : "[" + unit + "]";
chart.getYAxis().setAxisTitleText(label);
chart.getYAxis().setReversed(isYAxisReversed());
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
@Override
public String format(ToolTipData toolTipData) {
String seriesName = toolTipData.getSeriesName();
Double xValue = toolTipData.getXAsDouble();
Double yValue = toolTipData.getYAsDouble();
String raceColumnName = raceColumnNamesWithData.get(xValue.intValue());
NumberFormat numberFormat = DetailTypeFormatter.getNumberFormat(selectedDetailType);
return "<b>" + seriesName + ":</b> " + " " +
stringMessages.competitorRegattaDataAfterRaceN(numberFormat.format(yValue), raceColumnName);
}
}));
}
private boolean hasReversedYAxis(DetailType detailType) {
return detailType == DetailType.REGATTA_RANK;
}
private boolean isYAxisReversed() {
return hasReversedYAxis(selectedDetailType);
}
protected void clearChart() {
competitorSeries.clear();
chart.removeAllSeries();
}
protected Series getOrCreateSeries(CompetitorDTO competitor) {
Series result = competitorSeries.get(competitor);
if (result == null) {
result = chart.createSeries().setType(Series.Type.LINE).setName(competitor.name);
result.setPlotOptions(new LinePlotOptions()
.setLineWidth(LINE_WIDTH)
.setMarker(new Marker().setEnabled(true).setRadius(4).setSymbol(Symbol.DIAMOND))
.setShadow(true).setHoverStateLineWidth(LINE_WIDTH)
.setColor(competitorSelectionProvider.getColor(competitor)).setSelected(true));
competitorSeries.put(competitor, result);
}
return result;
}
@Override
public void onResize() {
chart.setSizeToMatchContainer();
// it's important here to recall the redraw method, otherwise the bug fix for wrong checkbox positions (nativeAdjustCheckboxPosition)
// in the BaseChart class would not be called
chart.redraw();
}
@Override
public void timeChanged(Date date) {
if(!isVisible()) {
return;
}
loadChartData(timer.getPlayMode() == PlayModes.Live ? null : date);
}
protected void loadChartData(Date date) {
if(timer.getPlayMode() != PlayModes.Live) {
chart.showLoading(stringMessages.loadingCompetitorData());
}
GetLeaderboardDataEntriesAction getLeaderboardDataEntriesAction = new GetLeaderboardDataEntriesAction(sailingService,
leaderboardName, /* date */ null, selectedDetailType, new AsyncCallback<List<Triple<String, List<CompetitorDTO>, List<Double>>>>() {
@Override
public void onSuccess(List<Triple<String, List<CompetitorDTO>, List<Double>>> result) {
List<Series> chartSeries = new ArrayList<Series>(Arrays.asList(chart.getSeries()));
chart.hideLoading();
setWidget(chart);
raceColumnNames.clear();
switch(selectedDetailType) {
case REGATTA_RANK:
fillTotalRanksSeries(result, chartSeries);
break;
case RACE_TOTAL_POINTS:
fillTotalPointsSeries(result, chartSeries);
break;
default:
break;
}
chart.setSizeToMatchContainer();
// it's important here to recall the redraw method, otherwise the bug fix for wrong checkbox
// positions (nativeAdjustCheckboxPosition)
// in the BaseChart class would not be called
chart.redraw();
}
@Override
public void onFailure(Throwable caught) {
chart.hideLoading();
errorReporter.reportError(stringMessages.errorFetchingChartData(caught.getMessage()),
timer.getPlayMode() == PlayModes.Live);
}
});
asyncActionsExecutor.execute(getLeaderboardDataEntriesAction);
}
private void fillTotalRanksSeries(List<Triple<String, List<CompetitorDTO>, List<Double>>> result, List<Series> chartSeries) {
Set<Series> unusedSeries = new HashSet<Series>(competitorSeries.values());
for (Series series : competitorSeries.values()) {
for (Point p : new ArrayList<Point>(Arrays.asList(series.getPoints()))) {
series.removePoint(p, /* redraw */false, /* animation */false);
}
}
int raceColumnNumber = 0;
int maxCompetitorCount = 0;
for (Triple<String, List<CompetitorDTO>, List<Double>> entry : result) {
List<Double> dataValues = entry.getC();
raceColumnNames.add(entry.getA());
if(hasValidValues(dataValues)) {
raceColumnNamesWithData.add(entry.getA());
int index = 0;
maxCompetitorCount = Math.max(maxCompetitorCount, entry.getB().size());
for (CompetitorDTO competitor : entry.getB()) {
if (isCompetitorVisible(competitor)) {
Series series = getOrCreateSeries(competitor);
Double dataValue = dataValues.get(index);
if (dataValue != null) {
series.addPoint(raceColumnNumber, dataValue, /* redraw */false, /* shift */false, /* animation */ false);
}
unusedSeries.remove(series);
if (!chartSeries.contains(series)) {
chart.addSeries(series);
chartSeries.add(series);
}
}
index++;
}
raceColumnNumber++;
}
}
chart.getYAxis().setMaxPadding(0.5 / maxCompetitorCount).setMinPadding(0.5 / maxCompetitorCount);
chart.getYAxis().setTickInterval(1.0);
chart.setHeight(maxCompetitorCount * 30 + 100 + "px");
}
private void fillTotalPointsSeries(List<Triple<String, List<CompetitorDTO>, List<Double>>> result, List<Series> chartSeries) {
Double maxTotalPoints = 0.0;
Set<Series> unusedSeries = new HashSet<Series>(competitorSeries.values());
for (Series series : competitorSeries.values()) {
for (Point p : new ArrayList<Point>(Arrays.asList(series.getPoints()))) {
series.removePoint(p, /* redraw */ false, /* animation */ false);
}
}
int raceColumnNumber = 0;
int maxCompetitorCount = 0;
Map<CompetitorDTO, Double> sumTotalPointsMap = new HashMap<CompetitorDTO, Double>();
for (Triple<String, List<CompetitorDTO>, List<Double>> entry : result) {
List<Double> dataValues = entry.getC();
raceColumnNames.add(entry.getA());
if(hasValidValues(dataValues)) {
raceColumnNamesWithData.add(entry.getA());
int index = 0;
maxCompetitorCount = Math.max(maxCompetitorCount, entry.getB().size());
for (CompetitorDTO competitor : entry.getB()) {
if (isCompetitorVisible(competitor)) {
Series series = getOrCreateSeries(competitor);
Double dataValue = dataValues.get(index);
if(dataValue != null) {
Double sumTotalPoints = dataValue;
if(sumTotalPointsMap.containsKey(competitor)) {
sumTotalPoints += sumTotalPointsMap.get(competitor);
}
sumTotalPointsMap.put(competitor, sumTotalPoints);
series.addPoint(raceColumnNumber, sumTotalPoints, /* redraw */ false, /* shift */ false, /* animation */ false);
if(sumTotalPoints > maxTotalPoints) {
maxTotalPoints = sumTotalPoints;
}
}
unusedSeries.remove(series);
if (!chartSeries.contains(series)) {
chart.addSeries(series);
chartSeries.add(series);
}
}
index++;
}
raceColumnNumber++;
}
}
chart.getYAxis().setMaxPadding(0.5/maxCompetitorCount).setMinPadding(0.5/maxCompetitorCount);
chart.getYAxis().setTickInterval(5.0);
chart.setHeight(maxCompetitorCount * 30 + 100 + "px");
}
private boolean hasValidValues(List<Double> values) {
boolean result = false;
for(Double value: values) {
if(value != null) {
result = true;
break;
}
}
return result;
}
@Override
public Widget getEntryWidget() {
return this;
}
@Override
public void competitorsListChanged(Iterable<CompetitorDTO> competitors) {
timeChanged(timer.getTime());
}
@Override
public void addedToSelection(CompetitorDTO competitor) {
timeChanged(timer.getTime());
}
@Override
public void removedFromSelection(CompetitorDTO competitor) {
timeChanged(timer.getTime());
}
protected DetailType getSelectedDetailType() {
return selectedDetailType;
}
}
@@ -58,7 +58,7 @@ import com.sap.sailing.gwt.ui.shared.CompetitorRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
/**
* ChartPanel is a GWT panel that can show one sort of competitor data (e.g. current speed over ground, windward
* AbstractCompetitorChart is a chart that can show one sort of competitor data (e.g. current speed over ground, windward
* distance to leader) for different races in a chart.
*
* When calling the constructor a chart is created that creates a final amount of series (so the maximum number of
@@ -126,12 +126,8 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
}
/**
* Creates a new chart for the given {@link DetailType} <code>dataToShow</code> and also clears the
* {@link #chartData}, the {@link #dataSeriesByCompetitor} and the {@link #markPassingSeriesByCompetitor}.
*
* @param detailType
* The detail type for the new chart.
* @return A chart for the given detail Type
* Creates a new chart.
* Attention: We can't reuse the old chart when the detail changes because HighChart does not support the inverting of the Y-Axis
*/
private Chart createChart() {
Chart chart = new Chart().setZoomType(Chart.ZoomType.X)
@@ -242,10 +238,9 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(
getStringMessages().failedToLoadRaceData() + ": " + caught.toString(),
timer.getPlayMode() == PlayModes.Live);
hideLoading();
errorReporter.reportError(stringMessages.errorFetchingChartData(caught.getMessage()),
timer.getPlayMode() == PlayModes.Live);
}
});
asyncActionsExecutor.execute(getCompetitorsRaceDataAction);
@@ -469,10 +464,6 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
protected DetailType getSelectedDetailType() {
return this.selectedDetailType;
}
private boolean hasReversedYAxis(DetailType detailType) {
return detailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER || detailType == DetailType.GAP_TO_LEADER_IN_SECONDS;
}
/**
* Updates the {@link #selectedDetailType} field, clears the chart for the new <code>selectedDetailType</code> and
@@ -528,12 +519,16 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
return hasDetailTypeChanged;
}
private boolean isYAxisReversed() {
private boolean hasReversedYAxis(DetailType detailType) {
return selectedDetailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER ||
selectedDetailType == DetailType.GAP_TO_LEADER_IN_SECONDS ||
selectedDetailType == DetailType.RACE_RANK ||
selectedDetailType == DetailType.REGATTA_RANK ||
selectedDetailType == DetailType.OVERALL_RANK;
selectedDetailType == DetailType.GAP_TO_LEADER_IN_SECONDS ||
selectedDetailType == DetailType.RACE_RANK ||
selectedDetailType == DetailType.REGATTA_RANK ||
selectedDetailType == DetailType.OVERALL_RANK;
}
private boolean isYAxisReversed() {
return hasReversedYAxis(selectedDetailType);
}
@Override
@@ -1,195 +0,0 @@
package com.sap.sailing.gwt.ui.client.shared.charts;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.moxieapps.gwt.highcharts.client.Axis;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.ChartTitle;
import org.moxieapps.gwt.highcharts.client.Color;
import org.moxieapps.gwt.highcharts.client.Credits;
import org.moxieapps.gwt.highcharts.client.Point;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.ToolTip;
import org.moxieapps.gwt.highcharts.client.ToolTipData;
import org.moxieapps.gwt.highcharts.client.ToolTipFormatter;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter;
import org.moxieapps.gwt.highcharts.client.labels.XAxisLabels;
import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker;
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker.Symbol;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeListener;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
public class LeaderboardRankChart extends SimplePanel implements RequiresResize, TimeListener {
private static final int LINE_WIDTH = 1;
private final CompetitorSelectionProvider competitorSelectionProvider;
private final Map<CompetitorDTO, Series> competitorSeries;
private final Chart chart;
private final List<String> raceColumnNames;
private final Timer timer;
private final String leaderboardName;
private final StringMessages stringMessages;
private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter;
public LeaderboardRankChart(SailingServiceAsync sailingService, String leaderboardName,
CompetitorSelectionProvider competitorSelectionProvider, Timer timer,
final StringMessages stringMessages, ErrorReporter errorReporter, boolean compactChart) {
this.competitorSelectionProvider = competitorSelectionProvider;
this.stringMessages = stringMessages;
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.timer = timer;
timer.addTimeListener(this);
this.leaderboardName = leaderboardName;
competitorSeries = new HashMap<CompetitorDTO, Series>();
raceColumnNames = new ArrayList<String>();
chart = new Chart()
.setPersistent(true)
.setMarginLeft(65)
.setMarginRight(65)
.setWidth100()
.setHeight100()
.setBorderColor(new Color("#CACACA"))
.setBorderWidth(1)
.setBorderRadius(0)
.setBackgroundColor(new Color("#EBEBEB"))
.setPlotBorderWidth(0)
.setCredits(new Credits().setEnabled(false))
.setChartTitle(new ChartTitle().setText(stringMessages.rank()))
.setLinePlotOptions(new LinePlotOptions().setLineWidth(LINE_WIDTH).setMarker(
new Marker().setEnabled(true).setHoverState(
new Marker().setEnabled(true).setRadius(4))).setShadow(false)
.setHoverStateLineWidth(LINE_WIDTH));
ChartUtil.useCheckboxesToShowAndHide(chart);
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
@Override
public String format(ToolTipData toolTipData) {
String seriesName = toolTipData.getSeriesName();
return "<b>" + seriesName + ":</b> " + stringMessages.rankNAfterRace(toolTipData.getYAsLong(),
raceColumnNames.get((int) toolTipData.getXAsLong()));
}
}));
chart.getXAxis().setType(Axis.Type.LINEAR).setAxisTitleText(stringMessages.afterRace());
chart.getXAxis().setTickInterval(1).setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() {
@Override
public String format(AxisLabelsData axisLabelsData) {
return raceColumnNames.get((int) axisLabelsData.getValueAsLong());
}
}));
chart.getYAxis().setAllowDecimals(false).setStartOnTick(true).setEndOnTick(true).setAxisTitleText(stringMessages.rank())
.setShowFirstLabel(false).setShowLastLabel(false).setReversed(true);
if (compactChart) {
chart.setSpacingBottom(10).setSpacingLeft(10).setSpacingRight(10).setSpacingTop(2)
.setOption("legend/margin", 2)
.setOption("title/margin", 5)
.setChartSubtitle(null)
.getXAxis().setAxisTitle(null);
}
setWidget(chart);
setSize("100%", "100%");
// show the loading message only upon initial load
chart.showLoading(stringMessages.loadingCompetitorData());
timeChanged(timer.getTime());
}
private void loadChartData(Date date) {
sailingService.getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(leaderboardName,
/* date: null means "now" or "live" */ null, new AsyncCallback<List<Pair<String,List<CompetitorDTO>>>>() {
@Override
public void onSuccess(List<Pair<String, List<CompetitorDTO>>> result) {
List<Series> chartSeries = new ArrayList<Series>(Arrays.asList(chart.getSeries()));
chart.hideLoading();
raceColumnNames.clear();
Set<Series> unusedSeries = new HashSet<Series>(competitorSeries.values());
for (Series series : competitorSeries.values()) {
for (Point p : new ArrayList<Point>(Arrays.asList(series.getPoints()))) {
series.removePoint(p, /* redraw */ false, /* animation */ false);
}
}
int raceNumber = 0;
int maxCompetitorCount = 0;
for (Pair<String, List<CompetitorDTO>> entry : result) {
raceColumnNames.add(entry.getA());
int rank = 1;
maxCompetitorCount = Math.max(maxCompetitorCount, entry.getB().size());
for (CompetitorDTO competitor : entry.getB()) {
if (Util.isEmpty(competitorSelectionProvider.getSelectedCompetitors()) ||
competitorSelectionProvider.isSelected(competitor)) {
Series series = getOrCreateSeries(competitor);
series.addPoint(raceNumber, rank, /* redraw */ false, /* shift */ false, /* animation */ false);
unusedSeries.remove(series);
if (!chartSeries.contains(series)) {
chart.addSeries(series);
chartSeries.add(series);
}
}
rank++;
}
raceNumber++;
}
chart.getYAxis().setMaxPadding(0.5/maxCompetitorCount).setMinPadding(0.5/maxCompetitorCount);
chart.setSizeToMatchContainer();
// it's important here to recall the redraw method, otherwise the bug fix for wrong checkbox positions (nativeAdjustCheckboxPosition)
// in the BaseChart class would not be called
chart.redraw();
}
@Override
public void onFailure(Throwable caught) {
chart.hideLoading();
errorReporter.reportError(stringMessages.errorFetchingRankingChartData(caught.getMessage()));
}
});
}
private Series getOrCreateSeries(CompetitorDTO competitor) {
Series result = competitorSeries.get(competitor);
if (result == null) {
result = chart.createSeries().setType(Series.Type.LINE).setName(competitor.name);
result.setPlotOptions(new LinePlotOptions()
.setLineWidth(LINE_WIDTH)
.setMarker(new Marker().setEnabled(true).setRadius(4).setSymbol(Symbol.DIAMOND))
.setShadow(true).setHoverStateLineWidth(LINE_WIDTH)
.setColor(competitorSelectionProvider.getColor(competitor)).setSelected(true));
competitorSeries.put(competitor, result);
}
return result;
}
@Override
public void onResize() {
if (!competitorSeries.isEmpty()) {
chart.setSizeToMatchContainer();
// it's important here to recall the redraw method, otherwise the bug fix for wrong checkbox positions (nativeAdjustCheckboxPosition)
// in the BaseChart class would not be called
chart.redraw();
}
}
@Override
public void timeChanged(Date date) {
loadChartData(timer.getPlayMode() == PlayModes.Live ? null : date);
}
}
@@ -0,0 +1,53 @@
package com.sap.sailing.gwt.ui.client.shared.charts;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialogComponent;
public class MultiCompetitorLeaderboardChart extends AbstractCompetitorLeaderboardChart<MultiCompetitorLeaderboardChartSettings> {
private MultiCompetitorLeaderboardChartSettings settings;
public MultiCompetitorLeaderboardChart(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
String leaderboardName, DetailType detailType, CompetitorSelectionProvider competitorSelectionProvider,
Timer timer, StringMessages stringMessages, ErrorReporter errorReporter) {
super(sailingService, asyncActionsExecutor, leaderboardName, detailType, competitorSelectionProvider, timer, stringMessages, errorReporter);
settings = new MultiCompetitorLeaderboardChartSettings(detailType);
}
@Override
public boolean hasSettings() {
return true;
}
@Override
public SettingsDialogComponent<MultiCompetitorLeaderboardChartSettings> getSettingsDialogComponent() {
MultiCompetitorLeaderboardChartSettings chartSettings = new MultiCompetitorLeaderboardChartSettings(settings.getDetailType());
return new MultiCompetitorLeaderboardChartSettingsDialogComponent(chartSettings, stringMessages);
}
@Override
public void updateSettings(MultiCompetitorLeaderboardChartSettings newSettings) {
boolean redraw = false;
if(settings.getDetailType() != newSettings.getDetailType()) {
settings = new MultiCompetitorLeaderboardChartSettings(newSettings.getDetailType());
setSelectedDetailType(newSettings.getDetailType());
redraw = true;
}
if(redraw) {
clearChart();
timeChanged(timer.getTime());
}
}
@Override
public String getLocalizedShortName() {
return stringMessages.competitorCharts();
}
}
@@ -0,0 +1,15 @@
package com.sap.sailing.gwt.ui.client.shared.charts;
import com.sap.sailing.domain.common.DetailType;
public class MultiCompetitorLeaderboardChartSettings {
private final DetailType detailType;
public MultiCompetitorLeaderboardChartSettings(DetailType detailType) {
this.detailType = detailType;
}
public DetailType getDetailType() {
return detailType;
}
}
@@ -0,0 +1,83 @@
package com.sap.sailing.gwt.ui.client.shared.charts;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator;
import com.sap.sailing.gwt.ui.client.DetailTypeFormatter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialogComponent;
public class MultiCompetitorLeaderboardChartSettingsDialogComponent implements SettingsDialogComponent<MultiCompetitorLeaderboardChartSettings> {
private ListBox chartTypeSelectionListBox;
private final DetailType initialDetailType;
private final List<DetailType> availableDetailsTypes;
private final StringMessages stringMessages;
public MultiCompetitorLeaderboardChartSettingsDialogComponent(MultiCompetitorLeaderboardChartSettings settings, StringMessages stringMessages) {
this.initialDetailType = settings.getDetailType();
this.stringMessages = stringMessages;
availableDetailsTypes = new ArrayList<DetailType>();
availableDetailsTypes.add(DetailType.REGATTA_RANK);
availableDetailsTypes.add(DetailType.RACE_TOTAL_POINTS);
}
@Override
public Validator<MultiCompetitorLeaderboardChartSettings> getValidator() {
return new Validator<MultiCompetitorLeaderboardChartSettings>() {
@Override
public String getErrorMessage(MultiCompetitorLeaderboardChartSettings valueToValidate) {
String errorMessage = null;
return errorMessage;
}
};
}
@Override
public Widget getAdditionalWidget(DataEntryDialog<?> dialog) {
VerticalPanel mainPanel = new VerticalPanel();
mainPanel.setSpacing(5);
Label chartSelectionLabel = new Label(stringMessages.chooseChart());
mainPanel.add(chartSelectionLabel);
chartTypeSelectionListBox = dialog.createListBox(/* isMultiSelect */false);
int i = 0;
for (DetailType detailType : availableDetailsTypes) {
chartTypeSelectionListBox.addItem(DetailTypeFormatter.format(detailType), detailType.name());
if (detailType == initialDetailType) {
chartTypeSelectionListBox.setSelectedIndex(i);
}
i++;
}
mainPanel.add(chartTypeSelectionListBox);
return mainPanel;
}
@Override
public MultiCompetitorLeaderboardChartSettings getResult() {
DetailType newDetailType = null;
int selectedIndex = chartTypeSelectionListBox.getSelectedIndex();
String selectedDetailType = chartTypeSelectionListBox.getValue(selectedIndex);
for (DetailType detailType : availableDetailsTypes) {
if (detailType.name().equals(selectedDetailType)) {
newDetailType = detailType;
break;
}
}
return new MultiCompetitorLeaderboardChartSettings(newDetailType);
}
@Override
public FocusWidget getFocusWidget() {
return chartTypeSelectionListBox;
}
}
@@ -13,7 +13,7 @@ import com.sap.sailing.gwt.ui.client.shared.components.Component;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialogComponent;
/**
* MultiChartPanel is a GWT panel that can show competitor data (e.g. current speed over ground, windward distance to
* MultiCompetitorRaceChart is a GWT panel that can show competitor data (e.g. current speed over ground, windward distance to
* leader) for different races in a chart. The chart type can be selected from the settings.
*
* When calling the constructor a chart is created that creates a final amount of series (so the maximum number of
@@ -0,0 +1,13 @@
package com.sap.sailing.gwt.ui.client.shared.components;
import com.google.gwt.user.client.ui.LazyPanel;
import com.google.gwt.user.client.ui.Widget;
public abstract class AbstractLazyComponent<SettingsType> extends LazyPanel implements LazyComponent<SettingsType> {
@Override
public Widget getEntryWidget() {
ensureWidget();
return getWidget();
}
}
@@ -0,0 +1,11 @@
package com.sap.sailing.gwt.ui.client.shared.components;
/**
* Used to describe different view modes for a component viewer
* The view modes are driven by media types.
* @author Frank
*
*/
public enum ComponentViewerViewModes {
WEB_VIEW, TV_VIEW
}
@@ -0,0 +1,7 @@
package com.sap.sailing.gwt.ui.client.shared.components;
import com.google.gwt.user.client.ui.Widget;
public interface LazyComponent<SettingsType> extends Component<SettingsType> {
Widget createWidget();
}
@@ -14,12 +14,14 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.AbstractEntryPoint;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -45,8 +47,8 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
private static final String PARAM_AUTO_EXPAND_LAST_RACE_COLUMN = "autoExpandLastRaceColumn";
private static final String PARAM_REGATTA_NAME = "regattaName";
private static final String PARAM_REFRESH_INTERVAL_MILLIS = "refreshIntervalMillis";
private static final String PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE = "showOverallLeaderboardsOnSamePage";
private static final String PARAM_DELAY_TO_LIVE_MILLIS = "delayToLiveMillis";
private static final String PARAM_SHOW_RANK_CHART = "showRankChart";
/**
* Parameter to support scaling the complete page by a given factor. This works by either using the
@@ -64,6 +66,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
private static final String PARAM_NAME_LAST_N = "lastN";
private String leaderboardName;
private String leaderboardGroupName;
private LeaderboardType leaderboardType;
private GlobalNavigationPanel globalNavigationPanel;
@Override
@@ -75,28 +78,34 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
&& Window.Location.getParameter(PARAM_EMBEDDED).equalsIgnoreCase("true");
final long delayToLiveMillis = Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS) != null ?
Long.valueOf(Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS)) : 5000l; // default 5s
sailingService.getLeaderboardNames(new AsyncCallback<List<String>>() {
@Override
public void onSuccess(List<String> leaderboardNames) {
leaderboardName = Window.Location.getParameter("name");
leaderboardGroupName = Window.Location.getParameter(PARAM_LEADERBOARD_GROUP_NAME);
if (leaderboardNames.contains(leaderboardName)) {
createUI(showRaceDetails, embedded, delayToLiveMillis);
} else {
RootPanel.get().add(new Label(stringMessages.noSuchLeaderboard()));
}
final String zoomTo = Window.Location.getParameter(PARAM_ZOOM_TO);
if (zoomTo != null) {
RootPanel.getBodyElement().setAttribute("style", "zoom: "+zoomTo+";-moz-transform: scale("+zoomTo+");-moz-transform-origin: 0 0;-o-transform: scale("+zoomTo+");-o-transform-origin: 0 0;-webkit-transform: scale("+zoomTo+");-webkit-transform-origin: 0 0;");
}
}
@Override
public void onFailure(Throwable t) {
reportError("Error trying to obtain list of leaderboard names: " + t.getMessage());
}
});
leaderboardName = Window.Location.getParameter("name");
leaderboardGroupName = Window.Location.getParameter(PARAM_LEADERBOARD_GROUP_NAME);
if(leaderboardName != null) {
sailingService.checkLeaderboardName(leaderboardName, new AsyncCallback<Pair<String, LeaderboardType>>() {
@Override
public void onSuccess(Pair<String, LeaderboardType> leaderboardNameAndType) {
if (leaderboardNameAndType != null && leaderboardName.equals(leaderboardNameAndType.getA())) {
leaderboardType = leaderboardNameAndType.getB();
createUI(showRaceDetails, embedded, delayToLiveMillis);
} else {
RootPanel.get().add(new Label(stringMessages.noSuchLeaderboard()));
}
}
@Override
public void onFailure(Throwable t) {
reportError("Error trying to obtain list of leaderboard names: " + t.getMessage());
}
});
} else {
RootPanel.get().add(new Label(stringMessages.noSuchLeaderboard()));
}
final String zoomTo = Window.Location.getParameter(PARAM_ZOOM_TO);
if (zoomTo != null) {
RootPanel.getBodyElement().setAttribute("style", "zoom: "+zoomTo+";-moz-transform: scale("+zoomTo+");-moz-transform-origin: 0 0;-o-transform: scale("+zoomTo+");-o-transform-origin: 0 0;-webkit-transform: scale("+zoomTo+");-webkit-transform-origin: 0 0;");
}
}
private void createUI(boolean showRaceDetails, boolean embedded, long delayToLiveMillis) {
@@ -136,17 +145,28 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
if (leaderboardSettings.getDelayBetweenAutoAdvancesInMilliseconds() != null) {
timer.setPlayMode(PlayModes.Live); // the leaderboard, viewed via the entry point, always goes "live"
}
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(),
leaderboardSettings,
preselectedRace, new CompetitorSelectionModel(
/* hasMultiSelection */true), timer, leaderboardGroupName, leaderboardName,
LeaderboardEntryPoint.this, stringMessages, userAgent, showRaceDetails, /* raceTimesInfoProvider */ null, Window.Location.getParameterMap().containsKey(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN) ?
Boolean.valueOf(Window.Location.getParameterMap().get(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN).get(0)) : false);
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
contentScrollPanel.setWidget(leaderboardPanel);
mainPanel.add(contentScrollPanel);
}
boolean autoExpandLastRaceColumn = Window.Location.getParameterMap().containsKey(
PARAM_AUTO_EXPAND_LAST_RACE_COLUMN) ? Boolean.valueOf(Window.Location.getParameterMap()
.get(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN).get(0)) : false;
boolean showRankChart = Window.Location.getParameterMap().containsKey(
PARAM_SHOW_RANK_CHART) ? Boolean.valueOf(Window.Location.getParameterMap()
.get(PARAM_SHOW_RANK_CHART).get(0)) : false;
Widget leaderboardViewer;
if(leaderboardType.isMetaLeaderboard()) {
leaderboardViewer = new MetaLeaderboardViewer(delayToLiveMillis, sailingService, new AsyncActionsExecutor(),
leaderboardSettings, null, preselectedRace, leaderboardGroupName, leaderboardName, this, stringMessages, userAgent,
showRaceDetails, autoExpandLastRaceColumn, showRankChart);
} else {
leaderboardViewer = new LeaderboardViewer(delayToLiveMillis, sailingService, new AsyncActionsExecutor(),
leaderboardSettings, preselectedRace, leaderboardGroupName, leaderboardName, this, stringMessages, userAgent,
showRaceDetails, autoExpandLastRaceColumn, showRankChart);
}
contentScrollPanel.setWidget(leaderboardViewer);
mainPanel.add(contentScrollPanel);
}
private RaceIdentifier getPreselectedRace(Map<String, List<String>> parameterMap) {
RaceIdentifier result;
@@ -178,9 +198,6 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
} else {
numberOfLastRacesToShow = null;
}
boolean showOverallLeaderboardsOnSamePage = parameterMap.containsKey(PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE) ?
Boolean.valueOf(parameterMap.get(PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE).get(0)) :
false;
if (parameterMap.containsKey(PARAM_RACE_NAME) || parameterMap.containsKey(PARAM_RACE_DETAIL) ||
parameterMap.containsKey(PARAM_LEG_DETAIL) || parameterMap.containsKey(PARAM_MANEUVER_DETAIL) ||
parameterMap.containsKey(PARAM_OVERALL_DETAIL)) {
@@ -199,13 +216,13 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
/* sort by column */ (namesOfRacesToShow != null && !namesOfRacesToShow.isEmpty()) ?
namesOfRacesToShow.get(0) : null, /* ascending */ true,
/* updateUponPlayStateChange */ raceDetails.isEmpty() && legDetails.isEmpty(),
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
raceColumnSelectionStrategy);
} else {
final List<DetailType> overallDetails = Collections.emptyList();
result = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null,
/* overallDetails */ overallDetails, null,
/* autoExpandFirstRace */false, refreshIntervalMillis, numberOfLastRacesToShow,
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
raceColumnSelectionStrategy);
}
return result;
}
@@ -323,7 +340,6 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
+ (settings.isShowRaceDetails() ? "&"+PARAM_SHOW_RACE_DETAILS+"=true" : "")
+ (leaderboardDisplayName != null ? "&displayName="+leaderboardDisplayName : "")
+ (settings.isEmbedded() ? "&"+PARAM_EMBEDDED+"=true" : "")
+ (settings.getLeaderboardSettings().isShowOverallLeaderboardsOnSamePage() ? "&"+PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE+"=true" : "")
+ (settings.getLeaderboardSettings().getDelayInMilliseconds() == null &&
settings.getLeaderboardSettings().getDelayInMilliseconds() != 0 ? "" :
"&"+PARAM_DELAY_TO_LIVE_MILLIS+"="+settings.getLeaderboardSettings().getDelayInMilliseconds())
@@ -29,18 +29,17 @@ import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.user.cellview.client.SafeHtmlHeader;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ImageResourceRenderer;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
@@ -63,7 +62,6 @@ import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction;
import com.sap.sailing.gwt.ui.client.Collator;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.FlagImageResolver;
@@ -79,7 +77,6 @@ import com.sap.sailing.gwt.ui.client.Timer.PlayStates;
import com.sap.sailing.gwt.ui.client.UserAgentDetails;
import com.sap.sailing.gwt.ui.client.shared.components.Component;
import com.sap.sailing.gwt.ui.client.shared.components.IsEmbeddableComponent;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialog;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialogComponent;
import com.sap.sailing.gwt.ui.client.shared.panels.BusyIndicator;
import com.sap.sailing.gwt.ui.client.shared.panels.SimpleBusyIndicator;
@@ -100,7 +97,7 @@ import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
* @author Axel Uhl (D043530)
*
*/
public class LeaderboardPanel extends FormPanel implements TimeListener, PlayStateListener,
public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayStateListener,
Component<LeaderboardSettings>, IsEmbeddableComponent, CompetitorSelectionChangeListener, LeaderboardFetcher {
private static final int RANK_COLUMN_INDEX = 0;
@@ -136,8 +133,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
* obtain the leaderboard contents} from the server. It may change in case the leaderboard is renamed.
*/
private String leaderboardName;
private String leaderboardGroupName;
private final ErrorReporter errorReporter;
@@ -189,15 +184,11 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
protected final String TOTAL_COLUMN_STYLE;
public static final String LEADERBOARD_MARGIN_STYLE = "leaderboardMargin";
private final Timer timer;
private boolean autoExpandPreSelectedRace;
private boolean autoExpandLastRaceColumn;
private boolean showOverallLeaderboardsOnSamePage;
/**
* Remembers whether the auto-expand of the pre-selected race (see {@link #autoExpandPreSelectedRace}) or last
@@ -223,18 +214,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
private final VerticalPanel contentPanel;
/**
* Used to display one or more overall leaderboards if selected. See also
* {@link LeaderboardSettings#isShowOverallLeaderboardsOnSamePage()}. Always contained by the {@link #contentPanel} at the
* right position, but may be empty.
*/
private final VerticalPanel overallLeaderboardsPanel;
/**
* The leaderboard panels displayed in {@link #overallLeaderboardsPanel}.
*/
private final List<LeaderboardPanel> overallLeaderboardPanels;
private final HorizontalPanel refreshAndSettingsPanel;
private final FlowPanel informationPanel;
@@ -292,21 +271,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
*/
private RaceTimesInfoProvider raceTimesInfoProvider;
private final UserAgentDetails userAgent;
private class SettingsClickHandler implements ClickHandler {
private final StringMessages stringMessages;
private SettingsClickHandler(StringMessages stringMessages) {
this.stringMessages = stringMessages;
}
@Override
public void onClick(ClickEvent event) {
new SettingsDialog<LeaderboardSettings>(LeaderboardPanel.this, stringMessages).show();
}
}
protected StringMessages getStringMessages() {
return stringMessages;
}
@@ -442,67 +406,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
getLeaderboardTable().sortColumn(raceColumnByRaceName, /* ascending */true);
}
}
if (newSettings.isShowOverallLeaderboardsOnSamePage() != showOverallLeaderboardsOnSamePage) {
showOverallLeaderboardsOnSamePage = newSettings.isShowOverallLeaderboardsOnSamePage();
if (showOverallLeaderboardsOnSamePage) {
showOverallLeaderboards();
} else {
hideOverallLeaderboards();
}
}
}
private void showOverallLeaderboards() {
overallLeaderboardsPanel.clear();
final Label overallStandingsLabel = new Label(stringMessages.overallStandings());
overallStandingsLabel.setStyleName("leaderboardHeading");
overallLeaderboardsPanel.add(overallStandingsLabel);
updateOverallLeaderboardsAndAddToPanel();
}
/**
* Fetches from the server the list of overall ("meta") leaderboard descriptions in which this main leaderboard occurs and
* creates one {@link LeaderboardPanel} for each such overall leaderboard. Those panels are then added to
* {@link #overallLeaderboardPanels}, registered with the {@link #timer} for common timing with the main leaderboard panel,
* and the panels are made visible by adding them to the {@link #overallLeaderboardsPanel} panel.
*/
private void updateOverallLeaderboardsAndAddToPanel() {
getSailingService().getOverallLeaderboardNamesContaining(leaderboardName, new AsyncCallback<List<String>>() {
@Override
public void onFailure(Throwable caught) {
getErrorReporter().reportError(
stringMessages.errorTryingToObtainOverallLeaderboards(caught.getMessage()));
}
@Override
public void onSuccess(List<String> overallLeaderboardNames) {
for (String overallLeaderboardName : overallLeaderboardNames) {
LeaderboardPanel overallLeaderboardPanel = new LeaderboardPanel(
getSailingService(), asyncActionsExecutor, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
/* namesOfRaceColumnsToShow */ null, /* namesOfRacesToShow */ null, /* nameOfRaceToSort */ null,
/* autoExpandPreSelectedRace */ false, /* showOverallLeaderboardsOnSamePage */ false),
/* preSelectedRace */ null, new CompetitorSelectionModel(/* hasMultiSelection */ true),
timer, leaderboardGroupName, overallLeaderboardName, errorReporter, stringMessages, userAgent,
showRaceDetails, /* optionalRaceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false);
overallLeaderboardPanels.add(overallLeaderboardPanel);
overallLeaderboardsPanel.add(overallLeaderboardPanel);
}
}
});
}
/**
* Clears the panel used to display the overall leaderboards, removes all overall leaderboard panels from their listener
* relationship with the {@link #timer} and then clears the {@link #overallLeaderboardPanels} list.
*/
private void hideOverallLeaderboards() {
overallLeaderboardsPanel.clear();
for (LeaderboardPanel overallLeaderboardPanel : overallLeaderboardPanels) {
timer.removeTimeListener(overallLeaderboardPanel);
timer.removePlayStateListener(overallLeaderboardPanel);
}
overallLeaderboardPanels.clear();
}
private void setRaceColumnSelectionToLastNStrategy(final Integer numberOfLastRacesToShow) {
@@ -1347,7 +1250,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
this.preSelectedRace = preSelectedRace;
this.competitorSelectionProvider = competitorSelectionProvider;
competitorSelectionProvider.addCompetitorSelectionChangeListener(this);
this.leaderboardGroupName = leaderboardGroupName;
this.setLeaderboardName(leaderboardName);
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
@@ -1356,7 +1258,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
this.selectedOverallDetailColumns = new ArrayList<DetailType>();
this.raceTimesInfoProvider = optionalRaceTimesInfoProvider;
this.selectedManeuverDetails = new ArrayList<DetailType>();
this.showOverallLeaderboardsOnSamePage = settings.isShowOverallLeaderboardsOnSamePage();
overallDetailColumnMap = createOverallDetailColumnMap();
settingsUpdatedExplicitly = !settings.updateUponPlayStateChange();
if (settings.getLegDetailsToShow() != null) {
@@ -1402,7 +1303,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
leaderboardTable = new SortedCellTableWithStylableHeaders<LeaderboardRowDTO>(
/* pageSize */10000, tableResources);
getLeaderboardTable().setWidth("100%");
this.userAgent = userAgent;
if (userAgent.isMobile() == UserAgentDetails.PlatformTypes.MOBILE) {
leaderboardSelectionModel = new ToggleSelectionModel<LeaderboardRowDTO>();
} else {
@@ -1429,8 +1329,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
}
contentPanel = new VerticalPanel();
contentPanel.setStyleName(STYLE_LEADERBOARD_CONTENT);
overallLeaderboardsPanel = new VerticalPanel();
overallLeaderboardPanels = new ArrayList<LeaderboardPanel>();
// the information panel
informationPanel = new FlowPanel();
@@ -1468,16 +1366,11 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
}
}
};
ImageResource chartIcon = resources.chartIcon();
ImageResource rankChartIcon = resources.rankChartIcon();
ImageResource leaderboardSettingsIcon = resources.leaderboardSettingsIcon();
pauseIcon = resources.pauseIcon();
playIcon = resources.playIcon();
pauseIcon = resources.autoRefreshEnabledIcon();
playIcon = resources.autoRefreshDisabledIcon();
refreshAndSettingsPanel = new HorizontalPanel();
refreshAndSettingsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
HorizontalPanel refreshPanel = new HorizontalPanel();
refreshPanel.setSpacing(5);
refreshPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
FlowPanel refreshPanel = new FlowPanel();
refreshPanel.addStyleName("refreshPanel");
toolbarPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
toolbarPanel.addStyleName("refreshAndSettings");
@@ -1485,35 +1378,13 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
playPause.addClickHandler(playPauseHandler);
playStateChanged(timer.getPlayState(), timer.getPlayMode());
refreshPanel.add(playPause);
Anchor rankChartsAnchor = new Anchor(AbstractImagePrototype.create(rankChartIcon).getSafeHtml());
rankChartsAnchor.setTitle(stringMessages.showRankChart());
rankChartsAnchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
showRankChartDialog();
}
});
Anchor chartsAnchor = new Anchor(AbstractImagePrototype.create(chartIcon).getSafeHtml());
chartsAnchor.setTitle(stringMessages.showCharts());
chartsAnchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
showCompareCompetitorsDialog();
}
});
Anchor settingsAnchor = new Anchor(AbstractImagePrototype.create(leaderboardSettingsIcon).getSafeHtml());
settingsAnchor.setTitle(stringMessages.settings());
settingsAnchor.addClickHandler(new SettingsClickHandler(stringMessages));
refreshAndSettingsPanel.add(rankChartsAnchor);
refreshAndSettingsPanel.add(chartsAnchor);
refreshAndSettingsPanel.add(refreshPanel);
refreshAndSettingsPanel.add(settingsAnchor);
toolbarPanel.add(refreshAndSettingsPanel, DockPanel.EAST);
if (!isEmbedded) {
contentPanel.add(toolbarPanel);
}
contentPanel.add(getLeaderboardTable());
contentPanel.add(overallLeaderboardsPanel);
setWidget(contentPanel);
raceNameForDefaultSorting = settings.getNameOfRaceToSort();
}
@@ -2415,7 +2286,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
: preSelectedRace.getRaceName(),
/* don't change nameOfRaceColumnToShow */null,
/* set nameOfRaceToShow if race was pre-selected */preSelectedRace == null ? null : preSelectedRace
.getRaceName(), getRaceColumnSelection(), showOverallLeaderboardsOnSamePage));
.getRaceName(), getRaceColumnSelection()));
}
currentlyHandlingPlayStateChange = false;
oldPlayMode = playMode;
@@ -2425,25 +2296,6 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
public void playSpeedFactorChanged(double newPlaySpeedFactor) {
// nothing to do
}
private void showCompareCompetitorsDialog() {
int selectedCompetitorsCount = Util.size(competitorSelectionProvider.getSelectedCompetitors());
if (selectedCompetitorsCount < 1) {
Window.alert(stringMessages.selectAtLeastOneCompetitor());
} else {
List<RegattaAndRaceIdentifier> races = getTrackedRacesIdentifiers();
RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, errorReporter, races, 5000l /* requestInterval*/);
CompareCompetitorsChartDialog chartDialog = new CompareCompetitorsChartDialog(sailingService, races, raceTimesInfoProvider,
competitorSelectionProvider, timer, stringMessages, errorReporter, leaderboardGroupName, leaderboardName);
chartDialog.show();
}
}
private void showRankChartDialog() {
RankChartDialog chartDialog = new RankChartDialog(sailingService, leaderboardName, competitorSelectionProvider,
timer, stringMessages, errorReporter, /* compactChart */ false);
chartDialog.show();
}
private List<RegattaAndRaceIdentifier> getTrackedRacesIdentifiers() {
List<RegattaAndRaceIdentifier> result = new ArrayList<RegattaAndRaceIdentifier>();
@@ -2473,7 +2325,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
Collections.unmodifiableList(selectedLegDetails), Collections.unmodifiableList(selectedRaceDetails),
Collections.unmodifiableList(selectedOverallDetailColumns), /* All races to select */ leaderboard.getRaceList(),
raceColumnSelection.getSelectedRaceColumnsOrderedAsInLeaderboard(leaderboard), raceColumnSelection, autoExpandPreSelectedRace,
showOverallLeaderboardsOnSamePage, timer.getRefreshInterval(), timer.getLivePlayDelayInMillis(), stringMessages);
timer.getRefreshInterval(), timer.getLivePlayDelayInMillis(), stringMessages);
}
@Override
@@ -4,21 +4,27 @@ import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
interface LeaderboardResources extends ClientBundle {
@Source("com/sap/sailing/gwt/ui/client/images/arrow-gain.png")
ImageResource arrowGainIcon();
@Source("com/sap/sailing/gwt/ui/client/images/arrow-loss.png")
ImageResource arrowLossIcon();
@Source("com/sap/sailing/gwt/ui/client/images/arrow-gain-loss.png")
ImageResource arrowGainLossIcon();
@Source("com/sap/sailing/gwt/ui/client/images/arrow-gain.png")
ImageResource arrowGainIcon();
@Source("com/sap/sailing/gwt/ui/client/images/arrow-loss.png")
ImageResource arrowLossIcon();
@Source("com/sap/sailing/gwt/ui/client/images/arrow-gain-loss.png")
ImageResource arrowGainLossIcon();
@Source("com/sap/sailing/gwt/ui/client/images/settings.png")
ImageResource settingsIcon();
@Source("com/sap/sailing/gwt/ui/client/images/leaderboardsettings.png")
ImageResource leaderboardSettingsIcon();
@Source("com/sap/sailing/gwt/ui/client/images/autorefresh_enabled.png")
ImageResource autoRefreshEnabledIcon();
@Source("com/sap/sailing/gwt/ui/client/images/autorefresh_disabled.png")
ImageResource autoRefreshDisabledIcon();
@Source("com/sap/sailing/gwt/ui/client/images/play.png")
ImageResource playIcon();
@@ -30,7 +30,6 @@ public class LeaderboardSettings {
private final Long delayBetweenAutoAdvancesInMilliseconds;
private final Long delayInMilliseconds;
private final boolean updateUponPlayStateChange;
private final boolean showOverallLeaderboardsOnSamePage;
/**
* There are two ways to select race columns.
@@ -54,7 +53,7 @@ public class LeaderboardSettings {
List<String> namesOfRaceColumnsToShow, List<String> namesOfRacesToShow, Integer numberOfLastRacesToShow,
boolean autoExpandPreSelectedRace, Long delayBetweenAutoAdvancesInMilliseconds, Long delayInMilliseconds,
String nameOfRaceToSort, boolean sortAscending, boolean updateUponPlayStateChange,
RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy, boolean showOverallLeaderboardsOnSamePage) {
RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy) {
if (namesOfRacesToShow != null && namesOfRaceColumnsToShow != null) {
throw new IllegalArgumentException("You can identify races either only by their race or by their column names, not both");
}
@@ -72,7 +71,6 @@ public class LeaderboardSettings {
this.nameOfRaceToSort = nameOfRaceToSort;
this.sortAscending = sortAscending;
this.updateUponPlayStateChange = updateUponPlayStateChange;
this.showOverallLeaderboardsOnSamePage = showOverallLeaderboardsOnSamePage;
}
public List<DetailType> getManeuverDetailsToShow() {
@@ -153,8 +151,4 @@ public class LeaderboardSettings {
public RaceColumnSelectionStrategies getActiveRaceColumnSelectionStrategy() {
return activeRaceColumnSelectionStrategy;
}
public boolean isShowOverallLeaderboardsOnSamePage() {
return showOverallLeaderboardsOnSamePage;
}
}
@@ -46,7 +46,6 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
private LongBox refreshIntervalInSecondsBox;
private LongBox delayInSecondsBox;
private final boolean autoExpandPreSelectedRace;
private final boolean showOverallLeaderboardOnSamePage;
private final long delayBetweenAutoAdvancesInMilliseconds;
private final long delayInMilliseconds;
private final Integer numberOfLastRacesToShow;
@@ -54,14 +53,13 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
private RadioButton explicitRaceColumnSelectionRadioBtn;
private RadioButton lastNRacesColumnSelectionRadioBtn;
private IntegerBox numberOfLastRacesToShowBox;
private CheckBox showOverallLeaderboardOnSamePageCheckbox;
public LeaderboardSettingsDialogComponent(List<DetailType> maneuverDetailSelection,
List<DetailType> legDetailSelection, List<DetailType> raceDetailSelection,
List<DetailType> overallDetailSelection, List<RaceColumnDTO> raceAllRaceColumns,
Iterable<RaceColumnDTO> raceColumnSelection, RaceColumnSelection raceColumnSelectionStrategy,
boolean autoExpandPreSelectedRace, boolean showOverallLeaderboardOnSamePage,
long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds, StringMessages stringMessages) {
boolean autoExpandPreSelectedRace, long delayBetweenAutoAdvancesInMilliseconds,
long delayInMilliseconds, StringMessages stringMessages) {
this.raceAllRaceColumns = raceAllRaceColumns;
this.numberOfLastRacesToShow = raceColumnSelectionStrategy.getNumberOfLastRaceColumnsToShow();
this.activeRaceColumnSelectionStrategy = raceColumnSelectionStrategy.getType();
@@ -79,14 +77,12 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
this.autoExpandPreSelectedRace = autoExpandPreSelectedRace;
this.delayBetweenAutoAdvancesInMilliseconds = delayBetweenAutoAdvancesInMilliseconds;
this.delayInMilliseconds = delayInMilliseconds;
this.showOverallLeaderboardOnSamePage = showOverallLeaderboardOnSamePage;
}
@Override
public Widget getAdditionalWidget(DataEntryDialog<?> dialog) {
FlowPanel dialogPanel = new FlowPanel();
dialogPanel.add(createSelectedRacesPanel(dialog));
dialogPanel.add(createOverallLeaderboardSelectionPanel(dialog));
dialogPanel.add(createOverallDetailPanel(dialog));
dialogPanel.add(createRaceDetailPanel(dialog));
dialogPanel.add(createLegDetailsPanel(dialog));
@@ -290,19 +286,6 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
return selectedRacesPanel;
}
private FlowPanel createOverallLeaderboardSelectionPanel(DataEntryDialog<?> dialog) {
FlowPanel overallLeaderboardSelectionPanel = new FlowPanel();
overallLeaderboardSelectionPanel.addStyleName("SettingsDialogComponent");
overallLeaderboardSelectionPanel.add(dialog.createHeadline(stringMessages.overallLeaderboardSelection(), true));
FlowPanel overallLeaderboardSelectionContent = new FlowPanel();
overallLeaderboardSelectionContent.addStyleName("dialogInnerContent");
overallLeaderboardSelectionPanel.add(overallLeaderboardSelectionContent);
showOverallLeaderboardOnSamePageCheckbox = dialog.createCheckbox(stringMessages.showOverallLeaderboardOnSamePage());
showOverallLeaderboardOnSamePageCheckbox.setValue(showOverallLeaderboardOnSamePage);
overallLeaderboardSelectionContent.add(showOverallLeaderboardOnSamePageCheckbox);
return overallLeaderboardSelectionPanel;
}
@Override
public LeaderboardSettings getResult() {
List<DetailType> maneuverDetailsToShow = new ArrayList<DetailType>();
@@ -347,8 +330,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
lastNRacesToShowValue,
autoExpandPreSelectedRace, 1000l * (delayBetweenAutoAdvancesValue == null ? 0l : delayBetweenAutoAdvancesValue.longValue()),
1000 * (delayInSecondsValue == null ? 0 : delayInSecondsValue.longValue()),
null, true, /* updateUponPlayStateChange */ true, activeRaceColumnSelectionStrategy,
showOverallLeaderboardOnSamePageCheckbox.getValue());
null, true, /* updateUponPlayStateChange */ true, activeRaceColumnSelectionStrategy);
}
@Override
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
/*
/**
* A factory class creating leaderboard settings for different contexts (user role, live or replay mode, etc.
*/
public class LeaderboardSettingsFactory {
@@ -39,7 +39,7 @@ public class LeaderboardSettingsFactory {
* this argument
*/
public LeaderboardSettings createNewSettingsForPlayMode(PlayModes playMode, String nameOfRaceToSort, String nameOfRaceColumnToShow,
String nameOfRaceToShow, RaceColumnSelection raceColumnSelection, boolean showOverallLeaderboardsOnSamePage) {
String nameOfRaceToShow, RaceColumnSelection raceColumnSelection) {
if (nameOfRaceColumnToShow != null && nameOfRaceToShow != null) {
throw new IllegalArgumentException("Can identify only one race to show, either by race name or by its column name, but not both");
}
@@ -70,11 +70,11 @@ public class LeaderboardSettingsFactory {
/* set autoExpandPreSelectedRace to true if we look at a single race */ nameOfRaceColumnToShow != null || nameOfRaceToShow != null,
/* refresh interval */ null, /* delay to live */ null,
/* name of race to sort */ nameOfRaceToSort, /* ascending */ true, /* updateUponPlayStateChange */ true,
raceColumnSelection.getType(), showOverallLeaderboardsOnSamePage);
raceColumnSelection.getType());
break;
case Replay:
settings = createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, nameOfRaceToSort, /* autoExpandFirstRace */
nameOfRaceColumnToShow != null, showOverallLeaderboardsOnSamePage);
nameOfRaceColumnToShow != null);
break;
}
return settings;
@@ -94,9 +94,9 @@ 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 showOverallLeaderboardsOnSamePage) {
List<String> namesOfRacesToShow, String nameOfRaceToSort, boolean autoExpandPreSelectedRace) {
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, /* leave overallDetailsToShow unchanged */ null,
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null, showOverallLeaderboardsOnSamePage);
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null);
}
/**
@@ -105,17 +105,16 @@ public class LeaderboardSettingsFactory {
*/
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, boolean showOverallLeaderboardsOnSamePage) {
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis) {
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, overallDetailsToShow,
nameOfRaceToSort, autoExpandPreSelectedRace, refreshIntervalMillis,
/* numberOfLastRacesToShow */null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT,
showOverallLeaderboardsOnSamePage);
/* numberOfLastRacesToShow */null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT);
}
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, Integer numberOfLastRacesToShow,
RaceColumnSelectionStrategies raceColumnSelectionStrategy, boolean showOverallLeaderboardsOnSamePage) {
RaceColumnSelectionStrategies raceColumnSelectionStrategy) {
if (namesOfRaceColumnsToShow != null && namesOfRacesToShow != null) {
throw new IllegalArgumentException("Can specify race columns either by column or by race name, not both");
}
@@ -134,6 +133,6 @@ public class LeaderboardSettingsFactory {
namesOfRacesToShow, numberOfLastRacesToShow,
autoExpandPreSelectedRace, refreshIntervalMillis, /* delay to live */ null,
/* sort by column */ nameOfRaceToSort, /* ascending */ true, /* updateUponPlayStateChange */ true,
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
raceColumnSelectionStrategy);
}
}
@@ -93,13 +93,12 @@ public class LeaderboardUrlConfigurationDialog extends SettingsDialog<Leaderboar
}
LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
namesOfRaceColumnsToShow, /* namesOfRacesToShow */null, /* nameOfRaceToSort */null, /* autoExpandPreSelectedRace */
false, /* showMetaLeaderboardsOnSamePage */ false);
false);
List<DetailType> overallDetailsToShow = Collections.emptyList();
leaderboardSettingsDialogComponent = new LeaderboardSettingsDialogComponent(settings.getManeuverDetailsToShow(),
settings.getLegDetailsToShow(), settings.getRaceDetailsToShow(), overallDetailsToShow, raceList,
/* select all races by default */ raceList, new ExplicitRaceColumnSelection(),
/* autoExpandPreSelectedRace */ false,
/* showOverallLeaderboardOnSamePage */ false,
/* delayBetweenAutoAdvancesInMilliseconds */ 3000l, /* delayInMilliseconds */ 3000l, stringMessages);
}
@@ -0,0 +1,155 @@
package com.sap.sailing.gwt.ui.leaderboard;
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.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeListener;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.client.UserAgentDetails;
import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorLeaderboardChart;
import com.sap.sailing.gwt.ui.client.shared.components.Component;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialog;
public class LeaderboardViewer extends SimplePanel {
private final StringMessages stringMessages;
private final LeaderboardPanel leaderboardPanel;
private final MultiCompetitorLeaderboardChart multiCompetitorChart;
private final AsyncActionsExecutor asyncActionsExecutor;
private final Timer timer;
private FlowPanel componentsNavigationPanel;
private final CompetitorSelectionModel competitorSelectionProvider;
private final static String STYLE_VIEWER_TOOLBAR = "viewerToolbar";
private final static String STYLE_VIEWER_TOOLBAR_INNERELEMENT = "viewerToolbar-innerElement";
private final static String STYLE_VIEWER_TOOLBAR_SETTINGS_BUTTON = "viewerToolbar-settingsButton";
public LeaderboardViewer(long delayToLiveMillis, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
LeaderboardSettings leaderboardSettings, RaceIdentifier preselectedRace, String leaderboardGroupName,
String leaderboardName, ErrorReporter errorReporter, StringMessages stringMessages,
UserAgentDetails userAgent, boolean showRaceDetails, boolean autoExpandLastRaceColumn, boolean showRankChart) {
this.stringMessages = stringMessages;
this.asyncActionsExecutor = asyncActionsExecutor;
competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */true);
FlowPanel mainPanel = new FlowPanel();
mainPanel.setSize("100%", "100%");
setWidget(mainPanel);
getElement().getStyle().setMarginLeft(12, Unit.PX);
getElement().getStyle().setMarginRight(12, Unit.PX);
componentsNavigationPanel = new FlowPanel();
componentsNavigationPanel.addStyleName(STYLE_VIEWER_TOOLBAR);
mainPanel.add(componentsNavigationPanel);
timer = new Timer(PlayModes.Replay, /*delayBetweenAutoAdvancesInMilliseconds*/ 3000l);
timer.setLivePlayDelayInMillis(delayToLiveMillis);
leaderboardPanel = createLeaderboardPanel(delayToLiveMillis, sailingService, leaderboardSettings, preselectedRace,
leaderboardGroupName, leaderboardName, errorReporter, stringMessages, userAgent,
showRaceDetails, autoExpandLastRaceColumn);
multiCompetitorChart = new MultiCompetitorLeaderboardChart(sailingService, asyncActionsExecutor, leaderboardName, DetailType.REGATTA_RANK, competitorSelectionProvider, timer,
stringMessages, errorReporter);
multiCompetitorChart.setVisible(showRankChart);
multiCompetitorChart.getElement().getStyle().setMarginTop(10, Unit.PX);
multiCompetitorChart.getElement().getStyle().setMarginBottom(10, Unit.PX);
mainPanel.add(leaderboardPanel);
mainPanel.add(multiCompetitorChart);
addComponentToNavigationMenu(leaderboardPanel, false);
addComponentToNavigationMenu(multiCompetitorChart, true);
if(showRankChart) {
multiCompetitorChart.timeChanged(timer.getTime());
}
}
private LeaderboardPanel createLeaderboardPanel(long delayToLiveMillis, SailingServiceAsync sailingService,
LeaderboardSettings leaderboardSettings, RaceIdentifier preselectedRace, String leaderboardGroupName,
String leaderboardName, ErrorReporter leaderboardEntryPoint, StringMessages stringMessages,
UserAgentDetails userAgent, boolean showRaceDetails, boolean autoExpandLastRaceColumn) {
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, asyncActionsExecutor,
leaderboardSettings, preselectedRace, competitorSelectionProvider, timer,
leaderboardGroupName, leaderboardName, leaderboardEntryPoint, stringMessages, userAgent,
showRaceDetails, /* raceTimesInfoProvider */null, autoExpandLastRaceColumn);
return leaderboardPanel;
}
private <SettingsType> void addComponentToNavigationMenu(final Component<SettingsType> component, boolean withCheckbox) {
final CheckBox checkBox= new CheckBox(component.getLocalizedShortName());
checkBox.getElement().getStyle().setFloat(Style.Float.LEFT);
checkBox.setEnabled(withCheckbox);
checkBox.setValue(component.isVisible());
checkBox.setTitle(stringMessages.showHideComponent(component.getLocalizedShortName()));
checkBox.addStyleName(STYLE_VIEWER_TOOLBAR_INNERELEMENT);
checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> newValue) {
boolean visible = checkBox.getValue();
component.setVisible(visible);
if (visible && component instanceof TimeListener) {
// trigger the component to update its data
((TimeListener) component).timeChanged(timer.getTime());
}
}
});
componentsNavigationPanel.add(checkBox);
Button settingsButton = new Button("");
if(component.hasSettings()) {
settingsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
new SettingsDialog<SettingsType>(component, stringMessages).show();
}
});
} else {
settingsButton.setEnabled(false);
}
settingsButton.addStyleName(STYLE_VIEWER_TOOLBAR_SETTINGS_BUTTON);
settingsButton.getElement().getStyle().setFloat(Style.Float.LEFT);
settingsButton.setTitle(stringMessages.settingsForComponent(component.getLocalizedShortName()));
componentsNavigationPanel.add(settingsButton);
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if(visible) {
timer.addTimeListener(multiCompetitorChart);
} else {
timer.removeTimeListener(multiCompetitorChart);
}
}
}
@@ -0,0 +1,250 @@
package com.sap.sailing.gwt.ui.leaderboard;
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.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.SimplePanel;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.TimeListener;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
import com.sap.sailing.gwt.ui.client.UserAgentDetails;
import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorLeaderboardChart;
import com.sap.sailing.gwt.ui.client.shared.components.Component;
import com.sap.sailing.gwt.ui.client.shared.components.ComponentViewerViewModes;
import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialog;
public class MetaLeaderboardViewer extends SimplePanel {
private final StringMessages stringMessages;
private final ErrorReporter errorReporter;
private final SailingServiceAsync sailingService;
private final CompetitorSelectionModel competitorSelectionProvider;
private FlowPanel componentsNavigationPanel;
private final LeaderboardPanel metaLeaderboardPanel;
private LeaderboardPanel selectedLeaderboardPanel;
private final MultiCompetitorLeaderboardChart multiCompetitorChart;
private String selectedLeaderboardName;
private final AsyncActionsExecutor asyncActionsExecutor;
private final String metaLeaderboardName;
private final String leaderboardGroupName;
private final UserAgentDetails userAgent;
private final boolean showRaceDetails;
private final Timer timer;
private final ComponentViewerViewModes viewMode;
private final FlowPanel mainPanel;
private final List<String> leaderboards;
private ListBox leaderboardSelectionListBox;
private final static String STYLE_VIEWER_TOOLBAR = "viewerToolbar";
private final static String STYLE_VIEWER_TOOLBAR_INNERELEMENT = "viewerToolbar-innerElement";
private final static String STYLE_VIEWER_TOOLBAR_SETTINGS_BUTTON = "viewerToolbar-settingsButton";
public MetaLeaderboardViewer(long delayToLiveMillis, SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
LeaderboardSettings leaderboardSettings, String preselectedLeaderboardName, RaceIdentifier preselectedRace, String leaderboardGroupName,
String metaLeaderboardName, ErrorReporter errorReporter, StringMessages stringMessages,
UserAgentDetails userAgent, boolean showRaceDetails, boolean autoExpandLastRaceColumn, boolean showRankChart) {
this.stringMessages = stringMessages;
this.errorReporter = errorReporter;
this.sailingService = sailingService;
this.asyncActionsExecutor = asyncActionsExecutor;
asyncActionsExecutor = new AsyncActionsExecutor();
this.metaLeaderboardName = metaLeaderboardName;
this.leaderboardGroupName = leaderboardGroupName;
this.userAgent = userAgent;
this.showRaceDetails = showRaceDetails;
this.selectedLeaderboardName = preselectedLeaderboardName;
viewMode = ComponentViewerViewModes.WEB_VIEW;
competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */true);
mainPanel = new FlowPanel();
mainPanel.setSize("100%", "100%");
setWidget(mainPanel);
getElement().getStyle().setMarginLeft(12, Unit.PX);
getElement().getStyle().setMarginRight(12, Unit.PX);
final Label overallStandingsLabel = new Label(stringMessages.overallStandings());
overallStandingsLabel.setStyleName("leaderboardHeading");
componentsNavigationPanel = new FlowPanel();
componentsNavigationPanel.addStyleName(STYLE_VIEWER_TOOLBAR);
mainPanel.add(componentsNavigationPanel);
timer = new Timer(PlayModes.Replay, /*delayBetweenAutoAdvancesInMilliseconds*/ 3000l);
timer.setLivePlayDelayInMillis(delayToLiveMillis);
metaLeaderboardPanel = new LeaderboardPanel(sailingService, asyncActionsExecutor,
leaderboardSettings, preselectedRace, competitorSelectionProvider, timer,
leaderboardGroupName, metaLeaderboardName, errorReporter, stringMessages, userAgent,
showRaceDetails, /* raceTimesInfoProvider */null, autoExpandLastRaceColumn);
multiCompetitorChart = new MultiCompetitorLeaderboardChart(sailingService, asyncActionsExecutor, metaLeaderboardName, DetailType.REGATTA_RANK, competitorSelectionProvider, timer,
stringMessages, errorReporter);
multiCompetitorChart.setVisible(showRankChart);
multiCompetitorChart.getElement().getStyle().setMarginTop(10, Unit.PX);
multiCompetitorChart.getElement().getStyle().setMarginBottom(10, Unit.PX);
mainPanel.add(metaLeaderboardPanel);
mainPanel.add(multiCompetitorChart);
addComponentToNavigationMenu(metaLeaderboardPanel, false, stringMessages.overallStandings());
addComponentToNavigationMenu(multiCompetitorChart, true, null);
leaderboards = new ArrayList<String>();
leaderboardSelectionListBox = new ListBox();
leaderboardSelectionListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
int selIndex =leaderboardSelectionListBox.getSelectedIndex();
if(selIndex >= 0) {
updateSelectedLeaderboard(leaderboardSelectionListBox.getItemText(selIndex));
}
}
});
leaderboardSelectionListBox.setVisible(false);
mainPanel.add(leaderboardSelectionListBox);
updateLeaderboardsOfMetaleaderboard();
if (selectedLeaderboardName != null) {
updateSelectedLeaderboard(selectedLeaderboardName);
}
}
private void updateLeaderboardsOfMetaleaderboard() {
sailingService.getLeaderboardsNamesOfMetaleaderboard(metaLeaderboardName, new AsyncCallback<List<Pair<String, String>>>() {
@Override
public void onSuccess(List<Pair<String, String>> leaderboardNamesAndDisplayNames) {
leaderboardSelectionListBox.clear();
int index = 0;
for (Pair<String, String> leaderboardNameAndDisplayName : leaderboardNamesAndDisplayNames) {
leaderboardSelectionListBox.addItem(leaderboardNameAndDisplayName.getB(), leaderboardNameAndDisplayName.getA());
if(selectedLeaderboardName != null && selectedLeaderboardName.equals(leaderboardNameAndDisplayName.getA())) {
leaderboardSelectionListBox.setSelectedIndex(index);
}
index++;
}
leaderboardSelectionListBox.setVisible(leaderboardNamesAndDisplayNames.size() > 0);
}
@Override
public void onFailure(Throwable caught) {
}
});
}
private <SettingsType> void addComponentToNavigationMenu(final Component<SettingsType> component, boolean isCheckboxEnabled,
String componentDisplayName) {
final String componentName = componentDisplayName != null ? componentDisplayName : component.getLocalizedShortName();
final CheckBox checkBox= new CheckBox(componentName);
checkBox.getElement().getStyle().setFloat(Style.Float.LEFT);
checkBox.setEnabled(isCheckboxEnabled);
checkBox.setValue(component.isVisible());
checkBox.setTitle(stringMessages.showHideComponent(componentName));
checkBox.addStyleName(STYLE_VIEWER_TOOLBAR_INNERELEMENT);
checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> newValue) {
boolean visible = checkBox.getValue();
component.setVisible(visible);
if (visible && component instanceof TimeListener) {
// trigger the component to update its data
((TimeListener) component).timeChanged(timer.getTime());
}
}
});
componentsNavigationPanel.add(checkBox);
Button settingsButton = new Button("");
if(component.hasSettings()) {
settingsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
new SettingsDialog<SettingsType>(component, stringMessages).show();
}
});
} else {
settingsButton.setEnabled(false);
}
settingsButton.addStyleName(STYLE_VIEWER_TOOLBAR_SETTINGS_BUTTON);
settingsButton.getElement().getStyle().setFloat(Style.Float.LEFT);
settingsButton.setTitle(stringMessages.settingsForComponent(componentName));
componentsNavigationPanel.add(settingsButton);
}
private void updateSelectedLeaderboard(String selectedLeaderboardName) {
if(selectedLeaderboardName != null) {
if(selectedLeaderboardPanel == null) {
LeaderboardSettings newDefaultSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, false);
selectedLeaderboardPanel = new LeaderboardPanel(sailingService, asyncActionsExecutor,
newDefaultSettings, /* preselectedRace*/ null, competitorSelectionProvider, timer,
leaderboardGroupName, metaLeaderboardName, errorReporter, stringMessages, userAgent,
showRaceDetails, /* raceTimesInfoProvider */null, false);
}
} else {
if(selectedLeaderboardPanel != null) {
remove(selectedLeaderboardPanel);
selectedLeaderboardPanel = null;
}
}
}
/**
* Fetches from the server the list of overall ("meta") leaderboard descriptions in which this main leaderboard occurs and
* creates one {@link LeaderboardPanel} for each such overall leaderboard. Those panels are then added to
* {@link #overallLeaderboardPanels}, registered with the {@link #timer} for common timing with the main leaderboard panel,
* and the panels are made visible by adding them to the {@link #overallLeaderboardsPanel} panel.
*/
private void updateOverallLeaderboardsAndAddToPanel() {
sailingService.getOverallLeaderboardNamesContaining(metaLeaderboardName, new AsyncCallback<List<String>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorTryingToObtainOverallLeaderboards(caught.getMessage()));
}
@Override
public void onSuccess(List<String> overallLeaderboardNames) {
for (String overallLeaderboardName : overallLeaderboardNames) {
}
}
});
}
}
@@ -1,23 +0,0 @@
package com.sap.sailing.gwt.ui.leaderboard;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Label;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.client.shared.charts.LeaderboardRankChart;
public class RankChartDialog extends DialogBoxExt {
public RankChartDialog(SailingServiceAsync sailingService, String leaderboardName, CompetitorSelectionProvider competitorSelectionProvider,
Timer timer, StringMessages stringMessages, ErrorReporter errorReporter, boolean compactChart) {
super(new Label(stringMessages.close()));
this.setPopupPosition(15, 15);
this.setHTML(stringMessages.compareCompetitors());
this.setWidth(Window.getClientWidth() - 250 + "px");
this.setAnimationEnabled(true);
setWidget(new LeaderboardRankChart(sailingService, leaderboardName, competitorSelectionProvider, timer,
stringMessages, errorReporter, compactChart));
}
}
@@ -563,8 +563,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,
/* showMetaLeaderboardsOnSamePage */ false),
/* racesToShow */ null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false),
new CompetitorSelectionModel(/* hasMultiSelection */true),
leaderboardName, errorReporter, stringMessages, userAgent, /* showRaceDetails */ true);
suppressedCompetitorsShown = new ListDataProvider<CompetitorDTO>(new ArrayList<CompetitorDTO>());
@@ -78,7 +78,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
*/
private int scrollOffset;
private final List<ComponentViewer> componentViewers;
private FlowPanel componentsNavigationPanel;
private FlowPanel settingsPanel;
private RaceTimePanel timePanel;
@@ -125,17 +124,14 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
setWidget(mainPanel);
this.timer = timer;
timeRangeWithZoomModel = new TimeRangeWithZoomModel();
componentViewers = new ArrayList<ComponentViewer>();
competitorSelectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true);
componentsNavigationPanel = new FlowPanel();
componentsNavigationPanel.addStyleName("raceBoardNavigation");
switch (getConfiguration().getViewMode()) {
case ONESCREEN:
createOneScreenView(leaderboardName, leaderboardGroupName, mainPanel);
getElement().getStyle().setMarginLeft(12, Unit.PX);
getElement().getStyle().setMarginRight(12, Unit.PX);
break;
}
createOneScreenView(leaderboardName, leaderboardGroupName, mainPanel);
getElement().getStyle().setMarginLeft(12, Unit.PX);
getElement().getStyle().setMarginRight(12, Unit.PX);
timePanel = new RaceTimePanel(timer, timeRangeWithZoomModel, stringMessages, raceTimesInfoProvider, canReplayWhileLiveIsPossible);
timeRangeWithZoomModel.addTimeZoomChangeListener(timePanel);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(timePanel);
@@ -146,7 +142,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
private void createOneScreenView(String leaderboardName, String leaderboardGroupName, FlowPanel mainPanel) {
// create the default leaderboard and select the right race
leaderboardPanel = createLeaderboardPanel(leaderboardName, leaderboardGroupName);
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
leaderboardPanel.getElement().getStyle().setMargin(5, Unit.PX);
RaceMap raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelectionModel, stringMessages);
raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceMap);
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
@@ -160,10 +156,10 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
components.add(windChart);
leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, components);
componentViewers.add(leaderboardAndMapViewer);
for (ComponentViewer componentViewer : componentViewers) {
mainPanel.add(componentViewer.getViewerWidget());
}
mainPanel.add(leaderboardAndMapViewer.getViewerWidget());
mainPanel.add(windChart);
mainPanel.add(competitorChart);
setLeaderboardVisible(getConfiguration().isShowLeaderboard());
setWindChartVisible(getConfiguration().isShowWindChart());
setCompetitorChartVisible(getConfiguration().isShowCompetitorsChart());
@@ -203,8 +199,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
.createNewSettingsForPlayMode(timer.getPlayMode(),
/* nameOfRaceToSort */ selectedRaceIdentifier.getRaceName(),
/* nameOfRaceColumnToShow */ null, /* nameOfRaceToShow */ selectedRaceIdentifier.getRaceName(),
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier),
/* showOverallLeaderboardsOnSamePage */ false);
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier));
return new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderBoardSettings, selectedRaceIdentifier,
competitorSelectionModel, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages,
userAgent, /* showRaceDetails */ true, raceTimesInfoProvider, /* autoExpandLastRaceColumn */ false);
@@ -262,51 +257,35 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
}
/**
* 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 />
* Displays or hides the leaderboard.<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) {
switch (getConfiguration().getViewMode()) {
case ONESCREEN:
setComponentVisible(leaderboardAndMapViewer, leaderboardPanel, visible);
break;
}
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 />
*
* Displays or hides the wind chart.<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) {
switch (getConfiguration().getViewMode()) {
case ONESCREEN:
setComponentVisible(leaderboardAndMapViewer, windChart, visible);
break;
}
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 />
* Displays or hides the competitor chart.<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) {
switch (getConfiguration().getViewMode()) {
case ONESCREEN:
setComponentVisible(leaderboardAndMapViewer, competitorChart, visible);
break;
}
setComponentVisible(leaderboardAndMapViewer, competitorChart, visible);
}
public Widget getNavigationWidget() {
@@ -76,6 +76,7 @@ import com.sap.sailing.domain.common.CountryCode;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.MaxPointsReason;
@@ -2143,10 +2144,30 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
@Override
public List<String> getLeaderboardNames() throws Exception {
public List<String> getLeaderboardNames() {
return new ArrayList<String>(getService().getLeaderboards().keySet());
}
@Override
public Pair<String, LeaderboardType> checkLeaderboardName(String leaderboardName) {
Pair<String, LeaderboardType> result = null;
if(getService().getLeaderboards().containsKey(leaderboardName)) {
Leaderboard leaderboard = getService().getLeaderboards().get(leaderboardName);
boolean isMetaLeaderboard = leaderboard instanceof MetaLeaderboard ? true : false;
boolean isRegattaLeaderboard = leaderboard instanceof RegattaLeaderboard ? true : false;
LeaderboardType type;
if(isMetaLeaderboard) {
type = isRegattaLeaderboard ? LeaderboardType.RegattaMetaLeaderboard : LeaderboardType.FlexibleMetaLeaderboard;
} else {
type = isRegattaLeaderboard ? LeaderboardType.RegattaLeaderboard : LeaderboardType.FlexibleLeaderboard;
}
result = new Pair<String, LeaderboardType>(leaderboard.getName(), type);
}
return result;
}
@Override
public StrippedLeaderboardDTO createFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, int[] discardThresholds, ScoringSchemeType scoringSchemeType,
String courseAreaId) {
@@ -2245,15 +2266,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
leaderboardDTO.name = leaderboard.getName();
leaderboardDTO.displayName = leaderboard.getDisplayName();
leaderboardDTO.competitorDisplayNames = new HashMap<CompetitorDTO, String>();
leaderboardDTO.isMetaLeaderboard = leaderboard instanceof MetaLeaderboard ? true : false;
if (leaderboard instanceof RegattaLeaderboard) {
RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard;
Regatta regatta = regattaLeaderboard.getRegatta();
leaderboardDTO.regattaName = regatta.getName();
leaderboardDTO.isRegattaLeaderboard = true;
leaderboardDTO.type = leaderboard instanceof MetaLeaderboard ? LeaderboardType.RegattaMetaLeaderboard : LeaderboardType.RegattaLeaderboard;
leaderboardDTO.scoringScheme = regatta.getScoringScheme().getType();
} else {
leaderboardDTO.isRegattaLeaderboard = false;
leaderboardDTO.type = leaderboard instanceof MetaLeaderboard ? LeaderboardType.FlexibleMetaLeaderboard : LeaderboardType.FlexibleLeaderboard;
leaderboardDTO.scoringScheme = leaderboard.getScoringScheme().getType();
}
if (leaderboard.getDefaultCourseArea() != null) {
@@ -2806,6 +2826,68 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return result;
}
private Triple<String, List<CompetitorDTO>, List<Double>> getLeaderboardDataEntriesForRaceColumn(DetailType detailType,
LeaderboardDTO leaderboard, RaceColumnDTO raceColumn) throws NoWindException {
List<Double> values = new ArrayList<Double>();
List<CompetitorDTO> competitorDTOs = new ArrayList<CompetitorDTO>();
if(detailType != null) {
switch (detailType) {
case RACE_TOTAL_POINTS:
for (Map.Entry<CompetitorDTO, LeaderboardRowDTO> entry : leaderboard.rows.entrySet()) {
LeaderboardEntryDTO leaderboardEntryDTO = entry.getValue().fieldsByRaceColumnName.get(raceColumn.name);
values.add(leaderboardEntryDTO != null ? leaderboardEntryDTO.totalPoints : null);
competitorDTOs.add(entry.getKey());
}
break;
case REGATTA_RANK:
List<CompetitorDTO> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(raceColumn);
int rank = 1;
for(CompetitorDTO competitor: competitorsFromBestToWorst) {
values.add(new Double(rank));
competitorDTOs.add(competitor);
rank++;
}
break;
default:
break;
}
}
return new Triple<String, List<CompetitorDTO>, List<Double>>(raceColumn.name, competitorDTOs, values);
}
@Override
public List<Triple<String, List<CompetitorDTO>, List<Double>>> getLeaderboardDataEntriesForAllRaceColumns(String leaderboardName,
Date date, DetailType detailType) throws Exception {
List<Triple<String, List<CompetitorDTO>, List<Double>>> result = new ArrayList<Util.Triple<String,List<CompetitorDTO>,List<Double>>>();
// Attention: The reason why we read the data from the LeaderboardDTO and not from the leaderboard directly is to ensure
// the use of the leaderboard cache and the reuse of all the logic in the getLeaderboardByName method
LeaderboardDTO leaderboardDTO = getLeaderboardByName(leaderboardName, date, Collections.<String> emptyList());
for (RaceColumnDTO raceColumnDTO : leaderboardDTO.getRaceList()) {
result.add(getLeaderboardDataEntriesForRaceColumn(detailType, leaderboardDTO, raceColumnDTO));
}
return result;
}
@Override
public List<Pair<String, String>> getLeaderboardsNamesOfMetaleaderboard(String metaLeaderboardName) {
Leaderboard leaderboard = getService().getLeaderboardByName(metaLeaderboardName);
if (leaderboard == null) {
throw new IllegalArgumentException("Couldn't find leaderboard named "+metaLeaderboardName);
}
if(!(leaderboard instanceof MetaLeaderboard)) {
throw new IllegalArgumentException("The leaderboard "+metaLeaderboardName + " is not a metaleaderboard");
}
List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
MetaLeaderboard metaLeaderboard = (MetaLeaderboard) leaderboard;
for(Leaderboard containedLeaderboard: metaLeaderboard.getLeaderboards()) {
result.add(new Pair<String, String>(containedLeaderboard.getName(),
containedLeaderboard.getDisplayName() != null ? containedLeaderboard.getDisplayName() : containedLeaderboard.getName()));
}
return result;
}
@Override
public Map<CompetitorDTO, List<GPSFixDTO>> getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier,
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
@@ -3357,31 +3439,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
}
@Override
public List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException {
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
final TimePoint timePoint;
if (date == null) {
final TimePoint nowMinusDelay = leaderboard.getNowMinusDelay();
final TimePoint timePointOfLatestModification = leaderboard.getTimePointOfLatestModification();
if (timePointOfLatestModification != null && !nowMinusDelay.before(timePointOfLatestModification)) {
timePoint = timePointOfLatestModification;
} else {
timePoint = nowMinusDelay;
}
} else {
timePoint = new MillisecondsTimePoint(date);
}
Map<RaceColumn, List<Competitor>> preResult = leaderboard
.getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(timePoint);
List<Pair<String, List<CompetitorDTO>>> result = new ArrayList<Util.Pair<String,List<CompetitorDTO>>>();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
List<CompetitorDTO> competitorList = getCompetitorDTOList(preResult.get(raceColumn));
result.add(new Pair<String, List<CompetitorDTO>>(raceColumn.getName(), competitorList));
}
return result;
}
@Override
public List<String> getOverallLeaderboardNamesContaining(String leaderboardName) {
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
@@ -10,6 +10,7 @@ import java.util.Set;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.impl.Util.Pair;
@@ -27,8 +28,7 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
public String defaultCourseAreaIdAsString;
public String defaultCourseAreaName;
public ScoringSchemeType scoringScheme;
public boolean isMetaLeaderboard;
public boolean isRegattaLeaderboard;
public LeaderboardType type;
private Long delayToLiveInMillisForLatestRace;
@@ -329,7 +329,7 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
return false;
if (hasCarriedPoints != other.hasCarriedPoints)
return false;
if (isMetaLeaderboard != other.isMetaLeaderboard)
if (type != other.type)
return false;
if (scoringScheme != other.scoringScheme)
return false;
@@ -15,12 +15,13 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
public String description;
public List<StrippedLeaderboardDTO> leaderboards;
public boolean displayLeaderboardsInReverseOrder;
private int[] overallLeaderboardDiscardThresholds;
private ScoringSchemeType overallLeaderboardScoringSchemeType;
/**
* Creates a new LeaderboardGroupDTO with empty but non-null name, description and an empty but non-null list for the leaderboards.<br />
* Creates a new LeaderboardGroupDTO with empty but non-null name, description and an empty but non-null list for
* the leaderboards.<br />
* The additional data (start dates and places for the races) will be initialized but empty.
*/
public LeaderboardGroupDTO() {
@@ -37,11 +38,11 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
this.description = description;
this.leaderboards = leaderboards;
}
public boolean hasOverallLeaderboard() {
return getOverallLeaderboardScoringSchemeType() != null;
}
public int[] getOverallLeaderboardDiscardThresholds() {
return overallLeaderboardDiscardThresholds;
}
@@ -57,17 +58,17 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
public void setOverallLeaderboardScoringSchemeType(ScoringSchemeType overallLeaderboardScoringSchemeType) {
this.overallLeaderboardScoringSchemeType = overallLeaderboardScoringSchemeType;
}
public List<StrippedLeaderboardDTO> getLeaderboardsInReverseOrder() {
List<StrippedLeaderboardDTO> leaderboardsInReverseOrder = new ArrayList<StrippedLeaderboardDTO>(leaderboards);
Collections.reverse(leaderboardsInReverseOrder);
return Collections.unmodifiableList(leaderboardsInReverseOrder);
}
public boolean containsRegattaLeaderboard() {
boolean result = false;
for (StrippedLeaderboardDTO leaderboard : leaderboards) {
if(leaderboard.isRegattaLeaderboard) {
if (leaderboard.type.isRegattaLeaderboard()) {
result = true;
break;
}
@@ -77,8 +78,7 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
public boolean containsRace(RaceIdentifier race) {
boolean containsRace = false;
leaderboardsLoop:
for (StrippedLeaderboardDTO leaderboard : leaderboards) {
leaderboardsLoop: for (StrippedLeaderboardDTO leaderboard : leaderboards) {
for (RaceColumnDTO raceInLeaderboard : leaderboard.getRaceList()) {
for (FleetDTO fleet : raceInLeaderboard.getFleets()) {
final RegattaAndRaceIdentifier raceIdentifierForFleet = raceInLeaderboard.getRaceIdentifier(fleet);
@@ -91,9 +91,10 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
}
return containsRace;
}
/**
* @return The earliest date in the start dates of the leaderboards, or <code>null</code> if no start dates are contained
* @return The earliest date in the start dates of the leaderboards, or <code>null</code> if no start dates are
* contained
*/
public Date getGroupStartDate() {
Date groupStart = null;
@@ -108,7 +109,7 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
}
return groupStart;
}
/**
* Uses {@link LeaderboardGroupDTO#getLeaderboardPlaces(leaderboard) LeaderboardGroupDTO.getLeaderboardPlaces} to
* create the {@link PlacemarkOrderDTO places} for all contained leaderboards and returns them as a list.
@@ -126,7 +127,7 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
}
return places;
}
/**
* @return <code>true</code> if the group contains a race which is live.
*/
@@ -138,7 +139,7 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
@@ -173,5 +174,5 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
public List<StrippedLeaderboardDTO> getLeaderboards() {
return leaderboards;
}
}
@@ -283,7 +283,7 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
private SafeHtml leaderboardRacesToHtml(StrippedLeaderboardDTO leaderboard) {
SafeHtmlBuilder b = new SafeHtmlBuilder();
if (leaderboard.isRegattaLeaderboard && leaderboard.regattaName != null) {
if (leaderboard.type.isRegattaLeaderboard()) {
RegattaDTO regatta = regattasByName.get(leaderboard.regattaName);
boolean renderSeriesName = Util.size(regatta.series) > 1;
for (SeriesDTO series : regatta.series) {
@@ -6,6 +6,7 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
@@ -94,8 +95,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
showWindChart = false;
leaderboard = null;
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false,
/* showMetaLeaderboardsOnSamePage */ false);
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false);
leaderboardTimer = new Timer(PlayModes.Live, /* delayBetweenAutoAdvancesInMilliseconds */1000l);
leaderboardTimer.setLivePlayDelayInMillis(delayToLiveInMillis);
@@ -122,7 +122,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
updateRaceTimesInfoProvider();
}
};
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
leaderboardPanel.getElement().getStyle().setMargin(5, Unit.PX);
return leaderboardPanel;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB