Merge remote-tracking branch 'origin/master'

This commit is contained in:
Nick committed 2015-07-18 18:13:56 +02:00
commit 79ae7b8530
137 files changed
+3889 -1439

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
# groups Apache / httpd log entries into "${referrer}.ips" files, where ${referrer} is the
# referrer URL identifying the "event"; the lines written to the .ips files hold the IP
# address of the requestor, the date (not the time) and the user agent string.
# Sorting for unique entries should give a count similar to what goaccess is using to
# determine "unique visitors."
# A sample line:
# 505Worlds2012.sapsailing.com 66.249.73.53 - - [12/Jan/2014:03:33:11 +0000] "GET /robots.txt HTTP/1.1" 404 238 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
#cat $* | sed -e 's/^\([^ ]*\) \([^ ]*\) \([^ ]*\) \([^ ]*\) \[\([^:]*\):\([^]]*\)\] \"[^\"]*\" [^ ]* [^ ]* \"[^\"]\" \"\([\"]*\)\"\(.*\)$/\1 \2 \5 \7/'
cat $* | sed -e 's/^\([^ ]*\) \([^ ]*\) \([^ ]*\) \([^ ]*\) \[\([^:]*\):\([^]]*\)\] \"[^\"]*\" [^ ]* [^ ]* \"[^\"]*\" \"\([^\"]*\)\"/\1 \2 \5 \7/' | while read referrer hit; do
echo "$hit" >>${referrer}.ips
done
for i in *.ips; do
echo -n "$i "
cat $i | sort -u | wc | awk '{ print $1; }'
done | tee unique-ips-days-useragents-per-event
echo "Done. Wrote results to `pwd`/unique-ips-days-useragents-per-event
Binary file not shown.
@@ -148,7 +148,7 @@ public class StartlineAnalysisCard extends Composite implements HasWidgets, Star
asyncActionsExecutor, null, Collections.singletonList(startAnalysisDTO.regattaAndRaceIdentifier), 5000l /* requestInterval */);
raceMap = new RaceMap(sailingServiceAsync, asyncActionsExecutor, null, timer, competitorSelectionModel,
StringMessages.INSTANCE, false, false, false, startAnalysisDTO.regattaAndRaceIdentifier,
resources.combinedWindPanelStyle());
resources.combinedWindPanelStyle(), /* showHeaderPanel */ true);
raceMap.onRaceSelectionChange(Collections.singletonList(startAnalysisDTO.regattaAndRaceIdentifier));
raceMap.getSettings().setZoomSettings(new RaceMapZoomSettings(zoomTypes, false));
raceMap.getSettings().setHelpLinesSettings(getHelpLineSettings());
@@ -4,7 +4,7 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
public enum BoatClassMasterdata {
_18Footer ("18Footer", true, 8.90, 2.00, BoatHullType.MONOHULL, true, "18.Footer", "18ft", "18ft Skiff"),
_18Footer ("18Footer", true, 8.90, 2.00, BoatHullType.MONOHULL, true, "18.Footer", "18ft", "18ft Skiff", "18. Footer"),
_2_4M ("2.4 Meter", true, 4.11, 0.81, BoatHullType.MONOHULL, false, "2.4M", "2.4mR", "2.4 Metre", "2.4-metre"),
_5O5 ("5O5", true, 5.03, 1.88, BoatHullType.MONOHULL, true, "505", "5o5", "505er", "5o5er"),
_12M ("12 Meter", true, 5.97, 1.43, BoatHullType.MONOHULL, true, "12M", "12mR", "12SQM", "12-metre", "12 metre"),
@@ -31,7 +31,7 @@ public enum BoatClassMasterdata {
FINN ("Finn", true, 4.50, 1.51, BoatHullType.MONOHULL, false),
FLYING_PHANTOM ("Flying Phantom", true, 5.52, 3.00, BoatHullType.CATAMARAN, true),
FOLKBOAT ("Folkboat", true, 7.68, 2.20, BoatHullType.MONOHULL, false, "Folke", "Folkeboot"),
FUN ("FUN", true, 7.20, 2.45, BoatHullType.MONOHULL, true, "FUN O.D.", "FUN OD"),
FUN ("FUN", true, 7.20, 2.45, BoatHullType.MONOHULL, true, "FUN O.D.", "FUN OD", "Open FUN"),
F_16 ("Formula 16", true, 5.00, 2.50, BoatHullType.CATAMARAN, true, "F16", "F-16"),
HOBIE_16 ("Hobie 16", true, 5.05, 2.41, BoatHullType.CATAMARAN, false, "H16"),
H_BOAT ("H-Boat", true, 8.28, 2.18, BoatHullType.MONOHULL, true, "HB"),
@@ -27,9 +27,10 @@ public enum WindSourceType {
TRACK_BASED_ESTIMATION(false, 0.5, /* useSpeed */ false),
/**
* Inferred from the race course layout if the course is known to have its first leg be an upwind leg
* Inferred from the race course layout if the course is known to have its first leg be an upwind leg. This
* source has very low confidence and must be superseded by any other wind source.
*/
COURSE_BASED(false, 0.3, /* useSpeed */ false),
COURSE_BASED(false, 0.01, /* useSpeed */ false),
/**
* Wind estimation combined from all other wind sources, using <code>TrackedRace.getWind(...)</code>, based on
@@ -6,7 +6,7 @@ package com.sap.sailing.domain.common.racelog;
*
*/
public enum RaceLogRaceStatus {
UNKNOWN(0), UNSCHEDULED(1), SCHEDULED(2), STARTPHASE(3), RUNNING(4), FINISHING(5), FINISHED(6);
UNKNOWN(0), UNSCHEDULED(1), PRESCHEDULED(2), SCHEDULED(3), STARTPHASE(4), RUNNING(5), FINISHING(6), FINISHED(7);
/** the order number represents the natural order of the states */
private int orderNumber;
@@ -63,108 +63,137 @@ public class DependentStartTimeFinderTest {
@Test
public void testDependentStartTimeUpdate() {
final MillisecondsDurationImpl cAfterB = new MillisecondsDurationImpl(5000);
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB));
StartTimeFinder finder = new StartTimeFinder(raceLogResolver, raceLogC);
// Tests for correct behaviour, when race depending on has no start time set
assertNull(finder.analyze().getStartTime());
final MillisecondsDurationImpl bAfterA = new MillisecondsDurationImpl(6000);
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA));
finder = new StartTimeFinder(raceLogResolver, raceLogB);
StartTimeFinderResult result = finder.analyze();
List<SimpleRaceLogIdentifier> expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
List<SimpleRaceLogIdentifier> expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("A", "", ""));
assertNull(result.getStartTime());
assertEquals(bAfterA, result.getStartTimeDiff());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
// assertEquals(5000, result.getStartTimeDiff().asMillis());
finder = new StartTimeFinder(raceLogResolver, raceLogC);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("B", "", ""));
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("A", "", ""));
result = finder.analyze();
assertNull(result.getStartTime());
assertEquals(cAfterB, result.getStartTimeDiff());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
MillisecondsTimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
finder = new StartTimeFinder(raceLogResolver, raceLogB);
assertEquals(10000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(11000, result.getStartTime().asMillis());
assertEquals(6000, result.getStartTimeDiff().asMillis());
finder = new StartTimeFinder(raceLogResolver, raceLogC);
assertEquals(15000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(16000, result.getStartTime().asMillis());
assertEquals(5000, result.getStartTimeDiff().asMillis());
// Test correct behaviour, when middle element changes
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(15000)));
finder = new StartTimeFinder(raceLogResolver, raceLogA);
assertEquals(5000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(5000, result.getStartTime().asMillis());
assertNull(result.getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogB);
assertEquals(15000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(15000, result.getStartTime().asMillis());
assertNull(result.getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogC);
assertEquals(20000, finder.analyze().getStartTime().asMillis());
result = finder.analyze();
assertEquals(20000, result.getStartTime().asMillis());
assertEquals(5000, result.getStartTimeDiff().asMillis());
}
@Test
public void testDependentStartTimeCycle() {
final MillisecondsDurationImpl cAfterB = new MillisecondsDurationImpl(6000);
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB));
final MillisecondsDurationImpl bAfterA = new MillisecondsDurationImpl(7000);
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA));
final MillisecondsDurationImpl aAfterC = new MillisecondsDurationImpl(8000);
raceLogA.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("C", "", ""), new MillisecondsDurationImpl(5000)));
new SimpleRaceLogIdentifierImpl("C", "", ""), aAfterC));
// Check that all resolve to null in case of a cycle
StartTimeFinder finder = new StartTimeFinder(raceLogResolver, raceLogC);
assertNull(finder.analyze().getStartTime());
assertEquals(cAfterB, finder.analyze().getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogB);
assertNull(finder.analyze().getStartTime());
assertEquals(bAfterA, finder.analyze().getStartTimeDiff());
finder = new StartTimeFinder(raceLogResolver, raceLogA);
assertNull(finder.analyze().getStartTime());
assertEquals(aAfterC, finder.analyze().getStartTimeDiff());
// Check that all resolve correctly after changing some element in cycle
MillisecondsTimePoint now = MillisecondsTimePoint.now();
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12,
new MillisecondsTimePoint(5000)));
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
// now A -> C -> B
finder = new StartTimeFinder(raceLogResolver, raceLogA);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
List<SimpleRaceLogIdentifier> expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
List<SimpleRaceLogIdentifier> expectedDependingOnRaces;
expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("C", "", ""));
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("B", "", ""));
StartTimeFinderResult result = finder.analyze();
assertEquals(15000, result.getStartTime().asMillis());
assertEquals(19000, result.getStartTime().asMillis());
assertEquals(8000, result.getStartTimeDiff().asMillis());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
// now C -> B
finder = new StartTimeFinder(raceLogResolver, raceLogC);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
expectedDependingOnRaces = new ArrayList<>();
expectedDependingOnRaces.add(new SimpleRaceLogIdentifierImpl("B", "", ""));
result = finder.analyze();
assertEquals(10000, result.getStartTime().asMillis());
assertEquals(11000, result.getStartTime().asMillis());
assertEquals(6000, result.getStartTimeDiff().asMillis());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
// now B
finder = new StartTimeFinder(raceLogResolver, raceLogB);
//Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time is unknown
expectedDependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
// Tests for correct behaviour, when race depending on has a DependentStartTimeEvent and its parent start time
// is unknown
expectedDependingOnRaces = new ArrayList<>();
result = finder.analyze();
assertEquals(5000, result.getStartTime().asMillis());
assertNull(result.getStartTimeDiff());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
}
}
@@ -13,13 +13,12 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sse.common.TimePoint;
public class StartTimeFinderTest extends
PassAwareRaceLogAnalyzerTest<StartTimeFinder, StartTimeFinderResult> {
public class StartTimeFinderTest extends PassAwareRaceLogAnalyzerTest<StartTimeFinder, StartTimeFinderResult> {
@Override
protected StartTimeFinder createAnalyzer(RaceLog raceLog) {
@@ -30,7 +29,7 @@ public class StartTimeFinderTest extends
protected TargetPair getTargetEventsAndResultForPassAwareTests(int passId, AbstractLogEventAuthor author) {
RaceLogStartTimeEvent event = createEvent(RaceLogStartTimeEvent.class, 1, passId, author);
when(event.getStartTime()).thenReturn(mock(TimePoint.class));
return new TargetPair(Arrays.asList(event), new StartTimeFinderResult(event.getStartTime()));
return new TargetPair(Arrays.asList(event), new StartTimeFinderResult(event.getStartTime(), null));
}
@Test
@@ -1,8 +1,10 @@
package com.sap.sailing.domain.abstractlog.race;
import java.io.Serializable;
import com.sap.sse.common.Util.Triple;
public interface SimpleRaceLogIdentifier {
public interface SimpleRaceLogIdentifier extends Serializable {
String getRegattaLikeParentName();
String getRaceColumnName();
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -16,11 +17,10 @@ public class DependentStartTimeResolver {
public DependentStartTimeResolver(RaceLogResolver raceLogResolver) {
this.raceLogResolver = raceLogResolver;
}
public StartTimeFinderResult resolve(RaceLogDependentStartTimeEvent event) {
List<SimpleRaceLogIdentifier> dependingOnRaces = new ArrayList<SimpleRaceLogIdentifier>();
List<SimpleRaceLogIdentifier> dependingOnRaces = Collections.emptyList();
return internalResolve(event, dependingOnRaces);
}
@@ -28,21 +28,26 @@ public class DependentStartTimeResolver {
SimpleRaceLogIdentifier identifier = event.getDependentOnRaceIdentifier();
Duration startTimeDifference = event.getStartTimeDifference();
RaceLog raceLog = raceLogResolver.resolve(identifier);
final StartTimeFinderResult result;
if (raceLog == null) {
result = new StartTimeFinderResult(dependingOnRaces, null, startTimeDifference);
} else {
List<SimpleRaceLogIdentifier> extendedDependingOnRaces = new ArrayList<>();
Util.addAll(dependingOnRaces, extendedDependingOnRaces);
extendedDependingOnRaces.add(identifier);
final StartTimeFinderResult result;
if (containsCycle(extendedDependingOnRaces)) {
result = new StartTimeFinderResult(extendedDependingOnRaces, null);
result = new StartTimeFinderResult(extendedDependingOnRaces, null, null);
} else {
StartTimeFinder dependentStartTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
StartTimeFinderResult resultOfDependentRace = dependentStartTimeFinder.analyze(extendedDependingOnRaces);
if (resultOfDependentRace.getStartTime() == null) {
resultOfDependentRace.setStartTimeDiff(startTimeDifference);
result = resultOfDependentRace;
} else {
result = new StartTimeFinderResult(resultOfDependentRace.getRacesDependingOn(),
resultOfDependentRace.getStartTime() == null ? null : resultOfDependentRace.getStartTime()
.plus(startTimeDifference));
resultOfDependentRace.getStartTime().plus(startTimeDifference), startTimeDifference);
}
}
}
return result;
@@ -44,15 +44,14 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
@Override
protected RaceLogRaceStatus performAnalysis() {
ArrayListNavigableSet<RaceLogRaceStatusEvent> statusEvents = new ArrayListNavigableSet<RaceLogRaceStatusEvent>(RaceLogRaceStatusEventComparator.INSTANCE);
ArrayListNavigableSet<RaceLogRaceStatusEvent> statusEvents = new ArrayListNavigableSet<>(RaceLogRaceStatusEventComparator.INSTANCE);
for(RaceLogEvent event: getPassEvents()) {
if(event instanceof RaceLogRaceStatusEvent) {
statusEvents.add((RaceLogRaceStatusEvent) event);
}
}
for (RaceLogRaceStatusEvent event : statusEvents.descendingSet()) {
RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
statusEvent.accept(eventDispatcher);
event.accept(eventDispatcher);
return eventDispatcher.nextStatus;
}
@@ -79,7 +78,7 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
private void setRaceLogStatusBasedOnStartTime(TimePoint startTime) {
if (startTime == null) {
nextStatus = RaceLogRaceStatus.UNKNOWN;
nextStatus = RaceLogRaceStatus.PRESCHEDULED;
return;
}
TimePoint now = clock.now();
@@ -29,13 +29,13 @@ public class StartTimeFinder extends RaceLogAnalyzer<StartTimeFinderResult> {
for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogStartTimeEvent) {
TimePoint startTime = ((RaceLogStartTimeEvent) event).getStartTime();
return new StartTimeFinderResult(dependingOnRaces, startTime);
return new StartTimeFinderResult(dependingOnRaces, startTime, null);
} else if (event instanceof RaceLogDependentStartTimeEvent) {
DependentStartTimeResolver dependentStartTimeResolver = new DependentStartTimeResolver(resolver);
return dependentStartTimeResolver.internalResolve((RaceLogDependentStartTimeEvent) event, dependingOnRaces);
}
}
return new StartTimeFinderResult(dependingOnRaces, null);
return new StartTimeFinderResult(dependingOnRaces, null, null);
} finally {
log.unlockAfterRead();
}
@@ -1,29 +1,25 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
public class StartTimeFinderResult {
private final Iterable<SimpleRaceLogIdentifier> racesDependingOn;
private final TimePoint startTime;
private Duration startTimeDiff;
public StartTimeFinderResult(final Iterable<SimpleRaceLogIdentifier> racesDependingOn, final TimePoint startTime) {
this.racesDependingOn = racesDependingOn;
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime, Duration startTimeDiff) {
this.startTime = startTime;
this.startTimeDiff = startTimeDiff;
this.racesDependingOn = racesDependingOn;
}
public StartTimeFinderResult(TimePoint startTime) {
racesDependingOn = new ArrayList<SimpleRaceLogIdentifier>();
this.startTime = startTime;
}
public StartTimeFinderResult(List<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime) {
this.racesDependingOn = racesDependingOn;
this.startTime = startTime;
public StartTimeFinderResult(TimePoint startTime, Duration startTimeDiff) {
this(/* racesDependingOn */ Collections.<SimpleRaceLogIdentifier>emptyList(), startTime, startTimeDiff);
}
public Iterable<SimpleRaceLogIdentifier> getRacesDependingOn() {
@@ -34,11 +30,16 @@ public class StartTimeFinderResult {
return startTime;
}
public boolean isDependentStartTime() {
if (racesDependingOn == null) {
return false;
public Duration getStartTimeDiff() {
return startTimeDiff;
}
return Util.isEmpty(racesDependingOn);
public void setStartTimeDiff(Duration startTimeDiff) {
this.startTimeDiff = startTimeDiff;
}
public boolean isDependentStartTime() {
return racesDependingOn != null && !Util.isEmpty(racesDependingOn);
}
@Override
@@ -47,6 +48,7 @@ public class StartTimeFinderResult {
int result = 1;
result = prime * result + ((racesDependingOn == null) ? 0 : racesDependingOn.hashCode());
result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());
result = prime * result + ((startTimeDiff == null) ? 0 : startTimeDiff.hashCode());
return result;
}
@@ -69,6 +71,11 @@ public class StartTimeFinderResult {
return false;
} else if (!startTime.equals(other.startTime))
return false;
if (startTimeDiff == null) {
if (other.startTimeDiff != null)
return false;
} else if (!startTimeDiff.equals(other.startTimeDiff))
return false;
return true;
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.abstractlog.race.impl;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
public class SimpleRaceLogIdentifierImpl implements SimpleRaceLogIdentifier {
private static final long serialVersionUID = 5377117723950808853L;
protected final String regattaLikeParentName;
protected final String raceColumnName;
protected final String fleetName;
@@ -187,4 +187,9 @@ public interface RaceState extends ReadonlyRaceState {
void setAdditionalScoringInformationEnabled(TimePoint creationTimePoint, boolean enable, AdditionalScoringInformationType informationType);
boolean isAdditionalScoringInformationEnabled(AdditionalScoringInformationType informationType);
/**
* forces a RaceStateUpdate
*/
void forceUpdate();
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.abstractlog.race.state.impl;
import java.util.Collections;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -17,6 +18,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer
import com.sap.sailing.domain.abstractlog.race.scoring.AdditionalScoringInformationType;
import com.sap.sailing.domain.abstractlog.race.scoring.RaceLogAdditionalScoringInformationEvent;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedureFactory;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedurePrerequisite;
@@ -42,6 +44,10 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
private final AbstractLogEventAuthor author;
private final RaceLogEventFactory factory;
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog) {
return create(raceLogResolver, raceLog, /* forRaceLogIdentifier */ null, Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap());
}
/**
* Creates a {@link RaceState} with the initial racing procedure type set to a fallback value and an empty configuration.
*/
@@ -63,9 +69,11 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
this(raceLogResolver, raceLog, author, eventFactory, new RaceStatusAnalyzer.StandardClock(), procedureFactory);
}
private RaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog, AbstractLogEventAuthor author, RaceLogEventFactory eventFactory, RaceStatusAnalyzer.Clock analyzersClock,
private RaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog, AbstractLogEventAuthor author,
RaceLogEventFactory eventFactory, RaceStatusAnalyzer.Clock analyzersClock,
RacingProcedureFactory procedureFactory) {
super(raceLogResolver, raceLog, analyzersClock, procedureFactory, /* update */ true);
super(raceLogResolver, raceLog, /* forRaceLogIdentifier */ null, analyzersClock, procedureFactory,
Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap(), /* update */ true);
this.author = author;
this.factory = eventFactory;
}
@@ -221,4 +229,10 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
return event != null;
}
@Override
public void forceUpdate() {
super.update();
registerListenerOnDependentRaceIfDependentStartTime(Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap());
}
}
@@ -1,11 +1,16 @@
package com.sap.sailing.domain.abstractlog.race.state.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogChangedListener;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ConfirmedFinishPositioningListFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishPositioningListFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder;
@@ -15,10 +20,10 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastWindFixFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ProtestStartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer.Clock;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RacingProcedureTypeAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.impl.WeakRaceLogChangedVisitor;
import com.sap.sailing.domain.abstractlog.race.state.RaceStateChangedListener;
import com.sap.sailing.domain.abstractlog.race.state.RaceStateEvent;
@@ -49,21 +54,41 @@ import com.sap.sse.common.Util;
*/
public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedListener {
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog) {
return create(raceLogResolver, raceLog, /* forRaceLogIdentifier */null,
Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState> emptyMap());
}
/**
* Creates a {@link ReadonlyRaceState}.
*
* @param forRaceLogIdentifier
* If known, callers can specify the simple race log identifier for the race log to be wrapped by this
* new race state. This can help avoid endless recursions because this race state can be passed along
* together with the identifier so that when again along a dependent call chain a race state is required
* for that race log identifier, this one can be used.
*/
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog,
ConfigurationLoader<RegattaConfiguration> configurationLoader) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, new ReadonlyRacingProcedureFactory(
configurationLoader));
SimpleRaceLogIdentifier forRaceLogIdentifier,
ConfigurationLoader<RegattaConfiguration> configurationLoader,
Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, forRaceLogIdentifier, new ReadonlyRacingProcedureFactory(
configurationLoader), dependentRaceStates);
}
/**
* Creates a {@link ReadonlyRaceState} with an empty configuration ( {@link EmptyRegattaConfiguration} ).
*
* @param forRaceLogIdentifier
* If known, callers can specify the simple race log identifier for the race log to be wrapped by this
* new race state. This can help avoid endless recursions because this race state can be passed along
* together with the identifier so that when again along a dependent call chain a race state is required
* for that race log identifier, this one can be used.
*/
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, new ReadonlyRacingProcedureFactory(
new EmptyRegattaConfiguration()));
public static ReadonlyRaceState create(RaceLogResolver raceLogResolver, RaceLog raceLog,
SimpleRaceLogIdentifier forRaceLogIdentifier, Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
return new ReadonlyRaceStateImpl(raceLogResolver, raceLog, forRaceLogIdentifier, new ReadonlyRacingProcedureFactory(
new EmptyRegattaConfiguration()), dependentRaceStates);
}
/**
@@ -127,18 +152,26 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
private RaceStateChangedListener raceStateToObserveListener;
private ReadonlyRaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog,
RacingProcedureFactory procedureFactory) {
this(raceLogResolver, raceLog, new RaceStatusAnalyzer.StandardClock(), procedureFactory, /* update */true);
SimpleRaceLogIdentifier forRaceLogIdentifier, RacingProcedureFactory procedureFactory,
Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
this(raceLogResolver, raceLog, forRaceLogIdentifier, new RaceStatusAnalyzer.StandardClock(),
procedureFactory, dependentRaceStates, /* update */true);
}
/**
* @param raceLogResolver
* @param forRaceLogIdentifier
* If known, callers can specify the simple race log identifier for the race log to be wrapped by this
* new race state. This can help avoid endless recursions because this race state can be passed along
* together with the identifier so that when again along a dependent call chain a race state is required
* for that race log identifier, this one can be used.
* @param update
* if <code>true</code>, the race state will be updated whenever the underlying <code>raceLog</code>
* changes.
*/
protected ReadonlyRaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog, Clock analyzersClock,
RacingProcedureFactory procedureFactory, boolean update) {
protected ReadonlyRaceStateImpl(RaceLogResolver raceLogResolver, RaceLog raceLog,
SimpleRaceLogIdentifier forRaceLogIdentifier, Clock analyzersClock,
RacingProcedureFactory procedureFactory,
Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates, boolean update) {
this.raceLog = raceLog;
this.raceLogResolver = raceLogResolver;
this.procedureFactory = procedureFactory;
@@ -178,8 +211,18 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
// We known that recreateRacingProcedure calls update() when done, therefore this RaceState
// will be fully initialized after this line
recreateRacingProcedure();
final Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStatesAndMe;
if (forRaceLogIdentifier != null) {
dependentRaceStatesAndMe = new HashMap<>(dependentRaceStates);
dependentRaceStatesAndMe.put(forRaceLogIdentifier, this);
} else {
dependentRaceStatesAndMe = dependentRaceStates;
}
registerListenerOnDependentRaceIfDependentStartTime(dependentRaceStatesAndMe);
}
// Check wether the latest known StartTimeEvent is a non-dependent or dependent start time in case of a
protected void registerListenerOnDependentRaceIfDependentStartTime(Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
// Check whether the latest known StartTimeEvent is a non-dependent or dependent start time in case of a
// dependent startTime setup listeners
this.raceLog.lockForRead();
try {
@@ -187,7 +230,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
if (event instanceof RaceLogStartTimeEvent) {
break;
} else if (event instanceof RaceLogDependentStartTimeEvent) {
setupListenersOnDependentRace(event);
setupListenersOnDependentRace(event, dependentRaceStates);
break;
}
}
@@ -319,7 +362,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
@Override
public void eventAdded(RaceLogEvent event) {
if (event instanceof RaceLogDependentStartTimeEvent) {
setupListenersOnDependentRace(event);
setupListenersOnDependentRace(event, Collections.<SimpleRaceLogIdentifier, ReadonlyRaceState>emptyMap());
} else if (event instanceof RaceLogStartTimeEvent) {
if (raceStateToObserve != null) {
raceStateToObserve.removeChangedListener(raceStateToObserveListener);
@@ -329,15 +372,25 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
update();
}
private void setupListenersOnDependentRace(RaceLogEvent event) {
private void setupListenersOnDependentRace(RaceLogEvent event, Map<SimpleRaceLogIdentifier, ReadonlyRaceState> dependentRaceStates) {
if (raceStateToObserve != null) {
// Remove previous listeners
raceStateToObserve.removeChangedListener(raceStateToObserveListener);
raceStateToObserve = null;
}
RaceLogDependentStartTimeEvent dependentStartTimeEvent = (RaceLogDependentStartTimeEvent) event;
RaceLog resolvedRaceLog = raceLogResolver.resolve(dependentStartTimeEvent.getDependentOnRaceIdentifier());
raceStateToObserve = ReadonlyRaceStateImpl.create(raceLogResolver, resolvedRaceLog);
final SimpleRaceLogIdentifier dependentOnRaceIdentifier = dependentStartTimeEvent.getDependentOnRaceIdentifier();
if (dependentRaceStates.containsKey(dependentOnRaceIdentifier)) {
raceStateToObserve = dependentRaceStates.get(dependentOnRaceIdentifier);
raceStateToObserve.addChangedListener(raceStateToObserveListener);
} else {
RaceLog resolvedRaceLog = raceLogResolver.resolve(dependentOnRaceIdentifier);
if (resolvedRaceLog != null) {
raceStateToObserve = ReadonlyRaceStateImpl.create(raceLogResolver, resolvedRaceLog,
dependentOnRaceIdentifier, dependentRaceStates);
raceStateToObserve.addChangedListener(raceStateToObserveListener);
}
}
}
@Override
@@ -350,7 +403,7 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
return false;
}
private void update() {
protected void update() {
RacingProcedureType type = racingProcedureAnalyzer.analyze();
if (!Util.equalsWithNull(cachedRacingProcedureType, type) && type != RacingProcedureType.UNKNOWN) {
cachedRacingProcedureType = type;
@@ -139,7 +139,6 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
@@ -3669,7 +3668,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
if (polarDataService == null) {
throw new NotEnoughDataHasBeenAddedException("Target time estimation failed. No polar service available.");
}
Duration durationOfAllLegs = new MillisecondsDurationImpl(0);
Duration durationOfAllLegs = Duration.NULL;
for (TrackedLeg leg : trackedLegs.values()) {
Duration durationOfLeg = leg.getEstimatedTimeToComplete(polarDataService, timepoint);
durationOfAllLegs = durationOfAllLegs.plus(durationOfLeg);
@@ -11,6 +11,6 @@
</listAttribute>
<stringAttribute key="projectName" value="com.sap.sailing.gwt.ui"/>
<booleanAttribute key="showLaunchOutput" value="false"/>
<stringAttribute key="url" value="http://127.0.0.1:8888/gwt/Home.html"/>
<stringAttribute key="url" value="http://127.0.0.1:8888/gwt/AdminConsole.html"/>
<stringAttribute key="urlQueryParams" value=""/>
</launchConfiguration>
@@ -0,0 +1,695 @@
/** Add css rules here for your application. */
/*
body #calendar:after
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
*/
* {
font-family: 'Open Sans', sans-serif;
font-size: 12px;
}
iframe {
display: block;
width: 100%;
height: 100%;
border: none;
overflow-y: auto;
overflow-x: hidden;
}
.clear {
clear: both;
font-size: 1px;
height: 0px;
line-height: 0px;
margin: -1px 0 0;
width: 100%;
}
.cover {
left: -1000px;
overflow: hidden;
position: absolute;
top: -1000px;
}
body {
background: white;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
line-height: 1;
font-size: 14px;
font-weight: 400;
margin: 0;
}
table td, select, button {
font-family: 'Open Sans', Arial, Verdana, sans-serif;
line-height: 1;
font-size: 14px;
}
.contentOuterPanel {
background: #dddcdc;
margin: 0 12px 0 12px;
padding: 12px;
}
/** Example rules used by the template application (remove for your app) */
h1 {
font-size: 2em;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-weight: 700;
color: #777777;
margin: 40px 0px 70px;
text-align: center;
}
table {
border-collapse: separate;
}
.CombinedWindPanelParentDiv {
padding-top: 34px;
padding-left: 10px;
}
.TrueNorthIndicatorPanelParentDiv {
padding-top: 10px;
padding-left: 10px;
}
.MapSimulationLegendParentDiv {
padding-bottom: 10px;
padding-right: 10px;
pointer-events: none;
}
.MapSimulationLegend {
pointer-events: all;
}
.RaceMap-HeaderPanel {
height: 60px;
width: 100%;
background-color: #333;
opacity: .5;
}
.leaderboardLabel {
border-bottom: 2px solid #007dc0;
padding: 0 0 6px 0;
font-weight: 700;
}
.refreshAndSettings {
background: #f2f2f2;
border-bottom: 2px solid #fff;
border-top: 2px solid #fff;
height: 30px;
}
.refreshAndSettings table td {
padding: 0 10px 0 0;
}
.refreshAndSettings table table td {
padding: 0 0 0 0;
}
.race-name {
line-height: 1.0;
}
.openColumn {
position: relative;
vertical-align: middle;
}
img.openColumn {
display: block;
margin: 3px auto 3px auto !important;
}
.breadcrumbPanel {
height: 30px;
margin: 0 12px 13px 12px;
}
.mainPanel {
width: 100%;
}
.raceBoardNavigation {
float: left;
margin: 9px 0 0 12px;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-weight: 300;
}
.raceBoardControls {
float: right;
margin: 9px 0 0 12px;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-weight: 300;
}
.raceBoardNavigation-navigationitem {
color: #dddcdc;
text-transform: uppercase;
font-weight: 500;
margin: 0 12px 0 0;
padding: 0 0 5px 0;
}
.raceBoardNavigation-navigationitem:hover {
border-bottom: 4px solid #f0ab00;
color: #dddcdc;
text-decoration: none;
}
.raceBoardNavigation-navigationitem:active {
border-bottom: 4px solid #f0ab00;
color: #dddcdc;
text-decoration: none;
}
.competitorInfo-Canvas {
pointer-events: none;
}
.gwt-DialogBox {
z-index: 20;
}
.abstractChartPanel-importantMessageOfChart {
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-weight: 600;
font-size: 20px;
height: 70px;
margin: 0 auto;
padding: 49px 0 0 80px;
width: 300px;
color: #7c7c7c;
background: url(images/important-message.png) left center no-repeat;
}
.breadcrumbPanel-NextArrow {
display: none;
}
.errorLabel {
color: #FF0000;
}
.raceBoardNavigation-settingsButtonPanel {
float: right;
margin: 9px 12px 0 0;
}
.raceBoardNavigation-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;
-moz-border-radius: 0px 3px 3px 0px;
-o-border-radius: 0px 3px 3px 0px;
-ms-border-radius: 0px 3px 3px 0px;
border-radius: 0px 3px 3px 0px;
}
.raceBoardNavigation-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;
}
.raceBoardNavigation-filterButton:hover {
background-image: url(images/filter_white.png),
url(images/btn-cta-orangeleft.png);
background-position: center, center -30px;
background-repeat: no-repeat, repeat-x;
cursor: pointer;
}
.raceBoardNavigation-innerElement {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center
0 transparent;
color: #FFFFFF;
cursor: pointer;
font-family: 'Open Sans', Verdana, Arial, sans-serif;
font-weight: 600;
font-size: 14px;
margin: 0 0 0 0;
padding: 2px 7px 2px 3px;
height: 18px;
display: inline-block;
text-decoration: none;
border-radius: 5px 0 0 5px;
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);
}
.raceBoardNavigation-innerElement:active {
top: 1px;
position: relative;
}
.raceBoardNavigation-innerElement:hover {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center
-30px transparent;
cursor: pointer;
}
.raceBoardNavigation-innerElement label {
cursor: pointer;
}
.raceBoardNavigation-standaloneElement {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center
0 transparent;
color: #FFFFFF;
cursor: pointer;
font-family: 'Open Sans', Verdana, Arial, sans-serif;
font-weight: 600;
font-size: 14px;
margin: 0 0 0 0;
padding: 2px 7px 2px 3px;
height: 23px;
display: inline-block;
text-decoration: none;
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;
}
.raceBoardNavigation-standaloneElement:active {
top: 1px;
position: relative;
}
.raceBoardNavigation-standaloneElement:hover {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center
-30px transparent;
cursor: pointer;
}
.raceBoardNavigation-standaloneElement label {
cursor: pointer;
}
.timePanelButton {
float: left;
margin: 0 0 0 5px;
}
.timePanelButton-SlowDown {
background-color: red !important;
}
.timePanelButton-SpeedUp {
background-color: green !important;
}
.playPauseButton {
padding: 0;
float: left;
width: 32px;
height: 28px;
background: url(images/viewicons/@1x/img/SAP_RV_Play_INACTIVE.png)
center no-repeat;
cursor: pointer;
border: 1px solid #cecece;
border-radius: 4px;
outline: 0;
}
.playPauseButton:hover {
cursor: pointer;
background-color: #cecece;
}
.playPauseButtonPause {
background: url(images/viewicons/@2x/img/SAP_RV_Pause_INACTIVE.png) 2px
0 no-repeat;
outline: 0;
background-size: contain;
}
.playPauseButtonPause:hover {
cursor: pointer;
}
.backToLivePlayButton {
width: 42px;
height: 38px;
font-size: 8px;
white-space: normal;
border-radius: 4px;
border: none;
margin-right: 23px;
margin-top: 5px;
margin-bottom: 5px;
z-index: 1;
}
.backToLivePlayButton-Inactive {
color: #6dc021;
background-color: white;
font-weight: bold;
}
.backToLivePlayButton-Active {
background-color: #6dc021;
color: white;
font-weight: normal;
}
.backToLivePlayButton:hover {
border: 2px solid gray;
}
.backToLivePlayButton:focus {
outline: 0 !important;
}
.dockLayoutPanel div[style="position: absolute; overflow: hidden; left: 0px; right: 0px; bottom: 0px; height: 90px;"]
{
z-index: 10;
}
.browserOptimizedMessage {
padding: 15px 0px;
}
.trackingQuality-circle {
width: 8px;
height: 8px;
border-radius: 50%;
float: left;
}
.circleYellow {
background: #fbba00;
}
.circleGreen {
background: #8ab54e;
}
.circleRed {
background: #f01010;
}
.busyIndicator-Simple {
position: absolute;
top: 0.7em;
right: 7.4em;
}
.busyIndicator-Circle {
}
.raceBoard-TitlePanel {
position: relative;
top: 10px;
z-index: 10;
color: #fff;
margin-left: auto;
margin-right: auto;
}
.raceBoard-TitlePanel-RaceNameLabel {
font-size: 32px;
font-weight: bold;
padding: 7px;
}
.raceBoard-Logo {
position: absolute;
top: 6px;
left: 10px;
z-index: 10;
cursor: pointer;
}
.SideBySideComponentViewer-MainPanel {
top: 12px;
}
.TimePanel-ShowExtended-Button {
width: 29px;
height: 28px;
float: left;
cursor: pointer;
border-radius: 4px;
border: 1px solid #ececec;
}
.TimePanel-ShowExtended-Button-Open {
background: white url(images/arrows_bottom_black.png) center no-repeat;
}
.TimePanel-ShowExtended-Button-Closed {
background: white url(images/arrows_top_black.png) center no-repeat;
}
.TimePanel-ShowExtended-Button:hover {
background-color: #cecece;
}
.TimePanel-ShowExtended-Button:focus {
outline: 0 !important;
}
.RaceBoard-globalNavigation-Link {
display: inline;
margin-right: 3px;
}
.RaceBoard-globalNavigation-Link a {
color: #fff;
cursor: pointer;
}
.RaceBoard-globalNavigation-Link+.RaceBoard-globalNavigation-Link:before
{
content: ' \00bb\00a0';
}
.TimePanel-settingsButtonStyle {
position: absolute;
right: 0px;
z-index: 1;
border: none;
}
.TimePanel-settingsButtonStyle:focus {
outline: 0 !important;
}
.TimePanel-settingsButtonStyle:hover {
border: 1px solid gray;
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed-multiCompetitorRaceChart
{
background-color: white;
background-image:
url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_INACTIVE.png),
url(images/arrows_top_black_small.png);
background-repeat: no-repeat, no-repeat;
background-position: 5% 50%, 95% 50%
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Open-multiCompetitorRaceChart
{
background-color: white;
background-image:
url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_GHOSTED.png),
url(images/arrows_bottom_black_small.png);
background-repeat: no-repeat, no-repeat;
background-position: 5% 50%, 95% 50%
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed-windChart {
background-color: white;
background-image:
url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_INACTIVE.png),
url(images/arrows_top_black_small.png);
background-repeat: no-repeat, no-repeat;
background-position: 5% 50%, 95% 50%
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Open-windChart {
background-color: white;
background-image:
url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_GHOSTED.png),
url(images/arrows_bottom_black_small.png);
background-repeat: no-repeat, no-repeat;
background-position: 5% 50%, 95% 50%
}
.gwt-SplitLayoutPanel-EastToggleButton-Open-leaderboard {
background-color: white;
background-image:
url(images/viewicons/@1x/img/SAP_RV_Leaderboard_GHOSTED.png),
url(images/arrows_top_black_small.png);
background-repeat: no-repeat, no-repeat;
background-position: 4% 50%, 96% 50%
}
.gwt-SplitLayoutPanel-EastToggleButton-Closed-leaderboard {
background-color: white;
background-image:
url(images/viewicons/@1x/img/SAP_RV_Leaderboard_INACTIVE.png),
url(images/arrows_bottom_black_small.png);
background-repeat: no-repeat, no-repeat;
background-position: 4% 50%, 96% 50%
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed-media,
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Open-media {
background-color: white;
background-image:
url(images/viewicons/@1x/img/SAP_RV_AudioVideo_INACTIVE.png);
background-repeat: no-repeat, no-repeat;
background-position: 10% 50%, 90% 50%
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-mediaplaying {
background-image:
url(images/viewicons/@1x/img/SAP_RV_AudioVideo_GHOSTED.png);
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Small-media {
background-position: 5% 50%, 100% 50% !important;
margin-left: 0px !important;
padding: 2px 35px !important;
}
@external gwt-ToggleButton-up, gwt-ToggleButton-down, gwt-ToggleButton-up-hovering, gwt-ToggleButton-down-hovering;
.gwt-ToggleButton-up, .gwt-ToggleButton-up-hovering {
background: url(images/unlock.png) center no-repeat;
margin-left: 5px;
}
.gwt-ToggleButton-down, .gwt-ToggleButton-down-hovering {
background: url(images/lock.png) center no-repeat;
margin-left: 5px;
}
.RegattaRaceInformation-Header {
font-family: 'Open Sans', Arial, Verdana, sans-serif;
position: relative;
left: 140px;
top: 11px;
}
.RegattaRaceInformation-Header .RaceName-Label {
display: inline;
color: white;
margin-right: 10px;
font-size: 36px;
}
.RegattaRaceInformation-Header .RaceSeriesAndFleet-Label {
display: inline;
color: white;
font-size: 15px;
}
.RegattaAndRaceTime-Header {
position: absolute;
right: 10px;
top: 13px;
text-align: right;
color: white;
}
.RegattaAndRaceTime-Header .RegattaName-Anchor {
font-size: 15px;
color: white;
text-decoration: none;
padding-left: 25px;
background-image: url(images/home.png);
background-repeat: no-repeat;
background-size: 17px;
background-position: 4px 1px;
}
.RegattaAndRaceTime-Header .RegattaName-Anchor:hover {
text-decoration: underline;
}
.RegattaAndRaceTime-Header .RaceTime-Label {
font-size: 15px;
margin-top: 5px;
}
.VideoPopup-Close-Button {
border: none;
}
.Media-Select-Button {
font-size: 15px;
font-size: 1rem;
display: inline-block;
font-weight: 400;
color: #111;
border: none;
padding: 0.5em 0.3em 0.5em 0.8em;
background-color: #fff;
-webkit-transition: background-color 0.2s;
-moz-transition: background-color 0.2s;
-o-transition: background-color 0.2s;
-ms-transition: background-color 0.2s;
transition: background-color 0.2s;
width: 100%;
max-width: 25em;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.Media-Select-Button:hover, .Media-Select-Button:focus {
background-color: #f0ab00;
}
.Media-Select-Button-playing {
color: #999;
}
.Media-Select-Button-playing:hover, .Media-Select-Button-playing:focus {
color: #fff;
background-color: #ccc;
}
.Media-Select-Popup .dialogMiddleCenterInner {
padding: 0;
}
.Media-Select-Popup .Caption {
padding: 4px 10px;
}
@@ -0,0 +1,41 @@
<!doctype html>
<html>
<head>
<!--[if lt IE 9]>
<meta http-equiv="REFRESH" content="0; url=http://www.sapsailing.com/browser-info.html">
<![endif]-->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>SAP Sailing Analytics Race Board</title>
<meta content='width = device-width, initial-scale = 1.0, user-scalable = yes' name='viewport'>
<meta content='yes' name='apple-mobile-web-app-capable'>
<meta content='black' name='apple-mobile-web-app-status-bar-style'>
<!-- <link href='iphone-splash-screen.png' rel='apple-touch-startup-image'> -->
<link href='images/sap-sailing-app-icon.png' rel='apple-touch-icon'>
<link rel="shortcut icon" type="image/x-icon" href="images/sap.ico" />
<!--<link href='http://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700,300italic,400italic,500italic,700italic' rel='stylesheet' type='text/css'>-->
<!-- <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:700,300,300italic' rel='stylesheet' type='text/css'>-->
<link rel="stylesheet" type="text/css" href="/sailing-reset-2.0.cache.css">
<link rel="stylesheet" type="text/css" href="/sailing-fontface-1.0.cache.css">
<link rel="stylesheet" type="text/css" href="EmbeddedMapAndWindChart.css">
<script type="text/javascript" src="js/jquery-1.5.2.min.js"></script>
<script type="text/javascript" language="javascript" src="com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart/com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart.nocache.js"></script>
</head>
<body>
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
Your web browser must have JavaScript enabled
in order for this application to display correctly.
</div>
</noscript>
</body>
</html>
@@ -54,7 +54,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.ui.Search -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.ui.Search -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart.html com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m -Xdebug -Xrunjdwp:transport=dt_socket,address=7999,server=y"/>
</launchConfiguration>
@@ -4,6 +4,7 @@
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
@@ -70,7 +71,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.home.Desktop com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.home.Desktop com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m"/>
</launchConfiguration>
+4 -1
View File
@@ -50,5 +50,8 @@ bin.includes = META-INF/,\
Home.html,\
HomeDesktop.html,\
HomeMobile.html,\
SettingsDialog.css
SettingsDialog.css,\
EmbeddedMapAndWindChart.css,\
EmbeddedMapAndWindChart.html,\
com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart/
output.. = WEB-INF/classes/
+1
View File
@@ -130,6 +130,7 @@
<module>com.sap.sailing.gwt.ui.Leaderboard</module>
<module>com.sap.sailing.gwt.ui.LeaderboardEditing</module>
<module>com.sap.sailing.gwt.ui.RaceBoard</module>
<module>com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart</module>
<module>com.sap.sailing.gwt.ui.Spectator</module>
<module>com.sap.sailing.gwt.ui.DataMining</module>
<module>com.sap.sailing.gwt.ui.VideoPopup</module>
@@ -1,26 +0,0 @@
package com.sap.sailing.gwt.ui.actions;
import java.util.Date;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO;
import com.sap.sse.gwt.client.async.AsyncAction;
public class GetCoursePositionsAction implements AsyncAction<CoursePositionsDTO> {
private final SailingServiceAsync sailingService;
private final RegattaAndRaceIdentifier raceIdentifier;
private final Date date;
public GetCoursePositionsAction(SailingServiceAsync sailingService, RegattaAndRaceIdentifier raceIdentifier, Date date) {
this.sailingService = sailingService;
this.raceIdentifier = raceIdentifier;
this.date = date;
}
@Override
public void execute(AsyncCallback<CoursePositionsDTO> callback) {
sailingService.getCoursePositions(raceIdentifier, date, callback);
}
}
@@ -22,6 +22,13 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
private boolean autoAdjustPlayMode;
private RaceTimesInfoDTO lastRaceTimesInfo;
/**
* When a marker update is required but min/max are not yet initialized, this flag is set. If later
* {@link #setMinMax(Date, Date, boolean)} is invoked and this flag is set, a {@link #redrawAllMarkers(RaceTimesInfoDTO)}
* will be issued and the flag is cleared.
*/
private boolean redrawAllMarkersPendingForMinMaxBeingInitialized;
public RaceTimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages,
RaceTimesInfoProvider raceTimesInfoProvider, boolean canReplayWhileLiveIsPossible) {
super(timer, timeRangeProvider, stringMessages, canReplayWhileLiveIsPossible);
@@ -105,7 +112,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
// maybe show a special state for this like "Race did not start yet"
}
}
lastRaceTimesInfo = raceTimesInfo;
lastRaceTimesInfo = raceTimesInfo; // TODO bug 3122: when not redrawn yet because min/max not initialized yet, further redraw is disabled by this.
}
@Override
@@ -181,6 +188,15 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
}
}
@Override
public void setMinMax(Date min, Date max, boolean fireEvent) {
super.setMinMax(min, max, fireEvent);
if (redrawAllMarkersPendingForMinMaxBeingInitialized && timeSlider.isMinMaxInitialized()) {
redrawAllMarkers(lastRaceTimesInfo);
redrawAllMarkersPendingForMinMaxBeingInitialized = false;
}
}
/**
* When in {@link PlayModes#Replay} mode, tries to put the {@link #timer} to the time point when the last leg was
* finished first. If this time point is not (yet?) known, tries to put the {@link #timer} to the
@@ -239,8 +255,12 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
requiresMarkerUpdate = true;
}
}
if (requiresMarkerUpdate && timeSlider.isMinMaxInitialized()) {
if (requiresMarkerUpdate) {
if (timeSlider.isMinMaxInitialized()) { // TODO bug 3122 later, the lastRaceTimesInfo will be updated even if not redrawn because min/max not initialized
redrawAllMarkers(newRaceTimesInfo);
} else {
redrawAllMarkersPendingForMinMaxBeingInitialized = true;
}
}
}
@@ -9,7 +9,7 @@ package com.sap.sailing.gwt.ui.client;
*/
public interface RequiresDataInitialization {
public void initializeData(boolean showMapControls);
public void initializeData(boolean showMapControls, boolean showHeaderPanel);
public boolean isDataInitialized();
}
@@ -614,4 +614,6 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
ArrayList<LeaderboardGroupDTO> getLeaderboardGroupsByEventId(UUID id);
boolean doesRegattaLogContainCompetitors(String name) throws DoesNotHaveRegattaLogException;
RegattaAndRaceIdentifier getRaceIdentifier(String regattaLikeName, String raceColumnName, String fleetName);
}
@@ -719,4 +719,6 @@ public interface SailingServiceAsync extends BuildVersionRetriever, FileStorageM
void getLeaderboardGroupsByEventId(UUID id, AsyncCallback<ArrayList<LeaderboardGroupDTO>> callback);
void doesRegattaLogContainCompetitors(String name, AsyncCallback<Boolean> regattaLogCallBack);
void getRaceIdentifier(String regattaLikeName, String raceColumnName, String fleetName, AsyncCallback<RegattaAndRaceIdentifier> asyncCallback);
}
@@ -1340,5 +1340,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String selectCompetitor();
String points();
String numberOfRacesShort();
String courseGeometry();
String degreesToWind(String diff);
String miniLeeaderboardLegendText();
}
@@ -881,7 +881,7 @@ errorRunningDataMiningQuery=An error occured running the query
hideToolbar=Hide toolbar
showSeriesLeaderboards=Show series leaderboards
showOverallLeaderboard=Show overall leaderboard
lineAngleToWindAndAdvantage=length: {0}m; angle to wind: {1}deg; {2} side has {3}m advantage
lineAngleToWindAndAdvantage=length: {0}m\nangle to wind: {1}deg\n{2} side has {3}m advantage
doYouReallyWantToRemoveEvents=Do you really want to remove the events?
doYouReallyWantToRemoveEvent=Do you really want to remove event {0}?
line=Line
@@ -1309,4 +1309,6 @@ hoursAgo={0} h ago
selectCompetitor=Please select one competitor
points=Points
numberOfRacesShort=# Races
courseGeometry=Course Geometry
degreesToWind={0}° to wind
miniLeeaderboardLegendText=Shows the points along with the number of races per competitor
@@ -877,7 +877,7 @@ errorRunningDataMiningQuery=Es ist ein Felher bei der Datenabfrage aufgetreten
hideToolbar=Toolbar ausblenden
showSeriesLeaderboards=Ergebnisse der Serie anzeigen
showOverallLeaderboard=Gesamtergebnisse anzeigen
lineAngleToWindAndAdvantage=Länge: {0}m; Winkel zum Wind: {1}°; {2}-Seite ist {3}m bevorteilt
lineAngleToWindAndAdvantage=Länge: {0}m\nWinkel zum Wind: {1}°\n{2}-Seite ist {3}m bevorteilt
doYouReallyWantToRemoveEvents=Möchten Sie diese Veranstaltungen wirklich entfernen?
doYouReallyWantToRemoveEvent=Möchten Sie wirklich die Veranstaltung {0} entfernen?
line=Linie
@@ -1299,4 +1299,6 @@ hoursAgo=vor {0} h
selectCompetitor=Bitte einen Teilnehmer auswählen
points=Punkte
numberOfRacesShort=# Rennen
courseGeometry=Kursgeometrie
degreesToWind={0}° zum Wind
miniLeeaderboardLegendText=Es werden die Punkte und die Anzahl Rennen je Team angezeigt
@@ -870,7 +870,7 @@ errorRunningDataMiningQuery=Ошибка при выполнении запро
hideToolbar=Скрыть панель инструментов
showSeriesLeaderboards=Показать таблицу лидеров серии
showOverallLeaderboard=Показать общую таблицу лидеров
lineAngleToWindAndAdvantage=длина: {0}м; угол к ветру: {1}градус; {2} борт {3}м имеет преимущество
lineAngleToWindAndAdvantage=длина: {0}м\nугол к ветру: {1}градус\n{2} борт {3}м имеет преимущество
doYouReallyWantToRemoveEvents=Вы действительно хотите удалить все события?
doYouReallyWantToRemoveEvent=Вы действительно хотите удалить событие {0}?
line=Строка
@@ -1246,4 +1246,6 @@ hoursAgo={0} h часов назад
selectCompetitor=Please select one competitor
points=Points
numberOfRacesShort=# Races
courseGeometry=Course Geometry
degreesToWind={0}° to wind
miniLeeaderboardLegendText=Shows points along with the number of races per competitor
@@ -391,7 +391,7 @@ listRaces=List Races
dropReplicaConnection=Drop connection
errorTryingToObtainOverallLeaderboards=Error trying to obtain overall leaderboards: {0}
distanceTraveled=行驶距离
lineAngleToWindAndAdvantage=length: {0}m; angle to wind: {1}deg; {2} side has {3}m advantage
lineAngleToWindAndAdvantage=length: {0}m\nangle to wind: {1}deg\n{2} side has {3}m advantage
mapDevices=Map Devices to Competitors and Marks
scoringSchemeHighPointFirstGetsTenOrEight=High Point, winner gets 10 points (or 8)
playModeLive=Live
@@ -1295,4 +1295,6 @@ openLeaderboard=Open Leaderboard
selectCompetitor=Please select one competitor
points=Points
numberOfRacesShort=# Races
courseGeometry=Course Geometry
degreesToWind={0}° to wind
miniLeeaderboardLegendText=Shows points along with the number of races per competitor
@@ -20,6 +20,9 @@ import com.google.gwt.user.client.ui.RequiresResize;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.TimePanelCssResources.TimePanelCss;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.gwt.client.controls.slider.SliderBar;
import com.sap.sse.gwt.client.controls.slider.TimeSlider;
import com.sap.sse.gwt.client.player.PlayStateListener;
@@ -331,9 +334,13 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
if (timeRangeProvider.isZoomed()) {
timeSlider.setCurrentValue(new Double(newTime.getTime()), false);
} else {
if (getFromTime() == null || getToTime() == null) {
initializeTimeRange(oldTime, newTime);
}
if (getFromTime() != null && getToTime() != null) {
// handle the case where time advances beyond slider's end.
if (newTime.after(getToTime())) {
// in live mode, expand the range
switch (timer.getPlayMode()) {
case Live:
Date newMaxTime = new Date(newTime.getTime());
@@ -343,6 +350,7 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
setMinMax(getFromTime(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max
break;
case Replay:
// in replay, stop playing when the end of the range has been reached
timer.pause();
break;
}
@@ -362,6 +370,22 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
}
}
/**
* When the time range is not yet properly initialized and we get a time change, adjust the time range so that old and
* new time are in range. A margin of one minute is used before and after. This method assumes that at least one of the
* two time points passed as parameters has a value other than <code>null</code>. Should the other be <code>null</code>,
* it is ignored. No assumptions are made about the temporal relation of the two time points to each other; in other
* words, <code>oldTime</code> may also be after <code>newTime<code> which could, e.g., be the case for a timer running
* backwards.
*/
private void initializeTimeRange(Date oldTime, Date newTime) {
TimePoint from = Util.getEarliestOfTimePoints(getFromTime()==null?null:new MillisecondsTimePoint(getFromTime()), oldTime==null?null:new MillisecondsTimePoint(oldTime));
from = Util.getEarliestOfTimePoints(from, newTime==null?null:new MillisecondsTimePoint(newTime));
TimePoint to = Util.getEarliestOfTimePoints(getToTime()==null?null:new MillisecondsTimePoint(getToTime()), oldTime==null?null:new MillisecondsTimePoint(oldTime));
from = Util.getEarliestOfTimePoints(to, newTime==null?null:new MillisecondsTimePoint(newTime));
timeRangeProvider.setTimeRange(from==null?null:from.asDate(), to==null?null:to.asDate());
}
protected String getTimeToStartLabelText(Date time) {
return null;
}
@@ -396,13 +420,9 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
assert min != null && max != null;
boolean changed = false;
if (!max.equals(timeRangeProvider.getToTime())) {
if (!max.equals(timeRangeProvider.getToTime()) || !min.equals(timeRangeProvider.getFromTime())) {
changed = true;
timeSlider.setMaxValue(new Double(max.getTime()), fireEvent);
}
if (!min.equals(timeRangeProvider.getFromTime())) {
changed = true;
timeSlider.setMinValue(new Double(min.getTime()), fireEvent);
timeSlider.setMinAndMaxValue(new Double(min.getTime()),new Double(max.getTime()), fireEvent);
}
if (changed) {
if (!timeRangeProvider.isZoomed()) {
@@ -595,4 +615,8 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
public String getDependentCssClassName() {
return "timePanel";
}
public Button getBackToLiveButton() {
return backToLivePlayButton;
}
}
@@ -44,6 +44,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.ColorMapImpl;
import com.sap.sailing.gwt.ui.actions.GetWindInfoAction;
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -86,7 +87,9 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
* @param raceSelectionProvider
* if <code>null</code>, this chart won't update its contents automatically upon race selection change;
* otherwise, whenever the selection changes, the wind data of the race selected now is loaded from the
* server and displayed in this chart. If no race is selected, the chart is cleared.
* server and displayed in this chart. If no race is selected, the chart is cleared. The caller of this
* constructor must ensure to trigger {@link RaceSelectionChangeListener#onRaceSelectionChange(List)} at
* least once to ensure that this chart sets its {@link AbstractRaceChart#selectedRaceIdentifier} field.
*/
public WindChart(SailingServiceAsync sailingService, RaceSelectionProvider raceSelectionProvider, Timer timer,
TimeRangeWithZoomProvider timeRangeWithZoomProvider, WindChartSettings settings, final StringMessages stringMessages,
@@ -518,12 +521,8 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
*/
@Override
public void timeChanged(Date newTime, Date oldTime) {
if(!isVisible()) {
return;
}
if (isVisible()) {
updateTimePlotLine(newTime);
switch (timer.getPlayMode()) {
case Live:
{
@@ -553,6 +552,7 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
}
}
}
}
private void updateTimePlotLine(Date date) {
chart.getXAxis().removePlotLine(timePlotLine);
@@ -100,16 +100,19 @@ import com.sap.sailing.gwt.ui.client.shared.filter.QuickRankProvider;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings.HelpLineTypes;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes;
import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LegInfoDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.SidelineDTO;
import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO;
import com.sap.sailing.gwt.ui.shared.WaypointDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO;
@@ -118,6 +121,7 @@ import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapStyleHelper;
import com.sap.sailing.gwt.ui.shared.racemap.RaceSimulationOverlay;
import com.sap.sailing.gwt.ui.shared.racemap.WindStreamletsRaceboardOverlay;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.Util.Triple;
import com.sap.sse.common.filter.Filter;
import com.sap.sse.common.filter.FilterSet;
@@ -209,11 +213,24 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
private AdvantageLineMouseOverMapHandler advantageLineMouseOverHandler;
/**
* Polyline for the course middle line.
* Polylines for the course middle lines; keys are the two control points delimiting the leg for which the
* {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there
* are multiple legs using the same control points in different directions, using a {@link Set} makes this
* independent of the order of the two control points. If no course middle line is currently being shown for
* a pair of control points, the map will not contain a value for this pair.
*/
private Polyline courseMiddleLine;
private final Map<Set<ControlPointDTO>, Polyline> courseMiddleLines;
private Map<SidelineDTO, Polygon> courseSidelines;
private final Map<SidelineDTO, Polygon> courseSidelines;
/**
* When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the
* lines that show the tooltip text in a little info box linked to the line. When the line is removed by
* {@link #showOrRemoveOrUpdateLine(Polyline, boolean, Position, Position, LineInfoProvider, String)}, these
* overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is
* deactivated, all these overlays need to go.
*/
private final Map<Polyline, SmallTransparentInfoOverlay> infoOverlaysForLinesForCourseGeometry;
/**
* Wind data used to display the advantage line. Retrieved by a {@link GetWindInfoAction} execution and used in
@@ -382,7 +399,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
public RaceMap(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
ErrorReporter errorReporter, Timer timer, CompetitorSelectionProvider competitorSelection,
StringMessages stringMessages, boolean showMapControls, boolean showViewStreamlets, boolean showViewSimulation,
RegattaAndRaceIdentifier raceIdentifier, CombinedWindPanelStyle combinedWindPanelStyle) {
RegattaAndRaceIdentifier raceIdentifier, CombinedWindPanelStyle combinedWindPanelStyle, boolean showHeaderPanel) {
this.setSize("100%", "100%");
this.showMapControls = showMapControls;
this.stringMessages = stringMessages;
@@ -394,7 +411,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
timer.addTimeListener(this);
raceMapImageManager = new RaceMapImageManager();
markDTOs = new HashMap<String, MarkDTO>();
courseSidelines = new HashMap<SidelineDTO, Polygon>();
courseSidelines = new HashMap<>();
courseMiddleLines = new HashMap<>();
infoOverlaysForLinesForCourseGeometry = new HashMap<>();
boatOverlays = new HashMap<CompetitorDTO, BoatOverlay>();
competitorInfoOverlays = new HashMap<CompetitorDTO, CompetitorInfoOverlay>();
windSensorOverlays = new HashMap<WindSource, WindSensorOverlay>();
@@ -414,7 +433,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
headerPanel.setStyleName("RaceMap-HeaderPanel");
panelForLeftHeaderLabels = new AbsolutePanel();
panelForRightHeaderLabels = new AbsolutePanel();
initializeData(showMapControls);
initializeData(showMapControls, showHeaderPanel);
combinedWindPanel = new CombinedWindPanel(raceMapImageManager, combinedWindPanelStyle, stringMessages, coordinateSystem);
combinedWindPanel.setVisible(false);
trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, combinedWindPanelStyle, stringMessages, coordinateSystem);
@@ -483,8 +502,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
public void execute() {
if (map != null) {
map.setOptions(mapOptions);
final LatLngBounds newBounds = getDefaultZoomBounds();
zoomMapToNewBounds(newBounds);
// ensure zooming to what the settings tell, or defaults if what the settings tell isn't possible right now
mapFirstZoomDone = false;
trueNorthIndicatorPanel.redraw();
orientationChangeInProgress = false;
}
@@ -492,7 +511,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
});
}
private void loadMapsAPIV3(final boolean showMapControls) {
private void loadMapsAPIV3(final boolean showMapControls, final boolean showHeaderPanel) {
boolean sensor = true;
// load all the libs for use in the maps
@@ -506,8 +525,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false);
map = new MapWidget(mapOptions);
RaceMap.this.add(map, 0, 0);
if (showHeaderPanel) {
Image sapLogo = createSAPLogo();
RaceMap.this.add(sapLogo);
}
map.setControls(ControlPosition.LEFT_TOP, combinedWindPanel);
combinedWindPanel.getParent().addStyleName("CombinedWindPanelParentDiv");
map.setControls(ControlPosition.LEFT_TOP, trueNorthIndicatorPanel);
@@ -592,20 +613,27 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
simulationOverlay.addToMap();
simulationOverlay.setVisible(false);
}
if (showHeaderPanel) {
createHeaderPanel(map);
}
createSettingsButton(map);
// Data has been initialized
RaceMap.this.isMapInitialized = true;
RaceMap.this.redraw();
trueNorthIndicatorPanel.redraw();
showAdditionalControls(map);
}
};
LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey);
}
/**
* Subclasses may define additional stuff to be shown on the map.
*/
protected void showAdditionalControls(MapWidget map) {
}
private void setHasPolar() {
GetPolarAction getPolar = new GetPolarAction(sailingService, raceIdentifier);
asyncActionsExecutor.execute(getPolar, GET_POLAR_CATEGORY,
@@ -753,7 +781,6 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
getRaceMapDataCallback(oldTime, newTime, fromAndToAndOverlap.getC(), competitorsToShow, requestID));
// draw the wind into the map, get the combined wind
// TODO bug2057 also fetch wind for LEG_MIDDLE for all legs because this needs to be the basis for the advantage line display
List<String> windSourceTypeNames = new ArrayList<String>();
windSourceTypeNames.add(WindSourceType.EXPEDITION.name());
windSourceTypeNames.add(WindSourceType.COMBINED.name());
@@ -879,7 +906,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
updateCoordinateSystemFromSettings();
}
showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines);
showStartAndFinishLines(raceMapDataDTO.coursePositions);
showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions);
showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions);
// even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to
// adjust to new boat positions
@@ -1169,10 +1196,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
// this only works because the quickRanks are sorted
for (Entry<CompetitorDTO, Integer> competitorsByWindwardDistanceTraveledAndOneBasedLegNumber :
competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber.entrySet()) {
if (Util.contains(competitorsToShow, competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey())) {
if (Util.contains(competitorsToShow, competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey()) &&
competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue() != null) {
leadingCompetitorDTO = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey();
legOfLeaderCompetitor = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue() == null ?
0 : competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue();
legOfLeaderCompetitor = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue();
return new com.sap.sse.common.Util.Pair<Integer, CompetitorDTO>(legOfLeaderCompetitor, leadingCompetitorDTO);
}
}
@@ -1297,103 +1324,66 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
private final StringBuilder leewardStartLineMarkToFirstMarkLineText = new StringBuilder();
private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO){
if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)
&& courseDTO.startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1) {
LatLng windwardStartLinePoint = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(0));
LatLng leewardStartLinePoint = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(1));
LatLng firstMarkPoint = coordinateSystem.toLatLng(courseDTO.waypointPositions.get(1));
final List<Position> startMarkPositions = courseDTO.getStartMarkPositions();
final Position windwardStartLinePosition = startMarkPositions.get(0);
final Position leewardStartLinePosition = startMarkPositions.get(1);
final Position firstMarkPosition = courseDTO.waypointPositions.get(1);
windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(),
stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal
.format(courseDTO.startMarkPositions.get(0).getDistance(courseDTO.waypointPositions.get(1))
.format(windwardStartLinePosition.getDistance(firstMarkPosition)
.getMeters())));
leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(),
stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal
.format(courseDTO.startMarkPositions.get(1).getDistance(courseDTO.waypointPositions.get(1))
.format(leewardStartLinePosition.getDistance(firstMarkPosition)
.getMeters())));
if (windwardStartLineMarkToFirstMarkLine == null && leewardStartLineMarkToFirstMarkLine == null) {
PolylineOptions options = PolylineOptions.newInstance();
options.setGeodesic(true);
options.setStrokeColor("grey");
options.setStrokeWeight(1);
options.setStrokeOpacity(1.0);
MVCArray<LatLng> windwardLinePoints = MVCArray.newInstance();
windwardLinePoints.insertAt(0, windwardStartLinePoint);
windwardLinePoints.insertAt(1, firstMarkPoint);
MVCArray<LatLng> leewardLinePoints = MVCArray.newInstance();
leewardLinePoints.insertAt(0, leewardStartLinePoint);
leewardLinePoints.insertAt(1, firstMarkPoint);
windwardStartLineMarkToFirstMarkLine = Polyline.newInstance(options);
windwardStartLineMarkToFirstMarkLine.setPath(windwardLinePoints);
windwardStartLineMarkToFirstMarkLine.addMouseOverHandler(new MouseOverMapHandler() {
final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() {
@Override
public void onEvent(MouseOverMapEvent event) {
map.setTitle(windwardStartLineMarkToFirstMarkLineText.toString());
public String getLineInfo() {
return windwardStartLineMarkToFirstMarkLineText.toString();
}
});
windwardStartLineMarkToFirstMarkLine.addMouseOutMoveHandler(new MouseOutMapHandler() {
};
final LineInfoProvider leewardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
}
});
leewardStartLineMarkToFirstMarkLine = Polyline.newInstance(options);
leewardStartLineMarkToFirstMarkLine.setPath(leewardLinePoints);
leewardStartLineMarkToFirstMarkLine.addMouseOverHandler(new MouseOverMapHandler() {
@Override
public void onEvent(MouseOverMapEvent event) {
map.setTitle(leewardStartLineMarkToFirstMarkLineText.toString());
}
});
leewardStartLineMarkToFirstMarkLine.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
}
});
windwardStartLineMarkToFirstMarkLine.setMap(map);
leewardStartLineMarkToFirstMarkLine.setMap(map);
} else {
windwardStartLineMarkToFirstMarkLine.getPath().removeAt(1);
windwardStartLineMarkToFirstMarkLine.getPath().removeAt(0);
windwardStartLineMarkToFirstMarkLine.getPath().insertAt(0, windwardStartLinePoint);
windwardStartLineMarkToFirstMarkLine.getPath().insertAt(1, firstMarkPoint);
leewardStartLineMarkToFirstMarkLine.getPath().removeAt(1);
leewardStartLineMarkToFirstMarkLine.getPath().removeAt(0);
leewardStartLineMarkToFirstMarkLine.getPath().insertAt(0, leewardStartLinePoint);
leewardStartLineMarkToFirstMarkLine.getPath().insertAt(1, firstMarkPoint);
}
} else {
if (windwardStartLineMarkToFirstMarkLine != null) {
windwardStartLineMarkToFirstMarkLine.setMap(null);
windwardStartLineMarkToFirstMarkLine = null;
}
if (leewardStartLineMarkToFirstMarkLine != null) {
leewardStartLineMarkToFirstMarkLine.setMap(null);
leewardStartLineMarkToFirstMarkLine = null;
}
public String getLineInfo() {
return leewardStartLineMarkToFirstMarkLineText.toString();
}
};
windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE))
&& startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1,
windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider,
"grey");
leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE))
&& startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1,
leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider,
"grey");
}
private final StringBuilder startLineAdvantageText = new StringBuilder();
private final StringBuilder finishLineAdvantageText = new StringBuilder();
final LineInfoProvider startLineInfoProvider = new LineInfoProvider() {
@Override
public String getLineInfo() {
return stringMessages.startLine()+startLineAdvantageText;
}
};
final LineInfoProvider finishLineInfoProvider = new LineInfoProvider() {
@Override
public String getLineInfo() {
return stringMessages.finishLine()+finishLineAdvantageText;
}
};
private void showStartAndFinishLines(final CoursePositionsDTO courseDTO) {
if (map != null && courseDTO != null && lastRaceTimesInfo != null) {
com.sap.sse.common.Util.Pair<Integer, CompetitorDTO> leadingVisibleCompetitorInfo = getFarthestAheadVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow());
int legOfLeadingCompetitor = leadingVisibleCompetitorInfo == null ? -1 : leadingVisibleCompetitorInfo.getA();
int numberOfLegs = lastRaceTimesInfo.legInfos.size();
private void showStartAndFinishAndCourseMiddleLines(final CoursePositionsDTO courseDTO) {
if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null &&
!courseDTO.course.waypoints.isEmpty()) {
// draw the start line
updateCountdownCanvas(courseDTO.startMarkPositions);
if (legOfLeadingCompetitor <= 1 &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) && courseDTO.startMarkPositions != null && courseDTO.startMarkPositions.size() == 2) {
LatLng startLinePoint1 = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(0));
LatLng startLinePoint2 = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(1));
final WaypointDTO startWaypoint = courseDTO.course.waypoints.get(0);
updateCountdownCanvas(startWaypoint);
final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 : courseDTO.getStartMarkPositions().size();
final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null : courseDTO.getStartMarkPositions().get(0);
final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null : courseDTO.getStartMarkPositions().get(1);
if (courseDTO.startLineAngleToCombinedWind != null) {
startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage(
NumberFormat.getFormat("0.0").format(courseDTO.startLineLengthInMeters),
@@ -1403,52 +1393,16 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
} else {
startLineAdvantageText.delete(0, startLineAdvantageText.length());
}
if (startLine == null) {
PolylineOptions options = PolylineOptions.newInstance();
options.setClickable(true);
options.setGeodesic(true);
options.setStrokeColor("#FFFFFF");
options.setStrokeWeight(1);
options.setStrokeOpacity(1.0);
MVCArray<LatLng> pointsAsArray = MVCArray.newInstance();
pointsAsArray.insertAt(0, startLinePoint1);
pointsAsArray.insertAt(1, startLinePoint2);
startLine = Polyline.newInstance(options);
startLine.setPath(pointsAsArray);
startLine.addMouseOverHandler(new MouseOverMapHandler() {
@Override
public void onEvent(MouseOverMapEvent event) {
map.setTitle(stringMessages.startLine()+startLineAdvantageText);
}
});
startLine.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
}
});
startLine.setMap(map);
} else {
startLine.getPath().removeAt(1);
startLine.getPath().removeAt(0);
startLine.getPath().insertAt(0, startLinePoint1);
startLine.getPath().insertAt(1, startLinePoint2);
}
} else {
if (startLine != null) {
startLine.setMap(null);
startLine = null;
}
}
startLine = showOrRemoveOrUpdateLine(startLine, /* showLine */ numberOfStartWaypointMarks == 2 &&
((courseDTO.currentLegNumber <= 1 &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE)) ||
settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)),
startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff");
// draw the finish line
if (legOfLeadingCompetitor > 0 && legOfLeadingCompetitor == numberOfLegs &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) && courseDTO.finishMarkPositions != null && courseDTO.finishMarkPositions.size() == 2) {
LatLng finishLinePoint1 = coordinateSystem.toLatLng(courseDTO.finishMarkPositions.get(0));
LatLng finishLinePoint2 = coordinateSystem.toLatLng(courseDTO.finishMarkPositions.get(1));
if (courseDTO.startLineAngleToCombinedWind != null) {
final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 : courseDTO.getFinishMarkPositions().size();
final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0);
final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1);
if (courseDTO.finishLineAngleToCombinedWind != null) {
finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage(
NumberFormat.getFormat("0.0").format(courseDTO.finishLineLengthInMeters),
NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.finishLineAngleToCombinedWind)),
@@ -1457,94 +1411,177 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
} else {
finishLineAdvantageText.delete(0, finishLineAdvantageText.length());
}
if (finishLine == null) {
PolylineOptions options = PolylineOptions.newInstance();
options.setClickable(true);
options.setGeodesic(true);
options.setStrokeColor("#000000");
options.setStrokeWeight(1);
options.setStrokeOpacity(1.0);
finishLine = showOrRemoveOrUpdateLine(finishLine, /* showLine */ numberOfFinishWaypointMarks == 2 &&
(courseDTO.currentLegNumber == courseDTO.totalLegsCount &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE))
|| settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY),
finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, "#000000");
// the control point pairs for which we already decided whether or not
// to show a course middle line for; values tell whether to show the line and for which zero-based
// start waypoint index to do so; when for an equal control point pair multiple decisions with different
// outcome are made, a decision to show the line overrules the decision to not show it (OR-semantics)
final Map<Set<ControlPointDTO>, Pair<Boolean, Integer>> keysAlreadyHandled = new HashMap<>();
for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint<courseDTO.waypointPositions.size()-1; zeroBasedIndexOfStartWaypoint++) {
final Set<ControlPointDTO> key = getCourseMiddleLinesKey(courseDTO, zeroBasedIndexOfStartWaypoint);
boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() ||
settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) ||
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE)
&& courseDTO.currentLegNumber > 0
&& courseDTO.currentLegNumber-1 == zeroBasedIndexOfStartWaypoint);
keysAlreadyHandled.put(key, new Pair<>(showCourseMiddleLine, zeroBasedIndexOfStartWaypoint));
}
Set<Set<ControlPointDTO>> keysToConsider = new HashSet<>(keysAlreadyHandled.keySet());
keysToConsider.addAll(courseMiddleLines.keySet());
for (final Set<ControlPointDTO> key : keysToConsider) {
final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) ?
keysAlreadyHandled.get(key).getB() : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter
final Pair<Boolean, Integer> showLineAndZeroBasedIndexOfStartWaypoint = keysAlreadyHandled.get(key);
final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null && showLineAndZeroBasedIndexOfStartWaypoint.getA();
courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), zeroBasedIndexOfStartWaypoint, showCourseMiddleLine));
}
}
}
MVCArray<LatLng> pointsAsArray = MVCArray.newInstance();
pointsAsArray.insertAt(0, finishLinePoint1);
pointsAsArray.insertAt(1, finishLinePoint2);
/**
* Given a zero-based index into <code>courseDTO</code>'s {@link RaceCourseDTO#waypoints waypoints list} that denotes the start
* waypoint of the leg in question, returns a key that can be used for the {@link #courseMiddleLines} map, consisting of a set
* that holds the two {@link ControlPointDTO}s representing the start and finish control point of that leg.
*/
private Set<ControlPointDTO> getCourseMiddleLinesKey(final CoursePositionsDTO courseDTO,
final int zeroBasedIndexOfStartWaypoint) {
ControlPointDTO startControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint).controlPoint;
ControlPointDTO endControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint+1).controlPoint;
final Set<ControlPointDTO> key = new HashSet<>();
key.add(startControlPoint);
key.add(endControlPoint);
return key;
}
finishLine = Polyline.newInstance(options);
finishLine.setPath(pointsAsArray);
finishLine.addMouseOverHandler(new MouseOverMapHandler() {
/**
* @param showLine
* tells whether or not to show the line; if the <code>lineToShowOrRemoveOrUpdate</code> references a
* line but the line shall not be shown, the line is removed from the map; conversely, if the line is not
* yet shown but shall be, a new line is created, added to the map and returned. If the line is shown and
* shall continue to be shown, the line is returned after updating its vertex coordinates.
* @return <code>null</code> if the line is not shown; the polyline object representing the line being displayed
* otherwise
*/
private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, Polyline lineToShowOrRemoveOrUpdate,
final int zeroBasedIndexOfStartWaypoint, final boolean showLine) {
final Position position1DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint);
final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint+1);
final LineInfoProvider lineInfoProvider = new LineInfoProvider() {
@Override
public void onEvent(MouseOverMapEvent event) {
map.setTitle(stringMessages.finishLine()+finishLineAdvantageText);
}
});
finishLine.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
}
});
finishLine.setMap(map);
} else {
finishLine.getPath().removeAt(1);
finishLine.getPath().removeAt(0);
finishLine.getPath().insertAt(0, finishLinePoint1);
finishLine.getPath().insertAt(1, finishLinePoint2);
public String getLineInfo() {
final StringBuilder sb = new StringBuilder();
sb.append(stringMessages.courseMiddleLine());
sb.append('\n');
sb.append(NumberFormat.getFormat("0").format(
Math.abs(position1DTO.getDistance(position2DTO).getMeters()))+stringMessages.metersUnit());
if (lastCombinedWindTrackInfoDTO != null) {
final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO.getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint);
if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null && !windTrackAtLegMiddle.windFixes.isEmpty()) {
WindDTO windAtLegMiddle = windTrackAtLegMiddle.windFixes.get(0);
final double legBearingDeg = position1DTO.getBearingGreatCircle(position2DTO).getDegrees();
final String diff = NumberFormat.getFormat("0.0").format(
Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg-legBearingDeg),
Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg-legBearingDeg)));
sb.append(", ");
sb.append(stringMessages.degreesToWind(diff));
}
}
else {
if (finishLine != null) {
finishLine.setMap(null);
finishLine = null;
return sb.toString();
}
};
return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0");
}
// draw the course middle line
if (legOfLeadingCompetitor > 0 && courseDTO.waypointPositions.size() > legOfLeadingCompetitor &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE)) {
Position position1DTO = courseDTO.waypointPositions.get(legOfLeadingCompetitor-1);
Position position2DTO = courseDTO.waypointPositions.get(legOfLeadingCompetitor);
private interface LineInfoProvider {
String getLineInfo();
}
/**
* @param showLine
* tells whether or not to show the line; if the <code>lineToShowOrRemoveOrUpdate</code> references a
* line but the line shall not be shown, the line is removed from the map; conversely, if the line is not
* yet shown but shall be, a new line is created, added to the map and returned. If the line is shown and
* shall continue to be shown, the line is returned after updating its vertex coordinates.
* @return <code>null</code> if the line is not shown; the polyline object representing the line being displayed
* otherwise
*/
private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine,
final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, String lineColorRGB) {
if (showLine) {
LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO);
LatLng courseMiddleLinePoint2 = coordinateSystem.toLatLng(position2DTO);
if (courseMiddleLine == null) {
final MVCArray<LatLng> pointsAsArray;
if (lineToShowOrRemoveOrUpdate == null) {
PolylineOptions options = PolylineOptions.newInstance();
options.setClickable(true);
options.setGeodesic(true);
options.setStrokeColor("#2268a0");
options.setStrokeColor(lineColorRGB);
options.setStrokeWeight(1);
options.setStrokeOpacity(1.0);
MVCArray<LatLng> pointsAsArray = MVCArray.newInstance();
pointsAsArray.insertAt(0, courseMiddleLinePoint1);
pointsAsArray.insertAt(1, courseMiddleLinePoint2);
courseMiddleLine = Polyline.newInstance(options);
courseMiddleLine.setPath(pointsAsArray);
courseMiddleLine.addMouseOverHandler(new MouseOverMapHandler() {
pointsAsArray = MVCArray.newInstance();
lineToShowOrRemoveOrUpdate = Polyline.newInstance(options);
lineToShowOrRemoveOrUpdate.setPath(pointsAsArray);
lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() {
@Override
public void onEvent(MouseOverMapEvent event) {
map.setTitle(stringMessages.courseMiddleLine());
map.setTitle(lineInfoProvider.getLineInfo());
}
});
courseMiddleLine.addMouseOutMoveHandler(new MouseOutMapHandler() {
lineToShowOrRemoveOrUpdate.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
}
});
courseMiddleLine.setMap(map);
lineToShowOrRemoveOrUpdate.setMap(map);
} else {
courseMiddleLine.getPath().removeAt(1);
courseMiddleLine.getPath().removeAt(0);
courseMiddleLine.getPath().insertAt(0, courseMiddleLinePoint1);
courseMiddleLine.getPath().insertAt(1, courseMiddleLinePoint2);
pointsAsArray = lineToShowOrRemoveOrUpdate.getPath();
pointsAsArray.removeAt(1);
pointsAsArray.removeAt(0);
}
adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider);
pointsAsArray.insertAt(0, courseMiddleLinePoint1);
pointsAsArray.insertAt(1, courseMiddleLinePoint2);
} else {
if (lineToShowOrRemoveOrUpdate != null) {
lineToShowOrRemoveOrUpdate.setMap(null);
adjustInfoOverlayForRemovedLine(lineToShowOrRemoveOrUpdate);
lineToShowOrRemoveOrUpdate = null;
}
}
else {
if (courseMiddleLine != null) {
courseMiddleLine.setMap(null);
courseMiddleLine = null;
return lineToShowOrRemoveOrUpdate;
}
private void adjustInfoOverlayForRemovedLine(Polyline lineToShowOrRemoveOrUpdate) {
SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate);
if (infoOverlay != null) {
infoOverlay.removeFromMap();
}
}
private void adjustInfoOverlayForVisibleLine(Polyline lineToShowOrRemoveOrUpdate, final Position position1DTO,
final Position position2DTO, final LineInfoProvider lineInfoProvider) {
SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate);
if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) {
if (infoOverlay == null) {
infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, lineInfoProvider.getLineInfo(), coordinateSystem);
infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay);
infoOverlay.addToMap();
} else {
infoOverlay.setInfoText(lineInfoProvider.getLineInfo());
}
infoOverlay.setPosition(position1DTO.translateGreatCircle(position1DTO.getBearingGreatCircle(position2DTO),
position1DTO.getDistance(position2DTO).scale(0.5)), /* transition time */ -1);
infoOverlay.draw();
} else {
if (infoOverlay != null) {
infoOverlay.removeFromMap();
infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate);
}
}
}
@@ -1554,9 +1591,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
* still in the pre-start phase, show a {@link SmallTransparentInfoOverlay} at the
* start line that shows the count down.
*/
private void updateCountdownCanvas(List<Position> startMarkPositions) {
if (!settings.isShowSelectedCompetitorsInfo() || startMarkPositions == null || startMarkPositions.isEmpty()
|| lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) {
private void updateCountdownCanvas(WaypointDTO startWaypoint) {
if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null || Util.isEmpty(startWaypoint.controlPoint.getMarks())
|| lastRaceTimesInfo == null || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) {
if (countDownOverlay != null) {
countDownOverlay.removeFromMap();
countDownOverlay = null;
@@ -1572,7 +1609,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
} else {
countDownOverlay.setInfoText(countDownText);
}
countDownOverlay.setPosition(startMarkPositions.get(startMarkPositions.size() - 1), -1);
countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, /* transition time */ -1);
countDownOverlay.draw();
}
}
@@ -1610,7 +1647,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|| !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds)
|| graticuleAreaRatio(currentMapBounds, newBounds) > 10) {
// only change bounds if the new bounds don't fit into the current map zoom
List<ZoomTypes> oldZoomSettings = settings.getZoomSettings().getTypesToConsiderOnZoom();
Iterable<ZoomTypes> oldZoomSettings = settings.getZoomSettings().getTypesToConsiderOnZoom();
setAutoZoomInProgress(true);
autoZoomLatLngBounds = newBounds;
int newZoomLevel = getZoomLevel(autoZoomLatLngBounds);
@@ -2358,8 +2395,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
@Override
public void initializeData(boolean showMapControls) {
loadMapsAPIV3(showMapControls);
public void initializeData(boolean showMapControls, boolean showHeaderPanel) {
loadMapsAPIV3(showMapControls, showHeaderPanel);
}
@Override
@@ -1,16 +1,18 @@
package com.sap.sailing.gwt.ui.client.shared.racemap;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import com.sap.sse.common.Util;
public class RaceMapHelpLinesSettings {
/**
* Types of help lines on the map
*/
public enum HelpLineTypes {
STARTLINE, FINISHLINE, ADVANTAGELINE, COURSEMIDDLELINE, BUOYZONE, BOATTAILS, STARTLINETOFIRSTMARKTRIANGLE
STARTLINE, FINISHLINE, ADVANTAGELINE, COURSEMIDDLELINE, BUOYZONE, BOATTAILS, STARTLINETOFIRSTMARKTRIANGLE, COURSEGEOMETRY
}
private final Set<HelpLineTypes> visibleHelpLines;
@@ -27,14 +29,19 @@ public class RaceMapHelpLinesSettings {
visibleHelpLines.add(HelpLineTypes.BOATTAILS);
}
public RaceMapHelpLinesSettings(Collection<HelpLineTypes> visibleHelpLines) {
this.visibleHelpLines = new HashSet<HelpLineTypes>(visibleHelpLines);
public RaceMapHelpLinesSettings(Iterable<HelpLineTypes> visibleHelpLines) {
this.visibleHelpLines = new HashSet<>();
Util.addAll(visibleHelpLines, this.visibleHelpLines);
}
public boolean isVisible(HelpLineTypes helpLineType) {
return visibleHelpLines.contains(helpLineType);
}
public Iterable<HelpLineTypes> getVisibleHelpLineTypes() {
return Collections.unmodifiableCollection(visibleHelpLines);
}
@Override
public int hashCode() {
final int prime = 31;
@@ -47,6 +47,23 @@ public class RaceMapSettings extends AbstractSettings {
this.helpLinesSettings = new RaceMapHelpLinesSettings();
}
/**
* "Copy constructor" that produces a new settings object that equals the one passed as argument
*/
public RaceMapSettings(RaceMapSettings settings) {
this.buoyZoneRadiusInMeters = settings.buoyZoneRadiusInMeters;
this.helpLinesSettings = new RaceMapHelpLinesSettings(settings.getHelpLinesSettings().getVisibleHelpLineTypes());
this.maneuverTypesToShow = settings.maneuverTypesToShow;
this.showDouglasPeuckerPoints = settings.showDouglasPeuckerPoints;
this.showOnlySelectedCompetitors = settings.showOnlySelectedCompetitors;
this.showSelectedCompetitorsInfo = settings.showSelectedCompetitorsInfo;
this.showSimulationOverlay = settings.showSimulationOverlay;
this.showWindStreamletOverlay = settings.showWindStreamletOverlay;
this.tailLengthInMilliseconds = settings.tailLengthInMilliseconds;
this.windUp = settings.windUp;
this.zoomSettings = new RaceMapZoomSettings(settings.zoomSettings.getTypesToConsiderOnZoom(), settings.zoomSettings.isZoomToSelectedCompetitors());
}
/**
* @return 0 if the tails are not visible {@link #getTailLengthInMilliseconds()} otherwise
*/
@@ -98,7 +98,7 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<R
for (ZoomTypes zoomType : ZoomTypes.values()) {
if (zoomType != ZoomTypes.NONE) {
CheckBox cb = dialog.createCheckbox(RaceMapSettingsTypeFormatter.formatZoomType(zoomType, stringMessages));
cb.setValue(initialSettings.getZoomSettings().getTypesToConsiderOnZoom().contains(zoomType), false);
cb.setValue(Util.contains(initialSettings.getZoomSettings().getTypesToConsiderOnZoom(), zoomType), false);
checkboxAndZoomType.add(new Util.Pair<CheckBox, ZoomTypes>(cb, zoomType));
zoomSettingsBoxesPanel.add(cb);
@@ -195,6 +195,7 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<R
vp.add(createHelpLineCheckBox(dialog, HelpLineTypes.ADVANTAGELINE));
vp.add(createHelpLineCheckBox(dialog, HelpLineTypes.COURSEMIDDLELINE));
vp.add(createHelpLineCheckBox(dialog, HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE));
vp.add(createHelpLineCheckBox(dialog, HelpLineTypes.COURSEGEOMETRY));
return vp;
}
@@ -37,6 +37,8 @@ public class RaceMapSettingsTypeFormatter {
return stringConstants.boatTails();
case STARTLINETOFIRSTMARKTRIANGLE:
return stringConstants.startFirstMarkTriangle();
case COURSEGEOMETRY:
return stringConstants.courseGeometry();
}
return null;
}
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import com.google.gwt.maps.client.base.LatLngBounds;
import com.sap.sse.common.Util;
/**
* @author Lennart Hensler (D054527)
@@ -29,7 +30,7 @@ public class RaceMapZoomSettings {
}
};
private ArrayList<ZoomTypes> typesToConsiderOnZoom;
private Iterable<ZoomTypes> typesToConsiderOnZoom;
private boolean zoomToSelectedCompetitors;
/**
@@ -37,13 +38,14 @@ public class RaceMapZoomSettings {
* The attribute <code>zoomToSelectedCompetitors</code> will be <code>false</code>.
*/
public RaceMapZoomSettings() {
typesToConsiderOnZoom = new ArrayList<ZoomTypes>();
final List<ZoomTypes> myTypesToConsiderOnZoom = new ArrayList<>();
typesToConsiderOnZoom = myTypesToConsiderOnZoom;
// Other zoom types such as BOATS, TAILS or WINDSENSORS are not currently used as default zoom types.
typesToConsiderOnZoom.add(ZoomTypes.BUOYS);
myTypesToConsiderOnZoom.add(ZoomTypes.BUOYS);
zoomToSelectedCompetitors = false;
}
public RaceMapZoomSettings(ArrayList<ZoomTypes> typesToConsiderOnZoom, boolean zoomToSelected) {
public RaceMapZoomSettings(Iterable<ZoomTypes> typesToConsiderOnZoom, boolean zoomToSelected) {
this.typesToConsiderOnZoom = typesToConsiderOnZoom;
this.zoomToSelectedCompetitors = zoomToSelected;
}
@@ -67,12 +69,14 @@ public class RaceMapZoomSettings {
return newBounds;
}
public List<ZoomTypes> getTypesToConsiderOnZoom() {
public Iterable<ZoomTypes> getTypesToConsiderOnZoom() {
return typesToConsiderOnZoom;
}
public void setTypesToConsiderOnZoom(List<ZoomTypes> typesToConsiderOnZoom) {
this.typesToConsiderOnZoom = new ArrayList<ZoomTypes>(typesToConsiderOnZoom);
public void setTypesToConsiderOnZoom(Iterable<ZoomTypes> typesToConsiderOnZoom) {
List<ZoomTypes> newTypesToConsiderOnZoom = new ArrayList<>();
Util.addAll(typesToConsiderOnZoom, newTypesToConsiderOnZoom);
this.typesToConsiderOnZoom = newTypesToConsiderOnZoom;
}
public void setZoomToSelectedCompetitors(boolean zoomToSelectedCompetitors) {
@@ -84,7 +88,7 @@ public class RaceMapZoomSettings {
}
public boolean containsZoomType(ZoomTypes zoomType) {
return typesToConsiderOnZoom.contains(zoomType);
return Util.contains(typesToConsiderOnZoom, zoomType);
}
@Override
@@ -42,32 +42,45 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 {
infoBoxWidth = 20;
infoBoxHeight = 20;
cornerRadius = 4;
if (getCanvas() != null) {
getCanvas().setWidth(String.valueOf(canvasWidth));
getCanvas().setHeight(String.valueOf(canvasHeight));
getCanvas().setCoordinateSpaceWidth(canvasWidth);
getCanvas().setCoordinateSpaceHeight(canvasHeight);
}
// setCanvasSize(canvasWidth, canvasHeight);
}
@Override
protected void draw() {
final int LINE_SPACING = 3;
final int BOTTOM_MARGIN = 8;
final int LEFT_MARGIN = 8;
final int RIGHT_MARGIN = 9;
final int POLE_LENGTH = 25;
if (mapProjection != null && position != null) {
LatLng latLngPosition = coordinateSystem.toLatLng(position);
Context2d context2d = getCanvas().getContext2d();
CssColor grayTransparentColor = CssColor.make("rgba(255,255,255,0.75)");
context2d.setFont("12px bold Verdana sans-serif");
TextMetrics measureText = context2d.measureText(infoText);
double textWidth = measureText.getWidth();
canvasWidth = (int) textWidth + 17;
final int fontSizeInPx = 12+2*Math.max(0, map.getZoom()-15);
final int LINE_HEIGHT = fontSizeInPx+LINE_SPACING;
context2d.setFont(""+fontSizeInPx+"px Roboto, Arial, sans-serif");
// context2d.setFont(""+fontSizeInPx+"px bold Verdana sans-serif");
double textWidth = 0;
infoBoxHeight = BOTTOM_MARGIN;
for (final String line : infoText.split("\n")) {
TextMetrics measureText = context2d.measureText(line);
if (measureText.getWidth() > textWidth) {
textWidth = measureText.getWidth();
}
infoBoxHeight += LINE_HEIGHT;
}
canvasWidth = (int) textWidth + LEFT_MARGIN + RIGHT_MARGIN;
infoBoxWidth = canvasWidth;
canvasHeight = infoBoxHeight + POLE_LENGTH;
getCanvas().setWidth(String.valueOf(canvasWidth));
getCanvas().setCoordinateSpaceWidth(canvasWidth);
getCanvas().setHeight(String.valueOf(canvasHeight));
getCanvas().setCoordinateSpaceHeight(canvasHeight);
// Change origin and dimensions to match true size (a stroke makes the shape a bit larger)
context2d.setFillStyle(grayTransparentColor);
@@ -86,7 +99,13 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 {
context2d.beginPath();
context2d.setFillStyle("black");
context2d.fillText(infoText, 8, 14);
// context2d.setFont(""+fontSizeInPx+"px bold Verdana sans-serif");
context2d.setFont(""+fontSizeInPx+"px Roboto, Arial, sans-serif");
int y = LINE_HEIGHT;
for (final String line : infoText.split("\n")) {
context2d.fillText(line, LEFT_MARGIN, y);
y += LINE_HEIGHT;
}
context2d.stroke();
Point objectPositionInPx = mapProjection.fromLatLngToDivPixel(latLngPosition);
@@ -0,0 +1,216 @@
package com.sap.sailing.gwt.ui.raceboard;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.controls.ControlPosition;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel;
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
import com.sap.sailing.gwt.ui.client.TimePanel;
import com.sap.sailing.gwt.ui.client.TimePanelSettings;
import com.sap.sailing.gwt.ui.client.shared.charts.WindChart;
import com.sap.sailing.gwt.ui.client.shared.charts.WindChartSettings;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings.HelpLineTypes;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapResources;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util;
import com.sap.sse.common.filter.Filter;
import com.sap.sse.common.filter.FilterSet;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.player.TimeRangeWithZoomModel;
import com.sap.sse.gwt.client.player.TimeRangeWithZoomProvider;
import com.sap.sse.gwt.client.player.Timer;
import com.sap.sse.gwt.client.player.Timer.PlayModes;
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint {
private static final String PARAM_REGATTA_LIKE_NAME = "regattaLikeName";
private static final String PARAM_RACE_COLUMN_NAME = "raceColumnName";
private static final String PARAM_FLEET_NAME = "fleetName";
private static final String PARAM_EVENT_ID = "eventId";
private String regattaLikeName;
private String raceColumnName;
private String fleetName;
private UUID eventId;
private static final RaceMapResources raceMapResources = GWT.create(RaceMapResources.class);
private static final int DEFAULT_WIND_CHART_HEIGHT = 200;
@Override
protected void doOnModuleLoad() {
super.doOnModuleLoad();
// read mandatory parameters
regattaLikeName = Window.Location.getParameter(PARAM_REGATTA_LIKE_NAME);
raceColumnName = Window.Location.getParameter(PARAM_RACE_COLUMN_NAME);
fleetName = Window.Location.getParameter(PARAM_FLEET_NAME);
String eventIdParamValue = Window.Location.getParameter(PARAM_EVENT_ID);
if (eventIdParamValue != null && !eventIdParamValue.isEmpty()) {
eventId = UUID.fromString(eventIdParamValue);
}
if (regattaLikeName == null || regattaLikeName.isEmpty() || raceColumnName == null || raceColumnName.isEmpty() ||
fleetName == null || fleetName.isEmpty()) {
createErrorPage("This page requires a valid regatta, race column and fleet name to identify the race to show.");
return;
}
// read optional parameters
final boolean showWindChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_WINDCHART, true /* default*/);
final boolean showViewStreamlets = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_STREAMLETS, false /* default*/);
final boolean showViewSimulation = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_SIMULATION, true /* default*/);
final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, true /* default*/);
if (eventId != null) {
sailingService.getEventById(eventId, /* withStatisticalData */ false, new AsyncCallback<EventDTO>() {
@Override
public void onSuccess(EventDTO eventDTO) {
checkUrlParameters(eventDTO, showWindChart, showMapControls, showViewStreamlets, showViewSimulation);
}
@Override
public void onFailure(Throwable t) {
reportError("Error trying to create the raceboard: " + t.getMessage());
}
});
} else {
checkUrlParameters(/* event */ null, showWindChart, showMapControls, showViewStreamlets, showViewSimulation);
}
}
private void createErrorPage(String message) {
final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX);
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService());
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
RootLayoutPanel.get().add(vp);
vp.addNorth(logoAndTitlePanel, 100);
vp.add(new Label(message));
}
private void checkUrlParameters(final EventDTO event, final boolean showWindChart,
final boolean showMapControls, final boolean showViewStreamlets, final boolean showViewSimulation) {
if (eventId != null && event == null) {
createErrorPage(getStringMessages().noSuchEvent());
}
sailingService.getRaceIdentifier(regattaLikeName, raceColumnName, fleetName, new AsyncCallback<RegattaAndRaceIdentifier>() {
@Override
public void onFailure(Throwable caught) {
createErrorPage("Could not obtain a race with name " + raceColumnName + " for fleet "+fleetName+" for a regatta with name " + regattaLikeName+
": "+caught.getMessage());
}
@Override
public void onSuccess(final RegattaAndRaceIdentifier selectedRaceIdentifier) {
if (selectedRaceIdentifier == null) {
createErrorPage("Could not obtain a race with name " + raceColumnName + " for fleet "+fleetName+" for a regatta with name " + regattaLikeName);
} else {
final StringBuilder title = new StringBuilder(regattaLikeName);
title.append('/');
title.append(raceColumnName);
if (!fleetName.equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) {
title.append('/');
title.append(fleetName);
}
Window.setTitle(title.toString());
final RaceSelectionModel raceSelectionModel = new RaceSelectionModel();
final List<RegattaAndRaceIdentifier> raceList = Collections.singletonList(selectedRaceIdentifier);
raceSelectionModel.setSelection(raceList);
final long refreshInterval = Duration.ONE_SECOND.times(3).asMillis();
final Timer timer = new Timer(PlayModes.Live);
AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
final TimeRangeWithZoomProvider timeRangeWithZoomProvider = new TimeRangeWithZoomModel();
// Use a TimePanel to manage wind chart zoom, although the TimePanel itself is not being displayed;
// let the time panel always return to "live" mode if the user wants that
final TimePanel<TimePanelSettings> timePanel = new TimePanel<TimePanelSettings>(
timer, timeRangeWithZoomProvider, getStringMessages(), /* canReplayWhileLive */ false) {
protected boolean isLiveModeToBeMadePossible() {
return true;
}
};
final Button backToLivePlayButton = timePanel.getBackToLiveButton();
final TimePanelSettings timePanelSettings = timePanel.getSettings();
timePanelSettings.setRefreshInterval(refreshInterval);
timePanel.updateSettings(timePanelSettings);
raceMapResources.combinedWindPanelStyle().ensureInjected();
final CompetitorSelectionProvider competitorSelection = createEmptyFilterCompetitorModel(); // show no competitors
final RaceMap raceMap = new RaceMap(sailingService, asyncActionsExecutor, /* errorReporter */ EmbeddedMapAndWindChartEntryPoint.this, timer,
competitorSelection, getStringMessages(), showMapControls, showViewStreamlets,
showViewSimulation, selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle(), /* showHeaderPanel */ false) {
@Override
protected void showAdditionalControls(MapWidget map) {
backToLivePlayButton.removeFromParent();
map.setControls(ControlPosition.RIGHT_BOTTOM, backToLivePlayButton);
}
};
final RaceMapSettings mapSettings = new RaceMapSettings(raceMap.getSettings());
mapSettings.setZoomSettings(new RaceMapZoomSettings(Arrays.asList(ZoomTypes.BUOYS), /* zoom to selection */ false));
Set<HelpLineTypes> helpLineTypes = new HashSet<>();
Util.addAll(mapSettings.getHelpLinesSettings().getVisibleHelpLineTypes(), helpLineTypes);
helpLineTypes.add(HelpLineTypes.COURSEGEOMETRY);
mapSettings.setHelpLinesSettings(new RaceMapHelpLinesSettings(helpLineTypes));
mapSettings.setWindUp(true);
raceMap.updateSettings(mapSettings);
raceMap.onRaceSelectionChange(raceList);
final WindChart windChart;
if (showWindChart) {
windChart = new WindChart(sailingService, raceSelectionModel, timer,
timeRangeWithZoomProvider, new WindChartSettings(), getStringMessages(),
asyncActionsExecutor, /* errorReporter */
EmbeddedMapAndWindChartEntryPoint.this, /* compactChart */ true);
windChart.onRaceSelectionChange(raceList);
} else {
windChart = null;
}
createRaceBoardInOneScreenMode(raceMap, windChart);
}
}
});
}
private CompetitorSelectionProvider createEmptyFilterCompetitorModel() {
final CompetitorSelectionModel result = new CompetitorSelectionModel(/* hasMultiSelection */ true);
final FilterSet<CompetitorDTO, Filter<CompetitorDTO>> filterSet = result.getOrCreateCompetitorsFilterSet("Empty");
filterSet.addFilter(new Filter<CompetitorDTO>() {
@Override public boolean matches(CompetitorDTO object) { return false; }
@Override public String getName() { return "Never matching filter"; }
});
return result;
}
private void createRaceBoardInOneScreenMode(final RaceMap raceMap, final WindChart windChart) {
final TouchSplitLayoutPanel p = new TouchSplitLayoutPanel(/* horizontal splitter width */ 3, /* vertical splitter height */ 25);
RootLayoutPanel.get().add(p);
if (windChart != null) {
windChart.setVisible(true);
p.insert(windChart.getEntryWidget(), windChart, Direction.SOUTH, DEFAULT_WIND_CHART_HEIGHT);
p.setWidgetVisibility(windChart.getEntryWidget(), windChart, /* hidden */ false, DEFAULT_WIND_CHART_HEIGHT);
}
p.insert(raceMap.getEntryWidget(), raceMap, Direction.CENTER, 400);
p.addStyleName("dockLayoutPanel");
}
}
@@ -17,7 +17,6 @@ import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint;
@@ -138,10 +137,12 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
}
private void createErrorPage(String message) {
final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX);
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService());
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
RootPanel.get().add(logoAndTitlePanel);
RootPanel.get().add(new Label(message));
RootLayoutPanel.get().add(vp);
vp.addNorth(logoAndTitlePanel, 100);
vp.add(new Label(message));
}
private void checkUrlParameters(List<String> leaderboardNames, LeaderboardGroupDTO leaderboardGroup,
@@ -194,7 +195,6 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, this, singletonList, 5000l /* requestInterval*/);
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, getUserService(), asyncActionsExecutor, timer, raceSelectionModel,
leaderboardName, leaderboardGroupName, event, raceboardViewConfig, RaceBoardEntryPoint.this, getStringMessages(), userAgent, raceTimesInfoProvider, showMapControls);
raceBoardPanel.fillRegattas(regattas);
createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig);
}
@@ -35,7 +35,6 @@ import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent;
@@ -54,7 +53,6 @@ import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsFactory;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.common.filter.FilterSet;
import com.sap.sse.common.settings.AbstractSettings;
import com.sap.sse.gwt.client.ErrorReporter;
@@ -77,7 +75,7 @@ import com.sap.sse.security.ui.client.UserService;
* @author Frank Mittag, Axel Uhl (d043530)
*
*/
public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, RaceSelectionChangeListener, LeaderboardUpdateListener, PopupPositionProvider {
public class RaceBoardPanel extends SimplePanel implements RaceSelectionChangeListener, LeaderboardUpdateListener, PopupPositionProvider {
private final SailingServiceAsync sailingService;
private final MediaServiceAsync mediaService;
private final EventDTO event;
@@ -86,11 +84,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
private final RaceBoardViewConfiguration raceboardViewConfiguration;
private String raceBoardName;
/**
* Updated upon each {@link #fillRegattas(List)}
*/
private final Map<RaceIdentifier, RaceDTO> racesByIdentifier;
private final List<ComponentViewer> componentViewers;
private RaceTimePanel timePanel;
private final Timer timer;
@@ -146,7 +139,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
this.currentRaceHasBeenSelectedOnce = false;
this.leaderboardName = leaderboardName;
raceSelectionProvider.addRaceSelectionChangeListener(this);
racesByIdentifier = new HashMap<RaceIdentifier, RaceDTO>();
selectedRaceIdentifier = raceSelectionProvider.getSelectedRaces().iterator().next();
this.setRaceBoardName(selectedRaceIdentifier.getRaceName());
this.asyncActionsExecutor = asyncActionsExecutor;
@@ -163,7 +155,8 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
raceMapResources.combinedWindPanelStyle().ensureInjected();
raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer,
competitorSelectionModel, stringMessages, showMapControls, getConfiguration().isShowViewStreamlets(), getConfiguration().isShowViewSimulation(), selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle());
competitorSelectionModel, stringMessages, showMapControls, getConfiguration().isShowViewStreamlets(), getConfiguration().isShowViewSimulation(),
selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle(), /* showHeaderPanel */ true);
CompetitorFilterPanel competitorSearchTextBox = new CompetitorFilterPanel(competitorSelectionModel, stringMessages, raceMap,
new LeaderboardFetcher() {
@Override
@@ -336,18 +329,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
return errorReporter;
}
@Override
public void fillRegattas(Iterable<RegattaDTO> regattas) {
racesByIdentifier.clear();
for (RegattaDTO regatta : regattas) {
for (RaceDTO race : regatta.races) {
if (race != null && race.getRaceIdentifier() != null) {
racesByIdentifier.put(race.getRaceIdentifier(), race);
}
}
}
}
@Override
public void onRaceSelectionChange(List<RegattaAndRaceIdentifier> selectedRaces) {
}
@@ -902,7 +902,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
CourseBase lastCourse = state.getCourseDesign();
if (lastCourse != null) {
raceInfoDTO.lastCourseDesign = convertCourseDesignToRaceCourseDTO(lastCourse);
raceInfoDTO.lastCourseDesign = convertToRaceCourseDTO(lastCourse, /* trackedRace */ null, /* timePoint */ null);
raceInfoDTO.lastCourseName = lastCourse.getName();
}
@@ -952,20 +952,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
raceInfoDTO.startProcedureDTO = info;
}
private RaceCourseDTO convertCourseDesignToRaceCourseDTO(CourseBase lastCourseDesign) {
RaceCourseDTO result = new RaceCourseDTO(Collections.<WaypointDTO> emptyList());
if (lastCourseDesign != null) {
List<WaypointDTO> waypointDTOs = new ArrayList<WaypointDTO>();
for (Waypoint waypoint : lastCourseDesign.getWaypoints()) {
ControlPointDTO controlPointDTO = convertToControlPointDTO(waypoint.getControlPoint());
WaypointDTO waypointDTO = new WaypointDTO(waypoint.getName(), controlPointDTO, waypoint.getPassingInstructions());
waypointDTOs.add(waypointDTO);
}
result = new RaceCourseDTO(waypointDTOs);
}
return result;
}
private List<RaceWithCompetitorsDTO> convertToRaceDTOs(Regatta regatta) {
List<RaceWithCompetitorsDTO> result = new ArrayList<RaceWithCompetitorsDTO>();
for (RaceDefinition r : regatta.getAllRaces()) {
@@ -1864,6 +1850,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
result.totalLegsCount = trackedRace.getRace().getCourse().getLegs().size();
result.currentLegNumber = trackedRace.getLastLegStarted(dateAsTimePoint);
result.marks = new HashSet<MarkDTO>();
result.course = convertToRaceCourseDTO(trackedRace.getRace().getCourse(), trackedRace, dateAsTimePoint);
// now make sure we don't duplicate the MarkDTO objects but instead use the ones from the RaceCourseDTO
// object and amend them with the Position
result.waypointPositions = new ArrayList<>();
Set<Mark> marks = new HashSet<Mark>();
Course course = trackedRace.getRace().getCourse();
@@ -1876,11 +1865,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
marks.add(b);
}
}
for (Mark mark : marks) {
GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
Position positionAtDate = track.getEstimatedPosition(dateAsTimePoint, /* extrapolate */false);
if (positionAtDate != null) {
result.marks.add(convertToMarkDTO(mark, positionAtDate));
for (final WaypointDTO waypointDTO : result.course.waypoints) {
for (final MarkDTO markDTO : waypointDTO.controlPoint.getMarks()) {
if (markDTO.position != null) {
result.marks.add(markDTO);
}
}
}
@@ -1889,8 +1878,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
if (firstWaypoint != null && Util.size(firstWaypoint.getMarks()) == 2) {
final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getStartLine(dateAsTimePoint);
if (markPositionDTOsAndLineAdvantage != null) {
final List<Position> startMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace, firstWaypoint);
result.startMarkPositions = startMarkPositionDTOs;
result.startLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters();
Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage
.getAbsoluteAngleDifferenceToTrueWind();
@@ -1906,8 +1893,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
if (lastWaypoint != null && Util.size(lastWaypoint.getMarks()) == 2) {
final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getFinishLine(dateAsTimePoint);
if (markPositionDTOsAndLineAdvantage != null) {
final List<Position> finishMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace, lastWaypoint);
result.finishMarkPositions = finishMarkPositionDTOs;
result.finishLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters();
Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage
.getAbsoluteAngleDifferenceToTrueWind();
@@ -1952,32 +1937,22 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private ControlPointDTO convertToControlPointDTO(ControlPoint controlPoint, TrackedRace trackedRace, TimePoint timePoint) {
ControlPointDTO result;
final TimePoint timePointToUse = trackedRace == null ? null :
timePoint == null ? MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis()) : timePoint;
if (controlPoint instanceof ControlPointWithTwoMarks) {
final Mark left = ((ControlPointWithTwoMarks) controlPoint).getLeft();
final Position leftPos = trackedRace.getOrCreateTrack(left).getEstimatedPosition(timePoint, /* extrapolate */ false);
final Position leftPos = timePointToUse == null ? null : trackedRace.getOrCreateTrack(left).getEstimatedPosition(timePointToUse, /* extrapolate */ false);
final Mark right = ((ControlPointWithTwoMarks) controlPoint).getRight();
final Position rightPos = trackedRace.getOrCreateTrack(right).getEstimatedPosition(timePoint, /* extrapolate */ false);
final Position rightPos = timePointToUse == null ? null : trackedRace.getOrCreateTrack(right).getEstimatedPosition(timePointToUse, /* extrapolate */ false);
result = new GateDTO(controlPoint.getId().toString(), controlPoint.getName(), convertToMarkDTO(left, leftPos), convertToMarkDTO(right, rightPos));
} else {
final Position posOfFirst = trackedRace.getOrCreateTrack(controlPoint.getMarks().iterator().next()).
getEstimatedPosition(timePoint, /* extrapolate */ false);
final Position posOfFirst = timePointToUse == null ? null : trackedRace.getOrCreateTrack(controlPoint.getMarks().iterator().next()).
getEstimatedPosition(timePointToUse, /* extrapolate */ false);
result = new MarkDTO(controlPoint.getId().toString(), controlPoint.getName(), posOfFirst == null ? -1 : posOfFirst.getLatDeg(), posOfFirst == null ? -1 : posOfFirst.getLngDeg());
}
return result;
}
private ControlPointDTO convertToControlPointDTO(ControlPoint controlPoint) {
ControlPointDTO result;
if (controlPoint instanceof ControlPointWithTwoMarks) {
final Mark left = ((ControlPointWithTwoMarks) controlPoint).getLeft();
final Mark right = ((ControlPointWithTwoMarks) controlPoint).getRight();
result = new GateDTO(controlPoint.getId().toString(), controlPoint.getName(), convertToMarkDTO(left, null), convertToMarkDTO(right, null));
} else {
result = new MarkDTO(controlPoint.getId().toString(), controlPoint.getName());
}
return result;
}
private ControlPoint getOrCreateControlPoint(ControlPointDTO dto) {
String idAsString = dto.getIdAsString();
if (idAsString == null) {
@@ -2017,19 +1992,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
}
private List<Position> getMarkPositionDTOs(
TimePoint timePoint, TrackedRace trackedRace, Waypoint waypoint) {
List<Position> markPositionDTOs = new ArrayList<>();
for (Mark startMark : waypoint.getMarks()) {
final Position estimatedMarkPosition = trackedRace.getOrCreateTrack(startMark)
.getEstimatedPosition(timePoint, /* extrapolate */false);
if (estimatedMarkPosition != null) {
markPositionDTOs.add(estimatedMarkPosition);
}
}
return markPositionDTOs;
}
/**
* @param timePoint
* <code>null</code> means "live" and is then replaced by "now" minus the tracked race's
@@ -4041,7 +4003,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
Map<String, List<RegattaOverviewEntryDTO>> entriesPerFleet = new HashMap<String, List<RegattaOverviewEntryDTO>>();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
getRegattaOverviewEntries(showOnlyRacesOfSameDay, dayToCheck, usedCourseArea,
leaderboard, boatClass.getName(), regattaName, LeaderboardNameConstants.DEFAULT_SERIES_NAME, raceColumn, entriesPerFleet);
leaderboard, boatClass == null ? "" : boatClass.getName(), regattaName, LeaderboardNameConstants.DEFAULT_SERIES_NAME, raceColumn, entriesPerFleet);
}
result.addAll(getRegattaOverviewEntriesToBeShown(showOnlyCurrentlyRunningRaces, entriesPerFleet));
}
@@ -5090,36 +5052,56 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
raceLog.add(event);
}
private WaypointDTO convertToWaypointDTO(Waypoint waypoint, Map<Serializable, ControlPointDTO> controlPointCache) {
/**
* @param trackedRace
* if <code>null</code>, no position data will be attached to the {@link MarkDTO}s
* @param timePoint
* if <code>trackedRace</code> is not <code>null</code>, specifies the time point for which to determine
* the mark positions and attach to the {@link MarkDTO}s. If <code>null<c/code>, the current time point
* will be used as default.
*/
private WaypointDTO convertToWaypointDTO(Waypoint waypoint, Map<Serializable, ControlPointDTO> controlPointCache, TrackedRace trackedRace, TimePoint timePoint) {
ControlPointDTO cp = controlPointCache.get(waypoint.getControlPoint().getId());
if (cp == null) {
cp = convertToControlPointDTO(waypoint.getControlPoint());
cp = convertToControlPointDTO(waypoint.getControlPoint(), trackedRace, timePoint);
controlPointCache.put(waypoint.getControlPoint().getId(), cp);
}
return new WaypointDTO(waypoint.getName(), cp, waypoint.getPassingInstructions());
}
private RaceCourseDTO convertToRaceCourseDTO(CourseBase course) {
/**
* @param course
* the course to convert
* @param trackedRace
* if <code>null</code>, no position data will be attached to the {@link MarkDTO}s
* @param timePoint
* if <code>trackedRace</code> is not <code>null</code>, specifies the time point for which to determine
* the mark positions and attach to the {@link MarkDTO}s. If <code>null<c/code>, the current time point
* will be used as default.
*/
private RaceCourseDTO convertToRaceCourseDTO(CourseBase course, TrackedRace trackedRace, TimePoint timePoint) {
final RaceCourseDTO result;
if (course != null) {
List<WaypointDTO> waypointDTOs = new ArrayList<WaypointDTO>();
Map<Serializable, ControlPointDTO> controlPointCache = new HashMap<>();
RaceCourseDTO result = new RaceCourseDTO(waypointDTOs);
for (Waypoint waypoint : course.getWaypoints()) {
waypointDTOs.add(convertToWaypointDTO(waypoint, controlPointCache));
waypointDTOs.add(convertToWaypointDTO(waypoint, controlPointCache, trackedRace, timePoint));
}
result = new RaceCourseDTO(waypointDTOs);
} else {
result = new RaceCourseDTO(Collections.<WaypointDTO> emptyList());
}
return result;
}
@Override
public RaceCourseDTO getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName,
String fleetName) {
public RaceCourseDTO getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, String fleetName) {
RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
CourseBase lastPublishedCourse = new LastPublishedCourseDesignFinder(raceLog).analyze();
if (lastPublishedCourse == null) {
lastPublishedCourse = new CourseDataImpl("");
}
return convertToRaceCourseDTO(lastPublishedCourse);
return convertToRaceCourseDTO(lastPublishedCourse, /* trackedRace */ null, /* timePoint */ null);
}
@Override
@@ -5765,4 +5747,23 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return true;
}
}
@Override
public RegattaAndRaceIdentifier getRaceIdentifier(String regattaLikeName, String raceColumnName, String fleetName) {
RegattaAndRaceIdentifier result = null;
final Leaderboard leaderboard = getService().getLeaderboardByName(regattaLikeName);
if (leaderboard != null) {
final RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName);
if (raceColumn != null) {
final Fleet fleet = raceColumn.getFleetByName(fleetName);
if (fleet != null) {
final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null) {
result = trackedRace.getRaceIdentifier();
}
}
}
}
return result;
}
}
@@ -1,5 +1,6 @@
package com.sap.sailing.gwt.ui.shared;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -8,9 +9,8 @@ import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.Position;
public class CoursePositionsDTO implements IsSerializable {
public RaceCourseDTO course;
public List<Position> waypointPositions;
public List<Position> startMarkPositions;
public List<Position> finishMarkPositions;
public Set<MarkDTO> marks;
public int totalLegsCount;
@@ -35,4 +35,24 @@ public class CoursePositionsDTO implements IsSerializable {
public NauticalSide finishLineAdvantageousSide;
public Double finishLineAdvantageInMeters;
public Double finishLineLengthInMeters;
public List<Position> getStartMarkPositions() {
final List<Position> result = new ArrayList<>();
if (course != null && course.waypoints != null && !course.waypoints.isEmpty()) {
for (final MarkDTO mark : course.waypoints.get(0).controlPoint.getMarks()) {
result.add(mark.position);
}
}
return result;
}
public List<Position> getFinishMarkPositions() {
final List<Position> result = new ArrayList<>();
if (course != null && course.waypoints != null && !course.waypoints.isEmpty()) {
for (final MarkDTO mark : course.waypoints.get(course.waypoints.size()-1).controlPoint.getMarks()) {
result.add(mark.position);
}
}
return result;
}
}
@@ -27,4 +27,45 @@ public class GateDTO extends ControlPointDTO {
public Iterable<MarkDTO> getMarks() {
return Arrays.asList(left, right);
}
@Override
public int hashCode() {
final int prime = 31;
int result = prime * getIdAsString().hashCode() * getName().hashCode();
result = prime * result + ((left == null) ? 0 : left.hashCode());
result = prime * result + ((right == null) ? 0 : right.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (getClass() != obj.getClass())
return false;
GateDTO other = (GateDTO) obj;
if (getIdAsString() == null) {
if (other.getIdAsString() != null)
return false;
} else if (!getIdAsString().equals(other.getIdAsString()))
return false;
if (getName() == null) {
if (other.getName() != null)
return false;
} else if (!getName().equals(other.getName()))
return false;
if (left == null) {
if (other.left != null)
return false;
} else if (!left.equals(other.left))
return false;
if (right == null) {
if (other.right != null)
return false;
} else if (!right.equals(other.right))
return false;
return true;
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.shared;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -21,7 +22,21 @@ public class RaceCourseDTO implements IsSerializable {
}
public RaceCourseDTO(List<WaypointDTO> waypoints) {
this(waypoints, null);
this(waypoints, getMarksFromWaypoints(waypoints));
}
private static List<MarkDTO> getMarksFromWaypoints(List<WaypointDTO> waypoints) {
final LinkedHashSet<MarkDTO> marks = new LinkedHashSet<>();
for (final WaypointDTO waypoint : waypoints) {
for (final MarkDTO m : waypoint.controlPoint.getMarks()) {
marks.add(m);
}
}
final List<MarkDTO> result = new ArrayList<>();
for (final MarkDTO m : marks) {
result.add(m);
}
return result;
}
public RaceCourseDTO(List<WaypointDTO> waypoints, List<MarkDTO> allMarks) {
@@ -181,6 +181,7 @@ public class RaceContext {
case FINISHING:
case RUNNING:
case SCHEDULED:
case PRESCHEDULED:
case STARTPHASE:
case UNKNOWN:
break;
@@ -26,7 +26,7 @@ public class SimpleCompetitorDTO extends NamedDTO implements Serializable {
final Nationality nationality = competitor.getTeam().getNationality();
CountryCode countryCode = nationality == null ? null : nationality.getCountryCode();
this.sailID = competitor.getBoat().getSailID();
this.twoLetterIsoCountryCode = countryCode.getTwoLetterISOCode();
this.twoLetterIsoCountryCode = countryCode == null ? null : countryCode.getTwoLetterISOCode();
this.flagImageURL = competitor.getFlagImage() == null ? null : competitor.getFlagImage().toString();
}
@@ -360,7 +360,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
this.replayPathCanvasOverlays = null;
this.raceCourseCanvasOverlay = null;
this.timeListeners = new LinkedList<TimeListenerWithStoppingCriteria>();
this.initializeData(showMapControls);
this.initializeData(showMapControls, /* showHeaderPanel */ true);
this.parent = parent;
}
@@ -1000,7 +1000,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
}
@Override
public void initializeData(boolean showMapControls) {
public void initializeData(boolean showMapControls, boolean showHeaderPanel) {
loadMapsAPIV3();
}
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Could not determine the version of your GWT SDK; using the module DTD from GWT 1.6.4. You may want to change this. -->
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.7.0//EN" "http://gwtproject.org/doctype/2.7.0/gwt-module.dtd">
<module>
<inherits name='com.sap.sailing.gwt.ui.CommonConfigs'/>
<!-- Other module inherits -->
<inherits name="com.sap.sse.gwt.SSESharedGWT" />
<inherits name="com.sap.sse.SSECommon" />
<inherits name="com.sap.sailing.domain.SailingDomain" />
<inherits name="com.sap.sse.gwt.AdminConsole" />
<inherits name="com.sap.sse.security.ui.LoginPanel" />
<!-- Google Maps API -->
<inherits name='com.google.gwt.ajaxloader.AjaxLoader' />
<inherits name='com.google.gwt.maps.Maps' />
<!-- Highcharts API -->
<inherits name="com.sap.sse.gwt.Highcharts_Autoinject" />
<!-- Bootstrap API -->
<inherits name="com.github.gwtbootstrap.BootstrapNoResources" />
<inherits name='com.github.gwtbootstrap.datetimepicker.DatetimepickerNoResources' />
<!-- Specify the app entry point class. -->
<entry-point class='com.sap.sailing.gwt.ui.raceboard.EmbeddedMapAndWindChartEntryPoint'/>
<!-- Specify the paths for translatable code -->
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<inherits name='com.sap.sailing.gwt.ui.SourceCommon' />
<inherits name='com.sap.sailing.gwt.ui.SourceClient' />
<inherits name='com.sap.sailing.gwt.ui.SourceShared' />
<inherits name='com.sap.sailing.gwt.ui.SourceControl' />
<inherits name="com.sap.sailing.gwt.ui.SourceRaceboard"/>
<inherits name="com.sap.sailing.gwt.ui.SourceLeaderboard" />
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole" />
<inherits name='com.sap.sse.gwt.CommonControlCSS_Autoinject'/>
</module>
@@ -45,6 +45,7 @@ import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RequiresResize;
import com.sap.sse.common.Util;
/**
* A widget that allows the user to select a value within a range of possible values using a sliding bar that responds
@@ -704,9 +705,10 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
* the current value
*/
public void setMaxValue(Double maxValue, boolean fireEvent) {
if (!Util.equalsWithNull(maxValue, this.maxValue)) {
this.maxValue = maxValue;
drawTickLabels();
resetCurrentValue(fireEvent);
onMinMaxValueChanged(fireEvent);
}
}
/**
@@ -716,8 +718,40 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
* the current value
*/
public void setMinValue(Double minValue, boolean fireEvent) {
if (!Util.equalsWithNull(minValue, this.minValue)) {
this.minValue = minValue;
drawTickLabels();
onMinMaxValueChanged(fireEvent);
}
}
/**
* Set the minimum and maximum value
*
* @param minValue
* the current value for min
* @param maxValue
* the current valuefor max
*/
public void setMinAndMaxValue(Double minValue, Double maxValue, boolean fireEvent) {
boolean changed = false;
if (!Util.equalsWithNull(minValue, this.minValue)) {
this.minValue = minValue;
changed = true;
}
if (!Util.equalsWithNull(maxValue, this.maxValue)) {
this.maxValue = maxValue;
changed = true;
}
if (changed) {
onMinMaxValueChanged(fireEvent);
}
}
/**
* Handle value changes of min and/or max value
*/
protected void onMinMaxValueChanged(boolean fireEvent) {
redraw();
resetCurrentValue(fireEvent);
}
@@ -1037,6 +1071,7 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
int lineWidth = lineElement.getOffsetWidth();
if (numMarkers > 0) {
Element lastMarkerLabel = null;
Marker lastMarker = null;
int lastMarkerLabelOffsetLeft = 0;
// Create the labels or make them visible
for (int i = 0; i < numMarkers; i++) {
@@ -1076,9 +1111,9 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
// hide last marker label if it is too close to the one just created
if (lastMarkerLabelOffsetLeft > 0 && (labelLeftOffset-lastMarkerLabelOffsetLeft) <= 20) {
if (lastMarkerLabel != null) {
if (lastMarkerLabel != null && lastMarker != null) {
lastMarkerLabel.setPropertyString("className", "gwt-SliderBar-markerlabel-tooClose");
lastMarkerLabel.setPropertyString("innerHTML", "M"); // no space for more
lastMarkerLabel.setPropertyString("innerHTML", lastMarker.name); // no space for more
int currentMarkerOffsetLeft = labelLeftOffset;
try {
currentMarkerOffsetLeft = Integer.parseInt(markerElements.get(i).getStyle().getLeft().replace("px", ""));
@@ -1088,6 +1123,7 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue<Do
lastMarkerLabel.getStyle().setLeft(currentMarkerOffsetLeft-8, Unit.PX);
}
}
lastMarker = marker;
lastMarkerLabel = label;
lastMarkerLabelOffsetLeft = labelLeftOffset;
}
@@ -3,12 +3,12 @@ package com.sap.sse.gwt.client.controls.slider;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.DOM;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.user.client.DOM;
import com.sap.sse.gwt.client.controls.slider.TimeTicksCalculator.NormalizedInterval;
import com.sap.sse.gwt.client.controls.slider.TimeTicksCalculator.TickPosition;
@@ -182,7 +182,7 @@ public class TimeSlider extends SliderBar {
return;
Element knobElement = knobImage.getElement();
if(curValue >= minValue && curValue <= maxValue) {
if (curValue != null && minValue != null && maxValue != null && curValue >= minValue && curValue <= maxValue) {
// Move the knob to the correct position
int lineWidth = lineElement.getOffsetWidth();
int knobWidth = knobElement.getOffsetWidth();
@@ -204,7 +204,6 @@ public class TimeSlider extends SliderBar {
} else {
this.maxValue = maxValue;
}
calculateTicks();
}
public void setMinValue(Double minValue, boolean fireEvent) {
@@ -213,7 +212,22 @@ public class TimeSlider extends SliderBar {
} else {
this.minValue = minValue;
}
}
@Override
public void setMinAndMaxValue(Double minValue, Double maxValue, boolean fireEvent) {
if (!isZoomed) {
super.setMinAndMaxValue(minValue, maxValue, fireEvent);
} else {
this.minValue = minValue;
this.maxValue = maxValue;
}
}
@Override
protected void onMinMaxValueChanged(boolean fireEvent) {
calculateTicks();
super.onMinMaxValueChanged(fireEvent);
}
public void setStepSize(double stepSize, boolean fireEvent) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/dark_sap_gray_black_30" android:state_selected="true"/>
<item android:drawable="@color/dark_black"/>
</selector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/light_sap_gray_black_30" android:state_selected="true"/>
<item android:drawable="@color/light_black"/>
</selector>
@@ -4,5 +4,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:datePickerMode="spinner"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
@@ -4,6 +4,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="1.55"
android:scaleY="1.55"
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"
android:timePickerMode="spinner"/>
@@ -4,5 +4,5 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:calendarViewShown="false"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?sap_gray_black_30"
android:orientation="vertical"
android:padding="@dimen/default_padding_half">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textLarge"
tools:text="Singapore"/>
<TextView
android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?textNormal"
android:visibility="gone"
tools:text="Day 2"
tools:visibility="visible"/>
</LinearLayout>
@@ -20,6 +20,6 @@
android:layout_gravity="center"
android:paddingLeft="@dimen/default_padding"
android:paddingRight="@dimen/default_padding"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</LinearLayout>
@@ -25,7 +25,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="?chevron_left_24dp"
android:visibility="gone"/>
android:visibility="gone"
tools:visibility="visible"/>
<ImageView
android:id="@+id/header_flag"
@@ -112,8 +113,8 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="11"
android:scaleX="1.55"
android:scaleY="1.55">
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale">
<include
android:id="@+id/time_picker"
@@ -71,15 +71,32 @@
android:src="?marker_race"/>
<LinearLayout
android:id="@+id/race_flag"
android:id="@+id/additional_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_centerVertical="true">
<ImageView
android:id="@+id/has_dependent_races"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:src="?link_24dp"
android:visibility="gone"
tools:visibility="visible"/>
<LinearLayout
android:id="@+id/race_flag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical">
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:id="@+id/current_flag"
@@ -89,29 +106,18 @@
android:scaleType="centerInside"
tools:src="@drawable/alpha_flag"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/arrow_direction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:src="?arrow_down"/>
<TextView
android:id="@+id/flag_timer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:drawableLeft="?arrow_down"
android:textAppearance="?textXSmall"
tools:text="01:12"/>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/race_name"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
@@ -119,12 +125,28 @@
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_toLeftOf="@id/race_flag"
android:layout_toStartOf="@id/race_flag"
android:orientation="vertical"
android:layout_toLeftOf="@id/additional_info"
android:layout_toStartOf="@id/additional_info"
android:orientation="vertical">
<TextView
android:id="@+id/race_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textNormal"
tools:text="Race 3"/>
<TextView
android:id="@+id/depends_on"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textXSmall"
android:visibility="gone"
tools:text="5 minutes after ABC"
tools:visibility="visible"/>
</LinearLayout>
</RelativeLayout>
<ImageView
@@ -36,24 +36,23 @@
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/nav_button"
android:layout_toStartOf="@id/nav_button"
tools:text="Marina Bay / Viewer"
android:textSize="@dimen/textSize_32"
android:paddingLeft="@dimen/default_padding_half"
android:paddingRight="@dimen/default_padding_half"
android:textColor="@color/constant_white"
/>
android:textSize="@dimen/textSize_32"
tools:text="Marina Bay / Viewer"/>
<TextView
android:id="@+id/regatta_course"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/regatta_data"
android:layout_toLeftOf="@id/nav_button"
android:layout_toStartOf="@id/nav_button"
android:layout_above="@id/regatta_data"
android:paddingLeft="@dimen/default_padding_half"
android:paddingRight="@dimen/default_padding_half"
android:singleLine="true"
android:textColor="@color/constant_white"
tools:text="Extreme Sailing Series 2015 Act 1 Singapore"
android:textSize="@dimen/textSize_32" />
android:textSize="@dimen/textSize_32"
tools:text="Extreme Sailing Series 2015 Act 1 Singapore"/>
</RelativeLayout>
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/races_current"
android:textColor="?sap_light_gray"
android:textColor="?sap_yellow_1"
android:textSize="@dimen/textSize_20"
android:textStyle="bold"/>
@@ -49,10 +49,10 @@
android:dividerHeight="1dp"/>
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="@+id/progress"
android:visibility="gone"/>
</RelativeLayout>
</RelativeLayout>
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<RelativeLayout
android:id="@+id/race_content_header"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?sap_gray">
@@ -43,19 +44,36 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textAppearance="?textSmall"
tools:text="@string/time_start"/>
<ImageView
android:id="@+id/time_start_lock"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/default_margin_half"
android:layout_toLeftOf="@+id/time_start"
android:layout_toStartOf="@+id/time_start"
android:orientation="horizontal">
<ImageView
android:id="@+id/time_start_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="?lock_outline"
android:visibility="gone" />
android:visibility="gone"
tools:visibility="visible"/>
<ImageView
android:id="@+id/linked_race"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="?link_24dp"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
@@ -76,7 +94,8 @@
android:gravity="center"
android:textAppearance="?textSmall"
android:visibility="invisible"
tools:text="@string/time" />
tools:text="@string/time"
tools:visibility="visible"/>
</RelativeLayout>
<RelativeLayout
@@ -93,7 +112,8 @@
android:layout_alignParentStart="true"
android:text="@string/time_finish"
android:textAppearance="?textSmall"
android:visibility="invisible" />
android:visibility="invisible"
tools:visibility="visible"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
@@ -82,8 +82,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="75dp"
android:scaleX="1.55"
android:scaleY="1.55" />
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale" />
</LinearLayout>
@@ -4,5 +4,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
@@ -80,8 +80,8 @@
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/default_margin_twice"
android:layout_marginTop="@dimen/default_margin_twice"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</LinearLayout>
</LinearLayout>
@@ -123,8 +123,8 @@
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/default_margin_twice"
android:layout_marginTop="@dimen/default_margin_twice"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</LinearLayout>
<TextView
@@ -34,12 +34,67 @@
android:text="@string/set_time"
android:textAppearance="?textNormal"/>
</LinearLayout>
<View
android:id="@+id/line_top"
android:layout_width="match_parent"
android:layout_height="@dimen/thick_line"
android:layout_alignParentBottom="true"
android:background="?black"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/tab_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?black"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<Button
android:id="@+id/absolute"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dependent_races_absolute"
android:textAllCaps="true"
android:textColor="?sap_yellow_1"
android:textSize="@dimen/textSize_26"/>
<Button
android:id="@+id/relative"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/dependent_races_relative"
android:textAllCaps="true"
android:textColor="?sap_light_gray"
android:textSize="@dimen/textSize_26"/>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/tab_button"
android:layout_below="@id/header">
<RelativeLayout
android:id="@+id/time_absolute"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?sap_gray_black_30"
android:visibility="gone"
tools:visibility="visible">
<RelativeLayout
android:id="@+id/buttonBar"
android:layout_width="match_parent"
android:layout_height="@dimen/selector_header_height">
android:layout_height="@dimen/selector_header_height"
android:layout_alignParentTop="true">
<LinearLayout
android:layout_width="wrap_content"
@@ -85,16 +140,103 @@
</LinearLayout>
</RelativeLayout>
<View
android:id="@+id/line_top"
android:layout_width="match_parent"
android:layout_height="@dimen/thick_line"
android:layout_below="@id/buttonBar"
android:background="?black"/>
<LinearLayout
android:id="@+id/start_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale">
<NumberPicker
android:id="@+id/start_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:descendantFocusability="blocksDescendants"/>
<include
layout="@layout/race_time_picker"
android:id="@+id/start_time_picker"
layout="@layout/time_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<Button
android:id="@+id/set_start_time_absolute"
style="?attr/buttonSolidSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_margin="@dimen/default_margin_twice"
android:maxWidth="200dp"
android:text="@string/race_set_time"/>
<TextView
android:id="@+id/debug_time"
style="?textSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:visibility="gone"
tools:text="12:12:12"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/time_relative"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/buttonBar"/>
android:background="?sap_gray_black_30"
android:visibility="gone"
tools:visibility="visible">
<LinearLayout
android:id="@+id/relative_input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<NumberPicker
android:id="@+id/time_offset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="@dimen/default_padding"
android:text="@string/minutes_after"
android:textAppearance="?textLarge"/>
<Spinner
android:id="@+id/dependent_race"
android:layout_width="470dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:dropDownWidth="wrap_content"
android:prompt="@string/dependent_races_prompt"
android:spinnerMode="dropdown"/>
</LinearLayout>
<Button
android:id="@+id/set_start_time_relative"
style="?attr/buttonSolidSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/relative_input"
android:layout_centerInParent="true"
android:maxWidth="200dp"
android:text="@string/race_set_time"/>
</RelativeLayout>
</FrameLayout>
</RelativeLayout>
@@ -10,7 +10,7 @@
android:orientation="vertical">
<RelativeLayout
android:id="@+id/header"
android:id="@+id/header_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/selector_header_height"
android:background="?sap_gray">
@@ -54,8 +54,9 @@
android:src="?wind_on"
android:visibility="gone"/>
<LinearLayout
<TextView
android:id="@+id/header_text"
android:drawableLeft="?chevron_left_24dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
@@ -65,28 +66,18 @@
android:layout_marginStart="@dimen/default_margin"
android:layout_toLeftOf="@id/wind_off"
android:layout_toStartOf="@id/wind_off"
android:orientation="horizontal">
<ImageView
android:id="@+id/header_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="?chevron_left_24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal"
android:text="@string/wind"
android:textAppearance="?textNormal" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/content_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@id/header"
android:layout_below="@id/header_layout"
android:background="?sap_gray_black_30"
android:orientation="vertical"
android:paddingLeft="@dimen/default_padding"
@@ -140,10 +131,11 @@
android:visibility="visible"/>
<com.sap.sailing.racecommittee.app.ui.views.CompassView
android:id="@+id/compassView"
android:id="@+id/compass_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>
<RelativeLayout
@@ -169,15 +161,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleX="1.55"
android:scaleY="1.55"/>
android:scaleX="@integer/picker_scale"
android:scaleY="@integer/picker_scale"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/layout_map"
android:id="@+id/layout_position"
android:layout_width="wrap_content"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/default_margin"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:orientation="vertical">
<TextView
android:id="@+id/position_header"
@@ -189,77 +186,119 @@
android:textAppearance="?textSmall"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/position_header">
<FrameLayout
android:id="@+id/windMap"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/welcome"/>
android:layout_height="wrap_content"
android:layout_below="@+id/position_header">
<RelativeLayout
android:id="@+id/address"
<TextView
android:id="@+id/position_latitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:background="?white"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
android:text="@string/wind_latitude"
android:textAppearance="?textSmall"/>
<ImageButton
android:id="@+id/address_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@android:color/transparent"
android:padding="@dimen/default_padding_half"
android:src="?search_24dp_invert"/>
<EditText
android:id="@+id/address_input"
<TextView
android:id="@+id/position_longitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/default_margin_half"
android:layout_marginStart="@dimen/default_margin_half"
android:layout_toLeftOf="@id/address_search"
android:layout_toStartOf="@id/address_search"
android:hint="@string/wind_address_hint"
android:padding="@dimen/default_padding_half"
android:singleLine="true"
android:textAppearance="?textNormal"
android:textColor="?white_invert"
android:textColorHint="?sap_light_gray"/>
</RelativeLayout>
android:layout_margin="@dimen/default_margin"
android:layout_below="@+id/position_latitude"
android:text="@string/wind_longitude"
android:textAppearance="?textSmall"/>
<RelativeLayout
android:id="@+id/dark_layer"
<TextView
android:id="@+id/position_accuracy"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/constant_black_transparent"/>
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin"
android:layout_below="@+id/position_longitude"
android:text="@string/wind_accuracy"
android:textAppearance="?textSmall"/>
<Button
android:id="@+id/enter_position"
android:id="@+id/position_show"
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/button_border_light"
android:padding="@dimen/default_padding"
android:text="@string/wind_enter_position"
android:textAllCaps="true"
android:textAppearance="?textNormal"
android:textColor="?white"/>
</RelativeLayout>
android:layout_below="@+id/position_accuracy"
android:layout_centerHorizontal="true"
android:text="@string/wind_map_button_show"/>
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/map_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/content_layout"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<!-- the following layout is for a future implementation -->
<!--<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:visibility="gone">
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Overall"/>
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#1"/>
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#2"/>
<Button
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#3"/>
</LinearLayout>-->
</LinearLayout>
<Button
android:id="@+id/position_hide"
style="?attr/buttonSolidNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="@dimen/wind_hide_button_right_margin"
android:layout_marginEnd="@dimen/wind_hide_button_right_margin"
android:layout_marginTop="@dimen/wind_hide_button_top_margin"
android:text="@string/wind_map_button_hide"/>
</RelativeLayout>
</RelativeLayout>
@@ -300,7 +300,7 @@
<string name="time_down">Runter in #TIME#</string>
<string name="time_up">Hoch in #TIME#</string>
<string name="time_finish">Ende N/A</string>
<string name="time_start">Start #TIME#</string>
<string name="time_start">Start %s</string>
<string name="time">#TIME#</string>
<string name="set_signal">Signal setzen</string>
<string name="flag_ap_desc">Rennen verschieben</string>
@@ -323,13 +323,13 @@
<string name="race_reset_cancel">Nein danke</string>
<string name="race_reset_message">Möchten Sie wirklich das Rennen inklusive aller Einstellungen und getätigten Aktionen zurücksetzen? Das kann nicht rückgängig gemacht werden!</string>
<string name="race_set_time">Zeit setzen</string>
<string name="race_start_time_in">-#TIME#</string>
<string name="race_start_time_ago">#TIME#</string>
<string name="race_start_time_in">-%s</string>
<string name="race_start_time_ago">%s</string>
<string name="tomorrow">Morgen</string>
<string name="race_begin_finish_header">Beginn des Zieleinlaufs</string>
<string name="race_begin_finish_current">Aktuelle Zeit verwenden</string>
<string name="race_begin_finish_custom">Angegebene Zeit verwenden</string>
<string name="wind_sensor"><![CDATA[um #AT# / von #FROM#° / #SPEED# kn]]></string>
<string name="wind_sensor"><![CDATA[um %1$s / von %2$.0f° / %3$.1f kn]]></string>
<string name="wind_no_data">Keine Daten</string>
<string name="wind_panel"><![CDATA[von %1$.0f°\n%2$.1f kn]]></string>
<string name="settings_category_developer">Entwickler Optionen</string>
@@ -341,7 +341,7 @@
<string name="gate_start_set_pathfinder">Pathfinder setzen</string>
<string name="gate_start_set">Gate Time setzen</string>
<string name="race_setup_confirm">Setup bestätigen</string>
<string name="race_end_finish_header">Ende des Zieleinlaufs</string>
<string name="race_end_finish_header">Ende des Zieleinlaufs (Beginn Zieleinlauf: %s)</string>
<string name="race_summary_finish_begin">Beginn des Zieleinlaufs</string>
<string name="race_summary_finish_duration">Dauer des Zieleinlaufs</string>
<string name="race_summary_finish_end">Ende des Zieleinlaufs</string>
@@ -372,7 +372,7 @@
<string name="delete_file_title">Datei löschen</string>
<string name="degrees">Grad</string>
<string name="start_time">Startzeit</string>
<string name="start_time_value">Um #TIME# in #COUNTDOWN#</string>
<string name="start_time_value">Um %1$s in %2$s</string>
<!--Extracted locales -->
<string name="selected_course_area_lost">Die ausgewählte Kursumgebung ist nicht mehr verfügbar.</string>
@@ -419,13 +419,27 @@
<string name="change_cancel">Abbrechen</string>
<string name="change_message">Nach den Wettfahrtregeln sind diese Daten im aktuellen Rennenstatus nicht veränderbar.\nSie könnten jedoch die falschen Eingaben korrigieren.</string>
<string name="change_title">Warnung</string>
<string name="send_mail">Sende Email...</string>
<string name="send_mail">Sende Email…</string>
<string name="data_reload_title">Daten wurden extern verändert</string>
<string name="data_reload_message">Daten müssen neu geladen werden. Eventueller Datenverlust ist möglich.</string>
<string name="preference_allow_dependent_races_header">Relative Startzeit</string>
<string name="preference_allow_dependent_races_description">Aktiviert relative Startzeiten zwischen Rennen</string>
<string name="preference_dependent_races_offset_header">Standard Offset der relativen Startzeit (Minuten)</string>
<string name="error_finished_time">Die ausgewählte Zielzeit liegt vor der Zeit, der ersten Schiffeinfahrt. Bitte überprüfe die Uhrzeit.</string>
<string name="error_finishing_time">Die ausgewählte Zeit is vor dem Rennstart. Bitte überprüfe die Uhrzeit.</string>
<string name="error_time_in_future">Die ausgewählte Zeit liegt in der Zukunft. Bitte überprüfe die Uhrzeit.</string>
<string name="error_wrong_race_state">Der RaceState hat nicht den korrekten Status (benötigt: %1$s - aktuell: %2$s)</string>
<string name="wind_map_button_show">Windkarte</string>
<string name="wind_map_button_hide">Schließen</string>
<string name="wind_latitude">Breite</string>
<string name="wind_longitude">Länge</string>
<string name="wind_accuracy">Genauigkeit</string>
<string name="dependent_races_absolute">Absolute Startzeit</string>
<string name="dependent_races_relative">Relative Startzeit</string>
<string name="dependent_races_prompt">Bitte Rennen auswählen</string>
<string name="minutes_after">Minuten nach</string>
<string name="minutes_after_long">%1$.0f Minuten nach %2$s</string>
<string name="wind_accuracy_ago">alt</string>
</resources>
@@ -19,6 +19,7 @@
<attr name="buttonSolidXSmall" format="reference"/>
<attr name="buttonSolidSmall" format="reference"/>
<attr name="buttonSolidNormal" format="reference"/>
<attr name="text" format="reference"/>
<attr name="textXXSmall" format="reference"/>
<attr name="textXSmall" format="reference"/>
<attr name="textSmall" format="reference"/>
@@ -53,6 +54,7 @@
<attr name="course_updown_64dp" format="reference"/>
<attr name="dns_48dp" format="reference"/>
<attr name="hearing_48dp" format="reference"/>
<attr name="link_24dp" format="reference"/>
<attr name="lock_outline" format="reference"/>
<attr name="marker" format="reference"/>
<attr name="marker_race" format="reference"/>
@@ -63,6 +65,7 @@
<attr name="swipe_idle" format="reference"/>
<attr name="swipe_left" format="reference"/>
<attr name="swipe_right" format="reference"/>
<attr name="tab_widget" format="reference"/>
<attr name="value_next" format="reference"/>
<attr name="value_prev" format="reference"/>
<attr name="wind_on" format="reference"/>
@@ -49,4 +49,9 @@
<dimen name="bigger_over_flow_width">64dp</dimen>
<dimen name="bigger_over_flow_height">32dp</dimen>
<dimen name="wind_hide_button_right_margin">76dp</dimen>
<dimen name="wind_hide_button_top_margin">20dp</dimen>
<item name="picker_scale" format="float" type="integer">1.55</item>
</resources>
@@ -81,6 +81,11 @@
<string name="app_folder" translatable="false">racecommittee</string>
<bool name="preference_isSendingActive_default">false</bool>
<string name="preference_allow_dependent_races_key" translatable="false">dependentRaces</string>
<bool name="preference_allow_dependent_races_default">true</bool>
<string name="preference_dependent_races_offset_key" translatable="false">dependentRacesOffset</string>
<integer name="preference_dependent_races_offset_default">5</integer>
<string name="preference_allow_demo_key" translatable="false">allow_demo</string>
<bool name="preference_allow_demo_default">false</bool>
@@ -246,8 +246,8 @@
<string name="race_start_in_4_minutes">4 Minutes</string>
<string name="race_start_in_2_minutes">2 Minutes</string>
<string name="race_start_in_1_minute">1 Minute</string>
<string name="race_start_time_in">-#TIME#</string>
<string name="race_start_time_ago">#TIME#</string>
<string name="race_start_time_in">-%s</string>
<string name="race_start_time_ago">%s</string>
<string name="race_set_time">Set Time</string>
<string name="race_reset_course">Reset course</string>
<string name="no_course_active">No course active</string>
@@ -322,13 +322,13 @@
<string name="wind_enter_position">Set Your Position</string>
<string name="wind_kn">kn</string>
<string name="wind_address_hint">Enter Address</string>
<string name="wind_sensor"><![CDATA[at #AT# / from #FROM#° / #SPEED# kn]]></string>
<string name="wind_sensor"><![CDATA[at %1$s / from %2$.0f° / %3$.1f kn]]></string>
<string name="wind_panel"><![CDATA[from %1$.0f°\n%2$.1f kn]]></string>
<string name="wind_no_data">No Data</string>
<string name="time_up">Up in #TIME#</string>
<string name="time_down">Down in #TIME#</string>
<string name="time_start">Start #TIME#</string>
<string name="time_start">Start %s</string>
<string name="time_finish">Finish N/A</string>
<string name="time">#TIME#</string>
<string name="set_signal">Set Signal</string>
@@ -352,7 +352,7 @@
<string name="race_reset_cancel">No Thanks</string>
<string name="race_reset_message">Do you really want to reset this race including all settings and actions that have be made? This can not be undone.</string>
<string name="race_begin_finish_header">Begin Finish</string>
<string name="race_end_finish_header">End Finish</string>
<string name="race_end_finish_header">End Finish (Begin Finish: %s)</string>
<string name="race_begin_finish_current">Use Current Time</string>
<string name="race_begin_finish_custom">Use Custom Time</string>
<string name="race_setup_confirm">Confirm Setup</string>
@@ -393,7 +393,7 @@
<string name="delete_file_title">Delete file</string>
<string name="delete_file">This will delete the picture</string>
<string name="start_time">Start Time</string>
<string name="start_time_value">At #TIME# in #COUNTDOWN#</string>
<string name="start_time_value">At %1$s in %2$s</string>
<!--Extracted locales -->
<string name="selected_course_area_lost">The selected course area was lost.</string>
@@ -440,12 +440,26 @@
<string name="change_cancel">Cancel</string>
<string name="change_message">According to the racing rules this data is not changeable in the current\nrace state. You may proceed nevertheless to correct wrong inputs.</string>
<string name="change_title">Warning</string>
<string name="send_mail">Send mail...</string>
<string name="send_mail">Send mail…</string>
<string name="data_reload_title">Data has changed externally</string>
<string name="data_reload_message">Data have to be reloaded. Possible data loss is possible.</string>
<string name="preference_allow_dependent_races_header">Relative Start Time (Minutes)</string>
<string name="preference_allow_dependent_races_description">Enable Relative Start Time between Races</string>
<string name="preference_dependent_races_offset_header">Default Relative Start Time Offset</string>
<string name="error_finished_time">The given finish time is earlier than the first finisher time. Please recheck the time.</string>
<string name="error_finishing_time">The selected time is before the race start.</string>
<string name="error_time_in_future">The given time is in future. Please recheck the time.</string>
<string name="error_wrong_race_state">The RaceState isn\'t in the correct state (needed: %1$s - current: %2$s)</string>
<string name="wind_map_button_show">Wind Map</string>
<string name="wind_map_button_hide">Hide</string>
<string name="wind_latitude">Latitude</string>
<string name="wind_longitude">Longitude</string>
<string name="wind_accuracy">Accuracy</string>
<string name="dependent_races_absolute">Absolute Start Time</string>
<string name="dependent_races_relative">Relative Start Time</string>
<string name="dependent_races_prompt">Please select the race</string>
<string name="minutes_after">Minutes after</string>
<string name="minutes_after_long">%1$.0f Minutes after %2$s</string>
<string name="wind_accuracy_ago">ago</string>
</resources>
@@ -7,6 +7,7 @@
<style name="AppTheme.Dark.Base" parent="Theme.AppCompat.NoActionBar">
<item name="windowActionModeOverlay">true</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
<item name="android:tabWidgetStyle">@style/AppTheme.Dark.TabWidget</item>
<item name="colorPrimary">@color/dark_colorPrimary</item>
<item name="colorPrimaryDark">@color/dark_colorPrimaryDark</item>
@@ -34,6 +35,7 @@
<item name="buttonSolidXSmall">@style/AppTheme.Dark.Button.XSmall</item>
<item name="buttonSolidSmall">@style/AppTheme.Dark.Button.Small</item>
<item name="buttonSolidNormal">@style/AppTheme.Dark.Button.Normal</item>
<item name="text">@style/AppTheme.Dark.Text</item>
<item name="textXXSmall">@style/AppTheme.Dark.Text.XXSmall</item>
<item name="textXSmall">@style/AppTheme.Dark.Text.XSmall</item>
<item name="textSmall">@style/AppTheme.Dark.Text.Small</item>
@@ -67,6 +69,7 @@
<item name="course_updown_64dp">@drawable/course_updown_64dp_dark</item>
<item name="dns_48dp">@drawable/ic_dns_white_48dp</item>
<item name="hearing_48dp">@drawable/ic_hearing_white_48dp</item>
<item name="link_24dp">@drawable/ic_link_white_24dp</item>
<item name="lock_outline">@drawable/lock_outline_dark</item>
<item name="marker">@drawable/marker_dark</item>
<item name="marker_race">@drawable/marker_race_dark</item>
@@ -77,6 +80,7 @@
<item name="swipe_idle">@drawable/swipe_idle_dark</item>
<item name="swipe_left">@drawable/swipe_left_dark</item>
<item name="swipe_right">@drawable/swipe_right_dark</item>
<item name="tab_widget">@drawable/tabwidget_dark</item>
<item name="value_next">@drawable/value_next_dark</item>
<item name="value_prev">@drawable/value_prev_dark</item>
<item name="wind_off">@drawable/wind_off_dark</item>
@@ -171,4 +175,13 @@
<item name="android:gravity">center_horizontal</item>
</style>
<style name="AppTheme.Dark.TabWidget" parent="@android:style/Widget.TabWidget">
<item name="android:textAppearance">@style/AppTheme.Light.TabWidgetText</item>
</style>
<style name="AppTheme.Dark.TabWidgetText" parent="@android:style/TextAppearance.Widget.TabWidget">
<item name="android:textSize">@dimen/textSize_32</item>
<item name="android:textColor">?white</item>
</style>
</resources>
@@ -7,6 +7,7 @@
<style name="AppTheme.Light.Base" parent="Theme.AppCompat.NoActionBar">
<item name="windowActionModeOverlay">true</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
<item name="android:tabWidgetStyle">@style/AppTheme.Light.TabWidget</item>
<item name="colorPrimary">@color/light_colorPrimary</item>
<item name="colorPrimaryDark">@color/light_colorPrimaryDark</item>
@@ -37,6 +38,7 @@
<item name="buttonSolidXSmall">@style/AppTheme.Light.Button.XSmall</item>
<item name="buttonSolidSmall">@style/AppTheme.Light.Button.Small</item>
<item name="buttonSolidNormal">@style/AppTheme.Light.Button.Normal</item>
<item name="text">@style/AppTheme.Light.Text</item>
<item name="textXXSmall">@style/AppTheme.Light.Text.XXSmall</item>
<item name="textXSmall">@style/AppTheme.Light.Text.XSmall</item>
<item name="textSmall">@style/AppTheme.Light.Text.Small</item>
@@ -70,6 +72,7 @@
<item name="course_updown_64dp">@drawable/course_updown_64dp_light</item>
<item name="dns_48dp">@drawable/ic_dns_black_48dp</item>
<item name="hearing_48dp">@drawable/ic_hearing_black_48dp</item>
<item name="link_24dp">@drawable/ic_link_black_24dp</item>
<item name="lock_outline">@drawable/lock_outline_light</item>
<item name="marker">@drawable/marker_light</item>
<item name="marker_race">@drawable/marker_race_light</item>
@@ -80,6 +83,7 @@
<item name="swipe_idle">@drawable/swipe_idle_light</item>
<item name="swipe_left">@drawable/swipe_left_light</item>
<item name="swipe_right">@drawable/swipe_right_light</item>
<item name="tab_widget">@drawable/tabwidget_light</item>
<item name="value_next">@drawable/value_next_light</item>
<item name="value_prev">@drawable/value_prev_light</item>
<item name="wind_off">@drawable/wind_off_light</item>
@@ -181,4 +185,13 @@
<item name="android:windowBackground">@null</item>
</style>
<style name="AppTheme.Light.TabWidget" parent="@android:style/Widget.TabWidget">
<item name="android:textAppearance">@style/AppTheme.Light.TabWidgetText</item>
</style>
<style name="AppTheme.Light.TabWidgetText" parent="@android:style/TextAppearance.Widget.TabWidget">
<item name="android:textSize">@dimen/textSize_32</item>
<item name="android:textColor">?white</item>
</style>
</resources>
@@ -10,7 +10,7 @@
android:defaultValue="@string/empty"
android:key="@string/preference_identifier_key"
android:title="@string/device_identifier"/>
<EditTextPreference
<com.sap.sailing.racecommittee.app.ui.views.UrlEditTextPreference
android:defaultValue="@string/preference_server_url_default"
android:key="@string/preference_server_url_key"
android:title="@string/settings_advanced_service_header"/>
Loaded 100 of 137 files, more files were not shown because too many files have changed in this diff. Show more