Java tutorial
/******************************************************************************* * Copyright (C) 2005, 2016 Wolfgang Schramm and Contributors * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA *******************************************************************************/ package net.tourbook.ui.views.tourSegmenter; import gnu.trove.list.array.TIntArrayList; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import net.tourbook.Messages; import net.tourbook.algorithm.DPPoint; import net.tourbook.algorithm.DouglasPeuckerSimplifier; import net.tourbook.application.TourbookPlugin; import net.tourbook.chart.ColorCache; import net.tourbook.chart.SelectionChartXSliderPosition; import net.tourbook.common.UI; import net.tourbook.common.util.ColumnDefinition; import net.tourbook.common.util.ColumnManager; import net.tourbook.common.util.ITourViewer; import net.tourbook.common.util.PostSelectionProvider; import net.tourbook.common.util.Util; import net.tourbook.data.AltitudeUpDownSegment; import net.tourbook.data.TourData; import net.tourbook.data.TourMarker; import net.tourbook.data.TourSegment; import net.tourbook.preferences.ITourbookPreferences; import net.tourbook.preferences.PrefPageComputedValues; import net.tourbook.tour.BreakTimeMethod; import net.tourbook.tour.BreakTimeResult; import net.tourbook.tour.BreakTimeTool; import net.tourbook.tour.ITourEventListener; import net.tourbook.tour.SelectionDeletedTours; import net.tourbook.tour.SelectionTourData; import net.tourbook.tour.SelectionTourId; import net.tourbook.tour.SelectionTourIds; import net.tourbook.tour.TourEvent; import net.tourbook.tour.TourEventId; import net.tourbook.tour.TourManager; import net.tourbook.ui.ImageComboLabel; import net.tourbook.ui.TableColumnFactory; import net.tourbook.ui.action.ActionModifyColumns; import net.tourbook.ui.tourChart.TourChart; import net.tourbook.web.WEB; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseWheelListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Spinner; import org.eclipse.swt.widgets.Table; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.part.PageBook; import org.eclipse.ui.part.ViewPart; /** * */ public class TourSegmenterView extends ViewPart implements ITourViewer { public static final String ID = "net.tourbook.views.TourSegmenter"; //$NON-NLS-1$ private static final String DISTANCE_MILES_1_8 = "1/8"; //$NON-NLS-1$ private static final String DISTANCE_MILES_1_4 = "1/4"; //$NON-NLS-1$ private static final String DISTANCE_MILES_3_8 = "3/8"; //$NON-NLS-1$ private static final String DISTANCE_MILES_1_2 = "1/2"; //$NON-NLS-1$ private static final String DISTANCE_MILES_5_8 = "5/8"; //$NON-NLS-1$ private static final String DISTANCE_MILES_3_4 = "3/4"; //$NON-NLS-1$ private static final String DISTANCE_MILES_7_8 = "7/8"; //$NON-NLS-1$ private static final String FORMAT_ALTITUDE_DIFF = "%d / %d %s"; //$NON-NLS-1$ private static final int SEGMENTER_REQUIRES_ALTITUDE = 0x01; private static final int SEGMENTER_REQUIRES_DISTANCE = 0x02; private static final int SEGMENTER_REQUIRES_PULSE = 0x04; private static final int SEGMENTER_REQUIRES_MARKER = 0x08; private static final int SEGMENTER_REQUIRES_POWER = 0x10; private static final int MAX_DISTANCE_SPINNER_MILE = 80; private static final int MAX_DISTANCE_SPINNER_METRIC = 100; private static final String STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS = "STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS"; //$NON-NLS-1$ private static final int STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS_DEFAULT = 100; private static final String STATE_DP_TOLERANCE_POWER = "STATE_DP_TOLERANCE_POWER"; //$NON-NLS-1$ private static final String STATE_DP_TOLERANCE_PULSE = "STATE_DP_TOLERANCE_PULSE"; //$NON-NLS-1$ private static final String STATE_MINIMUM_ALTITUDE = "STATE_MINIMUM_ALTITUDE"; //$NON-NLS-1$ private static final String STATE_SELECTED_DISTANCE = "selectedDistance"; //$NON-NLS-1$ private static final String STATE_SELECTED_SEGMENTER_BY_USER = "STATE_SELECTED_SEGMENTER_BY_USER"; //$NON-NLS-1$ /** * Initially this was an int value, with 2 it's a string. */ private static final String STATE_SELECTED_BREAK_METHOD2 = "selectedBreakMethod2"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_MIN_AVG_SPEED_AS = "selectedBreakTimeMinAvgSpeedAS"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_MIN_SLICE_SPEED_AS = "selectedBreakTimeMinSliceSpeedAS"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_MIN_SLICE_TIME_AS = "selectedBreakTimeMinSliceTimeAS"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_MIN_AVG_SPEED = "selectedBreakTimeMinAvgSpeed"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_MIN_SLICE_SPEED = "selectedBreakTimeMinSliceSpeed"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_MIN_DISTANCE_VALUE = "selectedBreakTimeMinDistance"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_MIN_TIME_VALUE = "selectedBreakTimeMinTime"; //$NON-NLS-1$ private static final String STATE_BREAK_TIME_SLICE_DIFF = "selectedBreakTimeSliceDiff"; //$NON-NLS-1$ /* * Tour segmenter */ public static final String STATE_IS_SEGMENTER_ACTIVE = "STATE_IS_SEGMENTER_ACTIVE"; //$NON-NLS-1$ public static final String STATE_IS_HIDE_SMALL_VALUES = "STATE_IS_HIDE_SMALL_VALUES"; //$NON-NLS-1$ public static final boolean STATE_IS_HIDE_SMALL_VALUES_DEFAULT = true; public static final String STATE_IS_SHOW_SEGMENTER_DECIMAL_PLACES = "STATE_IS_SHOW_SEGMENTER_DECIMAL_PLACES"; //$NON-NLS-1$ public static final boolean STATE_IS_SHOW_SEGMENTER_DECIMAL_PLACES_DEFAULT = false; public static final String STATE_IS_SHOW_SEGMENTER_LINE = "STATE_IS_SHOW_SEGMENTER_LINE"; //$NON-NLS-1$ public static final boolean STATE_IS_SHOW_SEGMENTER_LINE_DEFAULT = true; public static final String STATE_IS_SHOW_SEGMENTER_MARKER = "STATE_IS_SHOW_SEGMENTER_MARKER"; //$NON-NLS-1$ public static final boolean STATE_IS_SHOW_SEGMENTER_MARKER_DEFAULT = false; public static final String STATE_IS_SHOW_SEGMENTER_TOOLTIP = "STATE_IS_SHOW_SEGMENTER_TOOLTIP"; //$NON-NLS-1$ public static final boolean STATE_IS_SHOW_SEGMENTER_TOOLTIP_DEFAULT = true; public static final String STATE_IS_SHOW_SEGMENTER_VALUE = "STATE_IS_SHOW_SEGMENTER_VALUE"; //$NON-NLS-1$ public static final boolean STATE_IS_SHOW_SEGMENTER_VALUE_DEFAULT = true; public static final String STATE_IS_SHOW_TOUR_SEGMENTS = "STATE_IS_SHOW_TOUR_SEGMENTS"; //$NON-NLS-1$ public static final boolean STATE_IS_SHOW_TOUR_SEGMENTS_DEFAULT = true; public static final String STATE_GRAPH_OPACITY = "STATE_GRAPH_OPACITY"; //$NON-NLS-1$ public static final int STATE_GRAPH_OPACITY_DEFAULT = 10; public static final String STATE_LINE_OPACITY = "STATE_LINE_OPACITY"; //$NON-NLS-1$ public static final int STATE_LINE_OPACITY_DEFAULT = 100; public static final String STATE_SMALL_VALUE_SIZE = "STATE_SMALL_VALUE_SIZE"; //$NON-NLS-1$ public static final int STATE_SMALL_VALUE_SIZE_DEFAULT = 50; public static final String STATE_STACKED_VISIBLE_VALUES = "STATE_STACKED_VISIBLE_VALUES"; //$NON-NLS-1$ public static final int STATE_STACKED_VISIBLE_VALUES_DEFAULT = 2; /* * Colors */ static final String STATE_COLOR_ALTITUDE_DOWN = "STATE_COLOR_ALTITUDE_DOWN"; //$NON-NLS-1$ static final RGB STATE_COLOR_ALTITUDE_DOWN_DEFAULT = new RGB(0, 240, 0); static final String STATE_COLOR_ALTITUDE_UP = "STATE_COLOR_ALTITUDE_UP"; //$NON-NLS-1$ static final RGB STATE_COLOR_ALTITUDE_UP_DEFAULT = new RGB(255, 66, 22); static final String STATE_COLOR_TOTALS = "STATE_COLOR_TOTALS"; //$NON-NLS-1$ static final RGB STATE_COLOR_TOTALS_DEFAULT = new RGB(253, 236, 115); private static final int COLUMN_DEFAULT = 0; // sort by time private static final int COLUMN_SPEED = 10; private static final int COLUMN_PACE = 20; private static final int COLUMN_GRADIENT = 30; private static final int COLUMN_PULSE = 40; private static final int COLUMN_CADENCE = 50; private static final float SPEED_DIGIT_VALUE = 10.0f; private static final IPreferenceStore _prefStore = TourbookPlugin.getPrefStore(); private static final IDialogSettings _state = TourbookPlugin.getState(ID); private final boolean _isOSX = net.tourbook.common.UI.IS_OSX; private ColumnManager _columnManager; private TourData _tourData; private float _dpToleranceAltitude; private float _dpToleranceAltitudeMultipleTours; private float _dpTolerancePower; private float _dpTolerancePulse; private float _savedDpToleranceAltitude = -1; private PostSelectionProvider _postSelectionProvider; private ISelectionListener _postSelectionListener; private IPartListener2 _partListener; private IPropertyChangeListener _prefChangeListener; private ITourEventListener _tourEventListener; private final NumberFormat _nf_0_0 = NumberFormat.getNumberInstance(); private final NumberFormat _nf_1_0 = NumberFormat.getNumberInstance(); private final NumberFormat _nf_1_1 = NumberFormat.getNumberInstance(); private final NumberFormat _nf_3_3 = NumberFormat.getNumberInstance(); { _nf_0_0.setMinimumFractionDigits(0); _nf_0_0.setMaximumFractionDigits(0); _nf_1_0.setMinimumFractionDigits(1); _nf_1_0.setMaximumFractionDigits(0); _nf_1_1.setMinimumFractionDigits(1); _nf_1_1.setMaximumFractionDigits(1); _nf_3_3.setMinimumFractionDigits(3); _nf_3_3.setMaximumFractionDigits(3); } private int _maxDistanceSpinner; private int _spinnerDistancePage; private boolean _isTourDirty = false; private boolean _isSaving; /** * when <code>true</code>, the tour dirty flag is disabled to load data into the fields */ private boolean _isDirtyDisabled = false; private boolean _isClearView; private float _altitudeUp; private float _altitudeDown; /** * Contains all available segmenters. * <p> * The sequence defines how they are displayed in the combobox. */ private static final ArrayList<TourSegmenter> _allTourSegmenter = new ArrayList<TourSegmenter>(); static { _allTourSegmenter .add(new TourSegmenter(SegmenterType.ByAltitudeWithDP, Messages.tour_segmenter_type_byAltitude, SEGMENTER_REQUIRES_ALTITUDE | SEGMENTER_REQUIRES_DISTANCE)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByAltitudeWithDPMerged, Messages.Tour_Segmenter_Type_ByAltitude_Merged, SEGMENTER_REQUIRES_ALTITUDE | SEGMENTER_REQUIRES_DISTANCE)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByAltitudeWithMarker, Messages.Tour_Segmenter_Type_ByAltitude_Marker, SEGMENTER_REQUIRES_ALTITUDE | SEGMENTER_REQUIRES_DISTANCE | SEGMENTER_REQUIRES_MARKER)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByMarker, Messages.tour_segmenter_type_byMarker, SEGMENTER_REQUIRES_MARKER)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByDistance, Messages.tour_segmenter_type_byDistance, SEGMENTER_REQUIRES_DISTANCE)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByBreakTime, Messages.Tour_Segmenter_Type_ByBreakTime, SEGMENTER_REQUIRES_DISTANCE)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByPowerWithDP, Messages.tour_segmenter_type_byPower, SEGMENTER_REQUIRES_POWER)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByPulseWithDP, Messages.tour_segmenter_type_byPulse, SEGMENTER_REQUIRES_PULSE)); _allTourSegmenter.add(new TourSegmenter(SegmenterType.ByComputedAltiUpDown, Messages.tour_segmenter_type_byComputedAltiUpDown, SEGMENTER_REQUIRES_ALTITUDE)); } private ArrayList<TourSegmenter> _availableSegmenter = new ArrayList<TourSegmenter>(); /** * segmenter type which the user has selected */ private SegmenterType _userSelectedSegmenterType; private long _tourBreakTime; private float _breakUIMinAvgSpeedAS; private float _breakUIMinSliceSpeedAS; private int _breakUIMinSliceTimeAS; private float _breakUIMinAvgSpeed; private float _breakUIMinSliceSpeed; private int _breakUIShortestBreakTime; private int _breakUISliceDiff; private float _breakUIMaxDistance; private PixelConverter _pc; private int _spinnerWidth; /** * contains the controls which are displayed in the first column, these controls are used to get * the maximum width and set the first column within the differenct section to the same width */ private final ArrayList<Control> _firstColBreakTime = new ArrayList<Control>(); private ActionModifyColumns _actionModifyColumns; private ActionTourChartSegmenterConfig _actionTCSegmenterConfig; private boolean _isGetInitialTours; private ArrayList<TourSegment> _allTourSegments; /* * UI resources */ private Font _boldFont; private final ColorCache _colorCache = new ColorCache(); /* * UI controls */ private Composite _parent; private PageBook _pageBookUI; private PageBook _pageBookSegmenter; private PageBook _pageBookBreakTime; private Button _btnSaveTourDP; private Button _btnSaveTourMin; private Composite _containerBreakTime; private Composite _containerViewer; private Composite _pageSegmenter; private Composite _pageBreakBy_AvgSliceSpeed; private Composite _pageBreakBy_AvgSpeed; private Composite _pageBreakBy_SliceSpeed; private Composite _pageBreakBy_TimeDistance; private Composite _pageNoData; private Composite _pageSegType_DPAltitude; private Composite _pageSegType_DPPower; private Composite _pageSegType_DPPulse; private Composite _pageSegType_ByMarker; private Composite _pageSegType_ByDistance; private Composite _pageSegType_ByAltiUpDown; private Composite _pageSegType_ByBreakTime; private Combo _comboBreakMethod; private Combo _comboSegmenterType; private ImageComboLabel _lblTitle; private Label _lblAltitudeUpDP; private Label _lblAltitudeUpMin; private Label _lblBreakDistanceUnit; private Label _lblDistanceValue; private Label _lblMinAltitude; private Label _lblNumSegments; private Label _lblTourBreakTime; private Spinner _spinnerBreak_MinAvgSpeedAS; private Spinner _spinnerBreak_MinSliceSpeedAS; private Spinner _spinnerBreak_MinSliceTimeAS; private Spinner _spinnerBreak_MinAvgSpeed; private Spinner _spinnerBreak_MinSliceSpeed; private Spinner _spinnerBreak_ShortestTime; private Spinner _spinnerBreak_MaxDistance; private Spinner _spinnerBreak_SliceDiff; private Spinner _spinnerDistance; private Spinner _spinnerDPTolerance_Altitude; private Spinner _spinnerDPTolerance_Power; private Spinner _spinnerDPTolerance_Pulse; private Spinner _spinnerMinAltitude; private TableViewer _segmentViewer; /** * {@link TourChart} contains the chart for the tour, this is necessary to move the slider in * the chart to a selected segment */ private TourChart _tourChart; public static enum SegmenterType { ByAltitudeWithDP, // ByAltitudeWithDPMerged, // ByAltitudeWithMarker, // ByPowerWithDP, // ByPulseWithDP, // ByMarker, // ByDistance, // ByComputedAltiUpDown, // ByBreakTime, // } /** * The content provider class is responsible for providing objects to the view. It can wrap * existing objects in adapters or simply return objects as-is. These objects may be sensitive * to the current input of the view, or ignore it and always show the same content (like Task * List, for example). */ class ViewContentProvider implements IStructuredContentProvider { public ViewContentProvider() { } @Override public void dispose() { } @Override public Object[] getElements(final Object parent) { if (_tourData == null) { return new Object[0]; } else { final Object[] tourSegments = createSegmenterContent(); updateUI_Altitude(); updateUI_BreakTime(); updateUI_SegmenterInfo(tourSegments); return tourSegments; } } @Override public void inputChanged(final Viewer v, final Object oldInput, final Object newInput) { } } private static class ViewSorter extends ViewerSorter { // private static final int ASCENDING = 0; private static final int DESCENDING = 1; private int _column; private int _direction; /** * Compares the object for sorting */ @Override public int compare(final Viewer viewer, final Object obj1, final Object obj2) { final TourSegment segment1 = ((TourSegment) obj1); final TourSegment segment2 = ((TourSegment) obj2); // sort total item to the end of the view if (segment1.isTotal) { return 1; } int rc = 0; // Determine which column and do the appropriate sort switch (_column) { case COLUMN_DEFAULT: rc = segment1.serieIndex_Start - segment2.serieIndex_Start; if (_direction == DESCENDING) { rc = -rc; } break; case COLUMN_SPEED: rc = (int) ((segment1.speed - segment2.speed) * 100); break; case COLUMN_PACE: rc = (int) ((segment1.pace - segment2.pace) * 100); break; case COLUMN_PULSE: rc = (int) (segment1.pulse - segment2.pulse); break; case COLUMN_GRADIENT: rc = (int) ((segment1.gradient - segment2.gradient) * 100); break; case COLUMN_CADENCE: rc = (int) (segment1.cadence - segment2.cadence); break; } // If descending order, flip the direction if (_direction == DESCENDING) { rc = -rc; } return rc; } /** * Does the sort. If it's a different column from the previous sort, do an ascending sort. * If it's the same column as the last sort, toggle the sort direction. * * @param column */ public void setSortColumn(final int column) { if (column == _column) { // Same column as last sort; toggle the direction _direction = 1 - _direction; } else { // New column; do an descending sort _column = column; _direction = DESCENDING; } } } /** * Constructor */ public TourSegmenterView() { super(); } public static IDialogSettings getState() { return _state; } private void addPartListener() { // set the part listener _partListener = new IPartListener2() { @Override public void partActivated(final IWorkbenchPartReference partRef) { } @Override public void partBroughtToTop(final IWorkbenchPartReference partRef) { } @Override public void partClosed(final IWorkbenchPartReference partRef) { if (partRef.getPart(false) == TourSegmenterView.this) { onPartClosed(); } } @Override public void partDeactivated(final IWorkbenchPartReference partRef) { } @Override public void partHidden(final IWorkbenchPartReference partRef) { } @Override public void partInputChanged(final IWorkbenchPartReference partRef) { } @Override public void partOpened(final IWorkbenchPartReference partRef) { if (partRef.getPart(false) == TourSegmenterView.this) { onPartOpened(); } } @Override public void partVisible(final IWorkbenchPartReference partRef) { } }; getSite().getPage().addPartListener(_partListener); } private void addPrefListener() { _prefChangeListener = new IPropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent event) { final String property = event.getProperty(); if (property.equals(ITourbookPreferences.MEASUREMENT_SYSTEM)) { // measurement system has changed net.tourbook.ui.UI.updateUnits(); /* * update viewer */ _columnManager.saveState(_state); _columnManager.clearColumns(); defineAllColumns(_containerViewer); recreateViewer(null); /* * update distance */ setMaxDistanceSpinner(); _spinnerDistance.setMaximum(_maxDistanceSpinner); _spinnerDistance.setPageIncrement(_spinnerDistancePage); updateUI_Distance(); /* * update min altitude */ _lblMinAltitude.setText(net.tourbook.common.UI.UNIT_LABEL_DISTANCE); _lblMinAltitude.pack(true); createSegments(true); } else if (property.equals(ITourbookPreferences.GRAPH_MARKER_IS_MODIFIED)) { // marker is hidden/visible final TourSegmenter selectedSegmenter = getSelectedSegmenter(); if (SegmenterType.ByAltitudeWithMarker.equals(selectedSegmenter.segmenterType)) { // this could be optimized to check if marker visibility has changed or not onSelect_SegmenterType(false); } } else if (property.equals(ITourbookPreferences.VIEW_LAYOUT_CHANGED)) { _segmentViewer.getTable() .setLinesVisible(_prefStore.getBoolean(ITourbookPreferences.VIEW_LAYOUT_DISPLAY_LINES)); _segmentViewer.refresh(); /* * the tree must be redrawn because the styled text does not show with the new * color */ _segmentViewer.getTable().redraw(); } } }; _prefStore.addPropertyChangeListener(_prefChangeListener); } private void addSelectionListener() { _postSelectionListener = new ISelectionListener() { @Override public void selectionChanged(final IWorkbenchPart part, final ISelection selection) { if (part == TourSegmenterView.this) { return; } onSelectionChanged(selection); } }; getSite().getPage().addPostSelectionListener(_postSelectionListener); } private void addTourEventListener() { _tourEventListener = new ITourEventListener() { @Override public void tourChanged(final IWorkbenchPart part, final TourEventId eventId, final Object eventData) { if (part == TourSegmenterView.this) { return; } if (eventId == TourEventId.TOUR_SELECTION && eventData instanceof ISelection) { onSelectionChanged((ISelection) eventData); } else { if (_tourData == null) { return; } if (eventId == TourEventId.TOUR_CHANGED && eventData instanceof TourEvent) { final TourEvent tourEvent = (TourEvent) eventData; final ArrayList<TourData> modifiedTours = tourEvent.getModifiedTours(); if (modifiedTours == null || modifiedTours.size() == 0) { return; } final TourData modifiedTourData = modifiedTours.get(0); final long viewTourId = _tourData.getTourId(); if (modifiedTourData.getTourId() == viewTourId) { // update existing tour if (checkDataValidation(modifiedTourData)) { if (tourEvent.isReverted) { /* * tour is reverted, saving existing tour is not necessary, just * update the tour */ setTour(modifiedTourData, true); } else { // it's the same tour but tour is modified onSelectionChanged(new SelectionTourData(null, modifiedTourData)); } } } else { // display new tour onSelectionChanged(new SelectionTourData(null, modifiedTourData)); } // removed old tour data from the selection provider _postSelectionProvider.clearSelection(); } else if (eventId == TourEventId.CLEAR_DISPLAYED_TOUR) { clearView(); } else if (eventId == TourEventId.SLIDER_POSITION_CHANGED && eventData instanceof SelectionChartXSliderPosition) { final SelectionChartXSliderPosition xSliderSelection = (SelectionChartXSliderPosition) eventData; final Object customData = xSliderSelection.getCustomData(); if (customData instanceof SelectedTourSegmenterSegments) { /* * This event is fired in the tour chart when a toursegmenter segment is * selected */ selectTourSegments((SelectedTourSegmenterSegments) customData); } } else if (eventId == TourEventId.TOUR_CHART_PROPERTY_IS_MODIFIED) { // tour chart smoothing can be modified createSegments(false); } else if (eventId == TourEventId.UPDATE_UI) { // check if a tour must be updated if (_tourData == null) { return; } final Long tourId = _tourData.getTourId(); // update ui if (net.tourbook.ui.UI.containsTourId(eventData, tourId) != null) { setTour(TourManager.getInstance().getTourData(tourId), true); } } } } }; TourManager.getInstance().addTourEventListener(_tourEventListener); } /** * check if data for the segmenter is valid */ private boolean checkDataValidation(final TourData tourData) { /* * tourdata and time serie are necessary to create any segment */ if (tourData == null || tourData.timeSerie == null || tourData.timeSerie.length < 2) { clearView(); return false; } if (checkSegmenterData(tourData) == 0) { clearView(); return false; } _pageBookUI.showPage(_pageSegmenter); enableActions(); return true; } private int checkSegmenterData(final TourData tourData) { final float[] altitudeSerie = tourData.getAltitudeSmoothedSerie(false); final float[] powerSerie = tourData.getPowerSerie(); final float[] metricDistanceSerie = tourData.getMetricDistanceSerie(); final float[] pulseSerie = tourData.pulseSerie; final Object[] markerSerie; if (tourData.isMultipleTours()) { markerSerie = tourData.multiTourMarkers.toArray(); } else { markerSerie = tourData.getTourMarkers().toArray(); } int checkedSegmenterData = 0; checkedSegmenterData |= altitudeSerie != null && altitudeSerie.length > 1 ? // SEGMENTER_REQUIRES_ALTITUDE : 0; checkedSegmenterData |= metricDistanceSerie != null && metricDistanceSerie.length > 1 ? SEGMENTER_REQUIRES_DISTANCE : 0; checkedSegmenterData |= powerSerie != null && powerSerie.length > 1 ? // SEGMENTER_REQUIRES_POWER : 0; checkedSegmenterData |= pulseSerie != null && pulseSerie.length > 1 ? // SEGMENTER_REQUIRES_PULSE : 0; checkedSegmenterData |= markerSerie != null && markerSerie.length > 0 ? // SEGMENTER_REQUIRES_MARKER : 0; return checkedSegmenterData; } private void clearView() { _pageBookUI.showPage(_pageNoData); _tourData = null; _tourChart = null; _isGetInitialTours = true; _isClearView = true; // removed old tour data from the selection provider _postSelectionProvider.clearSelection(); enableActions(); } private void createActions() { _actionModifyColumns = new ActionModifyColumns(this); _actionTCSegmenterConfig = new ActionTourChartSegmenterConfig(this, _parent); } @Override public void createPartControl(final Composite parent) { _parent = parent; _pc = new PixelConverter(parent); _spinnerWidth = _pc.convertWidthInCharsToPixels(_isOSX ? 10 : 5); _boldFont = JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT); setMaxDistanceSpinner(); // define all columns _columnManager = new ColumnManager(this, _state); _columnManager.setIsCategoryAvailable(true); defineAllColumns(parent); createUI(parent); createActions(); fillToolbar(); addSelectionListener(); addPartListener(); addPrefListener(); addTourEventListener(); // tell the site that this view is a selection provider getSite().setSelectionProvider(_postSelectionProvider = new PostSelectionProvider(ID)); _pageBookUI.showPage(_pageNoData); restoreState(); enableActions(); showTour(); } private Object[] createSegmenterContent() { final TourSegmenter selectedSegmenter = getSelectedSegmenter(); if (selectedSegmenter == null) { return new Object[0]; } /* * get break time values: time/distance & speed */ final BreakTimeTool btConfig; if (selectedSegmenter.segmenterType == SegmenterType.ByBreakTime) { // use segmenter values btConfig = new BreakTimeTool(getSelectedBreakMethod().methodId, _breakUIShortestBreakTime, _breakUIMaxDistance, _breakUIMinSliceSpeed, _breakUIMinAvgSpeed, _breakUISliceDiff, _breakUIMinAvgSpeedAS, _breakUIMinSliceSpeedAS, _breakUIMinSliceTimeAS); } else { // use pref values for time/distance & speed btConfig = BreakTimeTool.getPrefValues(); } _allTourSegments = _tourData.createSegmenterSegments(btConfig); return _allTourSegments == null // ? new Object[0] : _allTourSegments.toArray(); } /** * create points for the simplifier from distance and altitude * * @param isFireEvent */ private void createSegments(final boolean isFireEvent) { if (_isSaving) { return; } if (_tourData == null) { _pageBookUI.showPage(_pageNoData); enableActions(); return; } // disable computed altitude _tourData.segmentSerie_Altitude_Diff_Computed = null; // reset 2nd index _tourData.segmentSerieIndex2nd = null; final TourSegmenter selectedSegmenter = getSelectedSegmenter(); if (selectedSegmenter == null) { clearView(); return; } final SegmenterType selectedSegmenterType = selectedSegmenter.segmenterType; if (selectedSegmenterType == SegmenterType.ByAltitudeWithDP) { createSegmentsBy_AltitudeWithDP(); } else if (selectedSegmenterType == SegmenterType.ByAltitudeWithDPMerged) { final int[] forcedIndices = getTourIndices(); _tourData.segmentSerieIndex = createSegmentsBy_AltitudeWithDPMerged(forcedIndices); } else if (selectedSegmenterType == SegmenterType.ByAltitudeWithMarker) { final int[] forcedIndices = getTourAndMarkerIndices(); final int[] segmentSerieIndices = createSegmentsBy_AltitudeWithDPMerged(forcedIndices); createSegmentsBy_AltitudeWithMarker(forcedIndices, segmentSerieIndices); } else if (selectedSegmenterType == SegmenterType.ByPowerWithDP) { createSegmentsBy_PowerWithDP(); } else if (selectedSegmenterType == SegmenterType.ByPulseWithDP) { createSegmentsBy_PulseWithDP(); } else if (selectedSegmenterType == SegmenterType.ByDistance) { createSegmentsBy_Distance(); } else if (selectedSegmenterType == SegmenterType.ByMarker) { createSegmentsBy_Marker(); } else if (selectedSegmenterType == SegmenterType.ByComputedAltiUpDown) { createSegmentsBy_AltiUpDown(); } else if (selectedSegmenterType == SegmenterType.ByBreakTime) { createSegmentsBy_BreakTime(); } // update table and create the tour segments in tour data reloadViewer(); if (isFireEvent) { fireSegmentLayerChanged(); } } /** * create Douglas-Peucker segments from distance and altitude */ private void createSegmentsBy_AltitudeWithDP() { final float[] distanceSerie = _tourData.getMetricDistanceSerie(); final float[] altitudeSerie = _tourData.getAltitudeSmoothedSerie(false); // convert data series into dp points final DPPoint graphPoints[] = new DPPoint[distanceSerie.length]; for (int serieIndex = 0; serieIndex < graphPoints.length; serieIndex++) { graphPoints[serieIndex] = new DPPoint(distanceSerie[serieIndex], altitudeSerie[serieIndex], serieIndex); } final Object[] dpPoints = new DouglasPeuckerSimplifier(// _dpToleranceAltitude, graphPoints, getTourIndices()).simplify(); /* * copie the data index for the simplified points into the tour data */ final int[] segmentSerieIndex = _tourData.segmentSerieIndex = new int[dpPoints.length]; for (int iPoint = 0; iPoint < dpPoints.length; iPoint++) { final DPPoint point = (DPPoint) dpPoints[iPoint]; segmentSerieIndex[iPoint] = point.serieIndex; } } /** * Create Douglas-Peucker segments from distance and altitude. All segments are merged which * have the same vertical direction. * * @return */ private int[] createSegmentsBy_AltitudeWithDPMerged(final int[] forcedIndices) { final float[] distanceSerie = _tourData.getMetricDistanceSerie(); final float[] altitudeSerie = _tourData.getAltitudeSmoothedSerie(false); final int serieSize = distanceSerie.length; // convert data series into dp points final DPPoint graphPoints[] = new DPPoint[serieSize]; for (int serieIndex = 0; serieIndex < graphPoints.length; serieIndex++) { graphPoints[serieIndex] = new DPPoint(// distanceSerie[serieIndex], altitudeSerie[serieIndex], serieIndex); } final Object[] simplePoints = new DouglasPeuckerSimplifier(// _dpToleranceAltitude, graphPoints, forcedIndices).simplify(); /* * copie the data index for the simplified points into the tour data */ int forcedIndex = 0; int forcedIndexIndex = 0; if (forcedIndices != null && forcedIndices.length > 0) { forcedIndexIndex++; forcedIndex = forcedIndices[forcedIndexIndex]; } final TIntArrayList segmentSerieIndex = new TIntArrayList(); // set first point segmentSerieIndex.add(0); DPPoint prevDpPoint = (DPPoint) simplePoints[0]; double prevAltitude = prevDpPoint.y; boolean isPrevAltiUp = false; boolean isPrevAltiDown = false; for (int simpleIndex = 1; simpleIndex < simplePoints.length; simpleIndex++) { final DPPoint currentDpPoint = (DPPoint) simplePoints[simpleIndex]; boolean isAddPoint = false; if (forcedIndices != null && forcedIndex == prevDpPoint.serieIndex) { // this is a forced point /* * This algorithm ensures that the points are set only once, highly complicated * algorithm but now it works. */ isAddPoint = true; // get next forced index forcedIndexIndex++; if (forcedIndexIndex < forcedIndices.length) { forcedIndex = forcedIndices[forcedIndexIndex]; } } final double currentAltitude = currentDpPoint.y; if (simpleIndex == 1) { // first point isPrevAltiUp = (currentAltitude - prevAltitude) >= 0; isPrevAltiDown = (currentAltitude - prevAltitude) < 0; } else { // all other points final boolean isCurrentAltiUp = (currentAltitude - prevAltitude) >= 0; final boolean isCurrentAltiDown = (currentAltitude - prevAltitude) < 0; if (isPrevAltiUp && isCurrentAltiUp || isPrevAltiDown && isCurrentAltiDown) { // up or down have not changed } else { // up or down have changed isAddPoint = true; isPrevAltiUp = isCurrentAltiUp; isPrevAltiDown = isCurrentAltiDown; } } if (isAddPoint) { segmentSerieIndex.add(prevDpPoint.serieIndex); } prevDpPoint = currentDpPoint; prevAltitude = currentAltitude; } // add last point segmentSerieIndex.add(serieSize - 1); return segmentSerieIndex.toArray(); } private void createSegmentsBy_AltitudeWithMarker(final int[] forcedIndices, final int[] segmentSerieIndices) { _tourData.segmentSerieIndex = forcedIndices; _tourData.segmentSerieIndex2nd = segmentSerieIndices; } private void createSegmentsBy_AltiUpDown() { final float selectedMinAltiDiff = (float) (_spinnerMinAltitude.getSelection() / 10.0); final ArrayList<AltitudeUpDownSegment> tourSegements = new ArrayList<AltitudeUpDownSegment>(); // create segment when the altitude up/down is changing _tourData.computeAltitudeUpDown(tourSegements, selectedMinAltiDiff); // convert segment list into array int serieIndex = 0; final int segmentLength = tourSegements.size(); final int[] segmentSerieIndex = _tourData.segmentSerieIndex = new int[segmentLength]; final float[] altitudeDiff = _tourData.segmentSerie_Altitude_Diff_Computed = new float[segmentLength]; for (final AltitudeUpDownSegment altitudeUpDownSegment : tourSegements) { segmentSerieIndex[serieIndex] = altitudeUpDownSegment.serieIndex; altitudeDiff[serieIndex] = altitudeUpDownSegment.computedAltitudeDiff; serieIndex++; } } private void createSegmentsBy_BreakTime() { boolean[] breakTimeSerie = null; BreakTimeResult breakTimeResult = null; final String breakMethodId = getSelectedBreakMethod().methodId; if (breakMethodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_TIME_DISTANCE)) { _breakUIShortestBreakTime = _spinnerBreak_ShortestTime.getSelection(); _breakUIMaxDistance = _spinnerBreak_MaxDistance.getSelection() * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE_SMALL; _breakUISliceDiff = _spinnerBreak_SliceDiff.getSelection(); breakTimeResult = BreakTimeTool.computeBreakTimeByTimeDistance(_tourData, _breakUIShortestBreakTime, _breakUIMaxDistance, _breakUISliceDiff); } else if (breakMethodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_SLICE_SPEED)) { _breakUIMinSliceSpeed = _spinnerBreak_MinSliceSpeed.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; breakTimeResult = BreakTimeTool.computeBreakTimeBySpeed(_tourData, breakMethodId, _breakUIMinSliceSpeed); } else if (breakMethodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_AVG_SPEED)) { _breakUIMinAvgSpeed = _spinnerBreak_MinAvgSpeed.getSelection()// / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; breakTimeResult = BreakTimeTool.computeBreakTimeBySpeed(_tourData, breakMethodId, _breakUIMinAvgSpeed); } else if (breakMethodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_AVG_SLICE_SPEED)) { _breakUIMinAvgSpeedAS = _spinnerBreak_MinAvgSpeedAS.getSelection()// / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; _breakUIMinSliceSpeedAS = _spinnerBreak_MinSliceSpeedAS.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; _breakUIMinSliceTimeAS = _spinnerBreak_MinSliceTimeAS.getSelection(); breakTimeResult = BreakTimeTool.computeBreakTimeByAvgSliceSpeed(_tourData, _breakUIMinAvgSpeedAS, _breakUIMinSliceSpeedAS, _breakUIMinSliceTimeAS); } breakTimeSerie = breakTimeResult.breakTimeSerie; _tourBreakTime = breakTimeResult.tourBreakTime; /* * convert recognized breaks into segments */ final TIntArrayList segmentSerieIndex = new TIntArrayList(); // set start for first segment segmentSerieIndex.add(0); boolean prevIsBreak = false; boolean isBreak = breakTimeSerie[0]; for (int serieIndex = 1; serieIndex < breakTimeSerie.length; serieIndex++) { isBreak = breakTimeSerie[serieIndex]; if (isBreak != prevIsBreak) { // break has toggled, set end index segmentSerieIndex.add(serieIndex - 1); } prevIsBreak = isBreak; } // ensure the last segment ends at the end of the tour final int lastDistanceSerieIndex = _tourData.timeSerie.length - 1; final int serieSize = segmentSerieIndex.size(); if (serieSize == 1 || // // ensure the last index is not duplicated segmentSerieIndex.get(serieSize - 1) != lastDistanceSerieIndex) { segmentSerieIndex.add(lastDistanceSerieIndex); } _tourData.segmentSerieIndex = segmentSerieIndex.toArray(); _tourData.setBreakTimeSerie(breakTimeSerie); } private void createSegmentsBy_Distance() { final float[] distanceSerie = _tourData.getMetricDistanceSerie(); final int lastDistanceSerieIndex = distanceSerie.length - 1; final float segmentDistance = getDistance(); final TIntArrayList segmentSerieIndex = new TIntArrayList(); // set first segment start segmentSerieIndex.add(0); float nextSegmentDistance = segmentDistance; for (int distanceIndex = 0; distanceIndex < distanceSerie.length; distanceIndex++) { final float distance = distanceSerie[distanceIndex]; if (distance >= nextSegmentDistance) { segmentSerieIndex.add(distanceIndex); // set minimum distance for the next segment nextSegmentDistance += segmentDistance; } } // ensure the last segment ends at the end of the tour final int serieSize = segmentSerieIndex.size(); if (serieSize == 1 || // // ensure the last index is not duplicated segmentSerieIndex.get(serieSize - 1) != lastDistanceSerieIndex) { segmentSerieIndex.add(lastDistanceSerieIndex); } _tourData.segmentSerieIndex = segmentSerieIndex.toArray(); } private void createSegmentsBy_Marker() { final boolean isMultipleTours = _tourData.isMultipleTours(); final int[] timeSerie = _tourData.timeSerie; final int numTimeSlices = timeSerie.length; final Collection<TourMarker> tourMarkers = isMultipleTours // ? _tourData.multiTourMarkers : _tourData.getTourMarkers(); // sort markers by time - they can be unsorted final ArrayList<TourMarker> sortedMarkers = new ArrayList<TourMarker>(tourMarkers); Collections.sort(sortedMarkers, new Comparator<TourMarker>() { @Override public int compare(final TourMarker tm1, final TourMarker tm2) { final int result = isMultipleTours // ? tm1.getMultiTourSerieIndex() - tm2.getMultiTourSerieIndex() : tm1.getSerieIndex() - tm2.getSerieIndex(); return result; } }); final TIntArrayList segmenterIndices = new TIntArrayList(); int prevSerieIndex = 0; // set first segment at tour start segmenterIndices.add(prevSerieIndex); final float[] distanceSerie = _tourData.getMetricDistanceSerie(); final float[] altitudeSerie = _tourData.getAltitudeSmoothedSerie(false); // ensure required data are available if (altitudeSerie != null // && altitudeSerie.length > 1 && distanceSerie != null && distanceSerie.length > 1) { // Merge tour with marker indices final int[] tourSerieIndices; if (isMultipleTours) { final int[] forcedIndices = getTourIndices(); tourSerieIndices = Arrays.copyOf(forcedIndices, forcedIndices.length + 1); // add last timeslice index tourSerieIndices[forcedIndices.length] = numTimeSlices - 1; } else { tourSerieIndices = new int[] { 0, numTimeSlices - 1 }; } final TIntArrayList markerIndices = new TIntArrayList(); // get a list with all marker indices for (final TourMarker tourMarker : sortedMarkers) { final int serieIndex = isMultipleTours // ? tourMarker.getMultiTourSerieIndex() : tourMarker.getSerieIndex(); markerIndices.add(serieIndex); } final int[] markerSerieIndices = markerIndices.toArray(); int markerIndex = 0; for (final int tourSerieIndex : tourSerieIndices) { for (; markerIndex < markerSerieIndices.length; markerIndex++) { final int markerSerieIndex = markerSerieIndices[markerIndex]; if (markerSerieIndex >= tourSerieIndex) { break; } // prevent to set a second segment at the same position if (markerSerieIndex != prevSerieIndex) { segmenterIndices.add(markerSerieIndex); } prevSerieIndex = markerSerieIndex; } // prevent to set a second segment at the same position if (tourSerieIndex != prevSerieIndex) { segmenterIndices.add(tourSerieIndex); } prevSerieIndex = tourSerieIndex; } } else { // altitude or distance is not available, use the old algorithm // create segment for each marker for (final TourMarker tourMarker : sortedMarkers) { final int serieIndex = isMultipleTours // ? tourMarker.getMultiTourSerieIndex() : tourMarker.getSerieIndex(); // prevent to set a second segment at the same position if (serieIndex != prevSerieIndex) { segmenterIndices.add(serieIndex); } prevSerieIndex = serieIndex; } } // add segment end at the tour end final int lastIndex = numTimeSlices - 1; if (prevSerieIndex != lastIndex) { segmenterIndices.add(lastIndex); } _tourData.segmentSerieIndex = segmenterIndices.toArray(); } /** * Create Douglas-Peucker segments from time and power. */ private void createSegmentsBy_PowerWithDP() { final int[] timeSerie = _tourData.timeSerie; final float[] powerSerie = _tourData.getPowerSerie(); if (powerSerie == null || powerSerie.length < 2) { _tourData.segmentSerieIndex = null; return; } // convert data series into points final DPPoint graphPoints[] = new DPPoint[timeSerie.length]; for (int serieIndex = 0; serieIndex < graphPoints.length; serieIndex++) { graphPoints[serieIndex] = new DPPoint(timeSerie[serieIndex], powerSerie[serieIndex], serieIndex); } final Object[] simplePoints = new DouglasPeuckerSimplifier(// _dpTolerancePower, graphPoints, getTourIndices()).simplify(); /* * copie the data index for the simplified points into the tour data */ final int[] segmentSerieIndex = _tourData.segmentSerieIndex = new int[simplePoints.length]; for (int iPoint = 0; iPoint < simplePoints.length; iPoint++) { final DPPoint point = (DPPoint) simplePoints[iPoint]; segmentSerieIndex[iPoint] = point.serieIndex; } } /** * create Douglas-Peucker segments from time and pulse */ private void createSegmentsBy_PulseWithDP() { final int[] timeSerie = _tourData.timeSerie; final float[] pulseSerie = _tourData.pulseSerie; if (pulseSerie == null || pulseSerie.length < 2) { _tourData.segmentSerieIndex = null; return; } // convert data series into points final DPPoint graphPoints[] = new DPPoint[timeSerie.length]; for (int serieIndex = 0; serieIndex < graphPoints.length; serieIndex++) { graphPoints[serieIndex] = new DPPoint(timeSerie[serieIndex], pulseSerie[serieIndex], serieIndex); } final Object[] simplePoints = new DouglasPeuckerSimplifier(// _dpTolerancePulse, graphPoints, getTourIndices()).simplify(); /* * copie the data index for the simplified points into the tour data */ final int[] segmentSerieIndex = _tourData.segmentSerieIndex = new int[simplePoints.length]; for (int iPoint = 0; iPoint < simplePoints.length; iPoint++) { final DPPoint point = (DPPoint) simplePoints[iPoint]; segmentSerieIndex[iPoint] = point.serieIndex; } } private void createUI(final Composite parent) { _pageBookUI = new PageBook(parent, SWT.NONE); _pageNoData = new Composite(_pageBookUI, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(_pageNoData); GridLayoutFactory.swtDefaults().numColumns(1).applyTo(_pageNoData); { final Label lblNoData = new Label(_pageNoData, SWT.WRAP); GridDataFactory.fillDefaults().grab(true, true).applyTo(lblNoData); lblNoData.setText(Messages.Tour_Segmenter_Label_no_chart); } _pageSegmenter = new Composite(_pageBookUI, SWT.NONE); GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(_pageSegmenter); { createUI_10_Header(_pageSegmenter); createUI_70_Viewer(_pageSegmenter); } } private void createUI_10_Header(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(4).extendedMargins(3, 3, 3, 5).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); { { // tour title _lblTitle = new ImageComboLabel(container, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).span(4, 1).applyTo(_lblTitle); } { // label: create segments with final Label label = new Label(container, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label); label.setText(Messages.tour_segmenter_label_createSegmentsWith); } { // combo: segmenter type _comboSegmenterType = new Combo(container, SWT.READ_ONLY); _comboSegmenterType.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_SegmenterType(true); } }); /* * fill the segmenter list that the next layout shows the combo that all segmenter * names are visible */ for (final TourSegmenter segmenter : _allTourSegmenter) { _comboSegmenterType.add(segmenter.name); } final Point size = _comboSegmenterType.computeSize(SWT.DEFAULT, SWT.DEFAULT); GridDataFactory.fillDefaults().hint(size).applyTo(_comboSegmenterType); } { // label: number of segments _lblNumSegments = new Label(container, SWT.LEAD); _lblNumSegments.setToolTipText(Messages.Tour_Segmenter_Label_NumberOfSegments_Tooltip); GridDataFactory.fillDefaults()// .align(SWT.FILL, SWT.CENTER).hint(_pc.convertWidthInCharsToPixels(12), SWT.DEFAULT) .applyTo(_lblNumSegments); } // pagebook: segmenter type _pageBookSegmenter = new PageBook(container, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).span(4, 1).applyTo(_pageBookSegmenter); { _pageSegType_DPAltitude = createUI_42_SegmenterBy_DPAltitude(_pageBookSegmenter); _pageSegType_DPPulse = createUI_43_SegmenterBy_DPPulse(_pageBookSegmenter); _pageSegType_DPPower = createUI_44_SegmenterBy_DPPower(_pageBookSegmenter); _pageSegType_ByMarker = createUI_45_SegmenterBy_Marker(_pageBookSegmenter); _pageSegType_ByDistance = createUI_46_SegmenterBy_Distance(_pageBookSegmenter); _pageSegType_ByAltiUpDown = createUI_48_SegmenterBy_MinAltitude(_pageBookSegmenter); _pageSegType_ByBreakTime = createUI_50_SegmenterBy_BreakTime(_pageBookSegmenter); } } } private Composite createUI_42_SegmenterBy_DPAltitude(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(4).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GREEN)); { _spinnerDPTolerance_Altitude = createUI_DP_Tolerance(container); _lblAltitudeUpDP = createUI_DP_Info(container); _btnSaveTourDP = createUI_DB_SaveTour(container); } return container; } private Composite createUI_43_SegmenterBy_DPPulse(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); { _spinnerDPTolerance_Pulse = createUI_DP_Tolerance(container); } return container; } private Composite createUI_44_SegmenterBy_DPPower(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); { _spinnerDPTolerance_Power = createUI_DP_Tolerance(container); } return container; } private Composite createUI_45_SegmenterBy_Marker(final Composite parent) { /* * display NONE, this is not easy to do - or I didn't find an easier way */ final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().applyTo(container); { final Canvas canvas = new Canvas(container, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.BEGINNING).hint(1, 1).applyTo(canvas); } return container; } private Composite createUI_46_SegmenterBy_Distance(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); { // label: distance final Label label = new Label(container, SWT.NONE); label.setText(Messages.tour_segmenter_segType_byDistance_label); // spinner: distance _spinnerDistance = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().applyTo(_spinnerDistance); _spinnerDistance.setMinimum(1); // 0.1 _spinnerDistance.setMaximum(_maxDistanceSpinner); _spinnerDistance.setPageIncrement(_spinnerDistancePage); _spinnerDistance.setDigits(1); _spinnerDistance.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_Distance(); } }); _spinnerDistance.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_Distance(); } }); // text: distance value _lblDistanceValue = new Label(container, SWT.NONE); _lblDistanceValue.setText(Messages.tour_segmenter_segType_byDistance_defaultDistance); GridDataFactory.fillDefaults()// .align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(_lblDistanceValue); } return container; } private Composite createUI_48_SegmenterBy_MinAltitude(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(5).applyTo(container); { // label: min alti diff final Label label = new Label(container, SWT.NONE); label.setText(Messages.tour_segmenter_segType_byUpDownAlti_label); // spinner: minimum altitude _spinnerMinAltitude = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().applyTo(_spinnerMinAltitude); _spinnerMinAltitude.setMinimum(1); // 0.1 _spinnerMinAltitude.setMaximum(10000); // 1000 _spinnerMinAltitude.setDigits(1); _spinnerMinAltitude.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { createSegments(true); } }); _spinnerMinAltitude.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: unit _lblMinAltitude = new Label(container, SWT.NONE); _lblMinAltitude.setText(net.tourbook.common.UI.UNIT_LABEL_ALTITUDE); _lblAltitudeUpMin = createUI_DP_Info(container); _btnSaveTourMin = createUI_DB_SaveTour(container); } return container; } private Composite createUI_50_SegmenterBy_BreakTime(final Composite parent) { _containerBreakTime = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(_containerBreakTime); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(_containerBreakTime); // _containerBreakTime.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); { final Composite container = new Composite(_containerBreakTime, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(1).applyTo(container); { createUI_51_TourBreakTime(container); createUI_52_BreakTimePageBook(container); } createUI_59_BreakActions(_containerBreakTime); } _containerBreakTime.layout(true, true); UI.setEqualizeColumWidths(_firstColBreakTime); return _containerBreakTime; } private void createUI_51_TourBreakTime(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults()// // .span(2, 1) .grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GREEN)); { /* * tour break time */ // label: break time final Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_TourBreakTime); GridDataFactory.fillDefaults()// .align(SWT.FILL, SWT.CENTER).applyTo(label); _firstColBreakTime.add(label); // label: value + unit _lblTourBreakTime = new Label(container, SWT.NONE); GridDataFactory.fillDefaults()// .align(SWT.FILL, SWT.CENTER).grab(true, false) // .span(2, 1) .applyTo(_lblTourBreakTime); } } private void createUI_52_BreakTimePageBook(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults()// .numColumns(2).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_MAGENTA)); { /* * label: compute break time by */ final Label label = new Label(container, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).applyTo(label); label.setText(Messages.Compute_BreakTime_Label_ComputeBreakTimeBy); _firstColBreakTime.add(label); _comboBreakMethod = new Combo(container, SWT.READ_ONLY | SWT.BORDER); _comboBreakMethod.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_BreakTimeMethod(); } }); // fill combo for (final BreakTimeMethod breakMethod : BreakTimeTool.BREAK_TIME_METHODS) { _comboBreakMethod.add(breakMethod.uiText); } /* * pagebook: break algorithm */ _pageBookBreakTime = new PageBook(container, SWT.NONE); GridDataFactory.fillDefaults().span(2, 1).grab(true, false).applyTo(_pageBookBreakTime); { _pageBreakBy_AvgSliceSpeed = createUI_53_BreakBy_AvgSliceSpeed(_pageBookBreakTime); _pageBreakBy_AvgSpeed = createUI_54_BreakBy_AvgSpeed(_pageBookBreakTime); _pageBreakBy_SliceSpeed = createUI_55_BreakBy_SliceSpeed(_pageBookBreakTime); _pageBreakBy_TimeDistance = createUI_56_BreakBy_TimeDistance(_pageBookBreakTime); } /* * force pages to be displayed otherwise they are hidden or the hint is not computed for * the first column until a resize is done */ // _pagebookBreakTime.showPage(_pageBreakBySliceSpeed); // _pagebookBreakTime.showPage(_pageBreakByAvgSpeed); // _pageBreakBySliceSpeed.layout(true, true); // _pageBreakByAvgSpeed.layout(true, true); // _pagebookBreakTime.layout(true, true); } } private Composite createUI_53_BreakBy_AvgSliceSpeed(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); { /* * minimum average speed */ { // label: minimum speed Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_MinimumAvgSpeed); _firstColBreakTime.add(label); // spinner: minimum speed _spinnerBreak_MinAvgSpeedAS = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .applyTo(_spinnerBreak_MinAvgSpeedAS); _spinnerBreak_MinAvgSpeedAS.setMinimum(0); // 0.0 km/h _spinnerBreak_MinAvgSpeedAS.setMaximum(PrefPageComputedValues.BREAK_MAX_SPEED_KM_H); // 10.0 km/h _spinnerBreak_MinAvgSpeedAS.setDigits(1); _spinnerBreak_MinAvgSpeedAS.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: km/h label = new Label(container, SWT.NONE); label.setText(net.tourbook.common.UI.UNIT_LABEL_SPEED); } /* * minimum slice speed */ { // label: minimum speed Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_MinimumSliceSpeed); _firstColBreakTime.add(label); // spinner: minimum speed _spinnerBreak_MinSliceSpeedAS = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .applyTo(_spinnerBreak_MinSliceSpeedAS); _spinnerBreak_MinSliceSpeedAS.setMinimum(0); // 0.0 km/h _spinnerBreak_MinSliceSpeedAS.setMaximum(PrefPageComputedValues.BREAK_MAX_SPEED_KM_H); // 10.0 km/h _spinnerBreak_MinSliceSpeedAS.setDigits(1); _spinnerBreak_MinSliceSpeedAS.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: km/h label = new Label(container, SWT.NONE); label.setText(net.tourbook.common.UI.UNIT_LABEL_SPEED); } /* * minimum slice time */ { // label: minimum slice time Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_MinimumSliceTime); _firstColBreakTime.add(label); // spinner: minimum slice time _spinnerBreak_MinSliceTimeAS = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .applyTo(_spinnerBreak_MinSliceTimeAS); _spinnerBreak_MinSliceTimeAS.setMinimum(0); // 0 sec _spinnerBreak_MinSliceTimeAS.setMaximum(10); // 10 sec _spinnerBreak_MinSliceTimeAS.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: seconds label = new Label(container, SWT.NONE); label.setText(Messages.app_unit_seconds); } } return container; } private Composite createUI_54_BreakBy_AvgSpeed(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); { /* * minimum average speed */ // label: minimum speed Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_MinimumAvgSpeed); _firstColBreakTime.add(label); // spinner: minimum speed _spinnerBreak_MinAvgSpeed = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreak_MinAvgSpeed); _spinnerBreak_MinAvgSpeed.setMinimum(0); // 0.0 km/h _spinnerBreak_MinAvgSpeed.setMaximum(PrefPageComputedValues.BREAK_MAX_SPEED_KM_H); // 10.0 km/h _spinnerBreak_MinAvgSpeed.setDigits(1); _spinnerBreak_MinAvgSpeed.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_BreakTime(); } }); _spinnerBreak_MinAvgSpeed.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: km/h label = new Label(container, SWT.NONE); label.setText(net.tourbook.common.UI.UNIT_LABEL_SPEED); } return container; } private Composite createUI_55_BreakBy_SliceSpeed(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); { /* * minimum speed */ // label: minimum speed Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_MinimumSliceSpeed); _firstColBreakTime.add(label); // spinner: minimum speed _spinnerBreak_MinSliceSpeed = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreak_MinSliceSpeed); _spinnerBreak_MinSliceSpeed.setMinimum(0); // 0.0 km/h _spinnerBreak_MinSliceSpeed.setMaximum(PrefPageComputedValues.BREAK_MAX_SPEED_KM_H); // 10.0 km/h _spinnerBreak_MinSliceSpeed.setDigits(1); _spinnerBreak_MinSliceSpeed.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_BreakTime(); } }); _spinnerBreak_MinSliceSpeed.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: km/h label = new Label(container, SWT.NONE); label.setText(net.tourbook.common.UI.UNIT_LABEL_SPEED); } return container; } private Composite createUI_56_BreakBy_TimeDistance(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); { /* * shortest break time */ { // label: break min time Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_MinimumTime); _firstColBreakTime.add(label); // spinner: break minimum time _spinnerBreak_ShortestTime = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreak_ShortestTime); _spinnerBreak_ShortestTime.setMinimum(1); _spinnerBreak_ShortestTime.setMaximum(120); // 120 seconds _spinnerBreak_ShortestTime.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_BreakTime(); } }); _spinnerBreak_ShortestTime.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: unit label = new Label(container, SWT.NONE); label.setText(Messages.App_Unit_Seconds_Small); } /* * recording distance */ { // label: break min distance final Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_MinimumDistance); _firstColBreakTime.add(label); // spinner: break minimum time _spinnerBreak_MaxDistance = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreak_MaxDistance); _spinnerBreak_MaxDistance.setMinimum(1); _spinnerBreak_MaxDistance.setMaximum(1000); // 1000 m/yards _spinnerBreak_MaxDistance.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_BreakTime(); } }); _spinnerBreak_MaxDistance.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: unit _lblBreakDistanceUnit = new Label(container, SWT.NONE); _lblBreakDistanceUnit.setText(net.tourbook.common.UI.UNIT_LABEL_DISTANCE_SMALL); GridDataFactory.fillDefaults()// // .span(2, 1) .align(SWT.FILL, SWT.CENTER).applyTo(_lblBreakDistanceUnit); } /* * slice diff break */ { // label: break slice diff Label label = new Label(container, SWT.NONE); label.setText(Messages.Compute_BreakTime_Label_SliceDiffBreak); label.setToolTipText(Messages.Compute_BreakTime_Label_SliceDiffBreak_Tooltip); _firstColBreakTime.add(label); // spinner: slice diff break time _spinnerBreak_SliceDiff = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreak_SliceDiff); _spinnerBreak_SliceDiff.setMinimum(0); _spinnerBreak_SliceDiff.setMaximum(60); // minutes _spinnerBreak_SliceDiff.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_BreakTime(); } }); _spinnerBreak_SliceDiff.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_BreakTime(); } }); // label: unit label = new Label(container, SWT.NONE); label.setText(Messages.App_Unit_Minute); } } return container; } private void createUI_59_BreakActions(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(false, true).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(1).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); { /* * button: restore from defaults */ final Button btnRestore = new Button(container, SWT.NONE); GridDataFactory.fillDefaults()// .align(SWT.FILL, SWT.END).grab(false, true).applyTo(btnRestore); btnRestore.setText(Messages.Compute_BreakTime_Button_RestoreDefaultValues); btnRestore.setToolTipText(Messages.Compute_BreakTime_Button_RestoreDefaultValues_Tooltip); btnRestore.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { restorePrefValues(); } }); /* * button: set as default values */ final Button btnSetDefault = new Button(container, SWT.NONE); GridDataFactory.fillDefaults()// .applyTo(btnSetDefault); btnSetDefault.setText(Messages.Compute_BreakTime_Button_SetDefaultValues); btnSetDefault.setToolTipText(Messages.Compute_BreakTime_Button_SetDefaultValues_Tooltip); btnSetDefault.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSetDefaults(parent); } }); } } private void createUI_70_Viewer(final Composite parent) { _containerViewer = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(_containerViewer); GridLayoutFactory.fillDefaults().applyTo(_containerViewer); { createUI_80_SegmentViewer(_containerViewer); } } private void createUI_80_SegmentViewer(final Composite parent) { final Table table = new Table(parent, // SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI /* | SWT.BORDER */); table.setHeaderVisible(true); // table.setLinesVisible(true); GridDataFactory.fillDefaults().grab(true, true).applyTo(table); _segmentViewer = new TableViewer(table); _columnManager.createColumns(_segmentViewer); _segmentViewer.setContentProvider(new ViewContentProvider()); _segmentViewer.setSorter(new ViewSorter()); _segmentViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { onSelect_Segment(event); } }); createUI_90_ContextMenu(); } /** * create the views context menu */ private void createUI_90_ContextMenu() { final Table table = (Table) _segmentViewer.getControl(); _columnManager.createHeaderContextMenu(table, null); } private Button createUI_DB_SaveTour(final Composite parent) { final Button btn = new Button(parent, SWT.NONE); GridDataFactory.fillDefaults().indent(5, 0).applyTo(btn); btn.setText(Messages.tour_segmenter_button_updateAltitude); btn.setToolTipText(Messages.Tour_Segmenter_Button_SaveTour_Tooltip); btn.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSaveTourAltitude(); } }); return btn; } private Label createUI_DP_Info(final Composite parent) { final Label label = new Label(parent, SWT.TRAIL); GridDataFactory.fillDefaults()// .align(SWT.END, SWT.CENTER).grab(true, false).hint(_pc.convertWidthInCharsToPixels(18), SWT.DEFAULT) .applyTo(label); return label; } private Spinner createUI_DP_Tolerance(final Composite parent) { // label: DP Tolerance final Link linkDP = new Link(parent, SWT.NONE); linkDP.setText(Messages.Tour_Segmenter_Label_DPTolerance); linkDP.setToolTipText(Messages.Tour_Segmenter_Label_DPTolerance_Tooltip); linkDP.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { WEB.openUrl(PrefPageComputedValues.URL_DOUGLAS_PEUCKER_ALGORITHM); } }); // spinner: DP tolerance final Spinner spinner = new Spinner(parent, SWT.BORDER); GridDataFactory.fillDefaults().applyTo(spinner); spinner.setMinimum(1); // 0.1 spinner.setMaximum(10000); // 1000 spinner.setDigits(1); spinner.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelect_Tolerance(); } }); spinner.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { UI.adjustSpinnerValueOnMouseScroll(event); onSelect_Tolerance(); } }); return spinner; } private void defineAllColumns(final Composite parent) { final SelectionAdapter defaultListener = new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { // sort columns ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_DEFAULT); _segmentViewer.refresh(); } }; defineColumn_Time_RecordingTimeTotal(defaultListener); defineColumn_Time_Recording(defaultListener); defineColumn_Time_Driving(defaultListener); defineColumn_Time_Paused(defaultListener); defineColumn_Motion_DistanceTotal(defaultListener); defineColumn_Motion_Distance(defaultListener); defineColumn_Motion_AvgSpeed(); defineColumn_Motion_AvgPace(); defineColumn_Motion_AvgPace_Difference(); defineColumn_Altitude_Gradient(); defineColumn_Altitude_Hour_Up(defaultListener); defineColumn_Altitude_Hour_Down(defaultListener); defineColumn_Altitude_Diff_SegmentBorder(defaultListener); defineColumn_Altitude_Segment_Up(defaultListener); defineColumn_Altitude_Segment_Down(defaultListener); defineColumn_Altitude_SummarizedBorder_Up(defaultListener); defineColumn_Altitude_SummarizedBorder_Down(defaultListener); defineColumn_Altitude_Diff_SegmentComputed(defaultListener); defineColumn_Altitude_SummarizedComputed_Up(defaultListener); defineColumn_Altitude_SummarizedComputed_Down(defaultListener); defineColumn_Body_AvgPulse(); defineColumn_Body_AvgPulse_Difference(); defineColumn_Powertrain_AvgCadence(); defineColumn_Data_SerieStartEndIndex(); } /** * column: altitude diff segment border (m/ft) */ private void defineColumn_Altitude_Diff_SegmentBorder(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_DIFF_SEGMENT_BORDER.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double altitudeDiff = segment.altitude_Segment_Border_Diff; final double value = altitudeDiff / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; colDef.printDetailValue(cell, value); setCellColor(cell, altitudeDiff); } }); } /** * column: computed altitude diff (m/ft) */ private void defineColumn_Altitude_Diff_SegmentComputed(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_DIFF_SEGMENT_COMPUTED.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double altitudeDiff = segment.altitude_Segment_Computed_Diff; final double value = altitudeDiff / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; colDef.printDetailValue(cell, value); setCellColor(cell, altitudeDiff); } }); } /** * column: gradient */ private void defineColumn_Altitude_Gradient() { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_GRADIENT.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_GRADIENT); _segmentViewer.refresh(); } }); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.gradient; colDef.printDetailValue(cell, value); } }); } /** * column: altitude down m/h */ private void defineColumn_Altitude_Hour_Down(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_ELEVATION_DOWN.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final int drivingTime = segment.time_Driving; if (drivingTime == 0) { cell.setText(UI.EMPTY_STRING); } else { final double altitudeDiff = segment.altitude_Segment_Border_Diff; final double value = altitudeDiff > 0 // ? 0 : (altitudeDiff / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE) / drivingTime * 3600; colDef.printValue_0(cell, value); } } }); } /** * column: altitude up m/h */ private void defineColumn_Altitude_Hour_Up(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_ELEVATION_UP.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final int drivingTime = segment.time_Driving; if (drivingTime == 0) { cell.setText(UI.EMPTY_STRING); } else { final double altitudeDiff = segment.altitude_Segment_Border_Diff; final double value = altitudeDiff < 0 // ? 0 : (altitudeDiff / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE) / drivingTime * 3600; colDef.printValue_0(cell, value); } } }); } /** * Column: Altitude segment down */ private void defineColumn_Altitude_Segment_Down(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_ELEVATION_SEGMENT_DOWN.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.altitude_Segment_Down; colDef.printValue_0(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * Column: Altitude segment up */ private void defineColumn_Altitude_Segment_Up(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_ELEVATION_SEGMENT_UP.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.altitude_Segment_Up; colDef.printValue_0(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: total altitude down (m/ft) */ private void defineColumn_Altitude_SummarizedBorder_Down( final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_SUMMARIZED_BORDER_DOWN.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.altitude_Summarized_Border_Down / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; colDef.printValue_0(cell, value); } }); } /** * column: total altitude up (m/ft) */ private void defineColumn_Altitude_SummarizedBorder_Up(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_SUMMARIZED_BORDER_UP.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.altitude_Summarized_Border_Up / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; colDef.printValue_0(cell, value); } }); } /** * column: total altitude down (m/ft) */ private void defineColumn_Altitude_SummarizedComputed_Down( final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_SUMMARIZED_COMPUTED_DOWN.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.altitude_Summarized_Computed_Down / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; colDef.printValue_0(cell, value); } }); } /** * column: total altitude up (m/ft) */ private void defineColumn_Altitude_SummarizedComputed_Up( final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.ALTITUDE_SUMMARIZED_COMPUTED_UP.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.altitude_Summarized_Computed_Up / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; colDef.printValue_0(cell, value); } }); } /** * column: average pulse */ private void defineColumn_Body_AvgPulse() { final ColumnDefinition colDef; colDef = TableColumnFactory.BODY_AVG_PULSE.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_PULSE); _segmentViewer.refresh(); } }); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.pulse; colDef.printDetailValue(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: pulse difference */ private void defineColumn_Body_AvgPulse_Difference() { final ColumnDefinition colDef; colDef = TableColumnFactory.BODY_AVG_PULSE_DIFFERENCE.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final float pulseDiff = segment.pulse_Diff; if (segment.isTotal) { cell.setText(UI.EMPTY_STRING); } else if (pulseDiff == Integer.MIN_VALUE) { cell.setText(UI.EMPTY_STRING); } else if (pulseDiff == 0) { cell.setText(UI.DASH); } else { cell.setText(Integer.toString((int) pulseDiff)); } } }); } /** * column: data serie start/end index */ private void defineColumn_Data_SerieStartEndIndex() { final ColumnDefinition colDef; colDef = TableColumnFactory.DATA_SERIE_START_END_INDEX.createColumn(_columnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); int startIndex = segment.serieIndex_Start; final int endIndex = segment.serieIndex_End; if (startIndex > 0) { startIndex++; } if (segment.isTotal) { cell.setText(UI.EMPTY_STRING); } else { cell.setText(startIndex == endIndex ? // Integer.toString(startIndex) : startIndex + UI.DASH_WITH_SPACE + endIndex); } } }); } /** * column: average pace */ private void defineColumn_Motion_AvgPace() { final ColumnDefinition colDef; colDef = TableColumnFactory.MOTION_AVG_PACE.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_SPEED); _segmentViewer.refresh(); } }); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final float pace = segment.pace; if (pace == 0) { cell.setText(UI.EMPTY_STRING); } else { cell.setText(net.tourbook.common.UI.format_mm_ss((long) pace)); } if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: pace difference */ private void defineColumn_Motion_AvgPace_Difference() { final ColumnDefinition colDef; colDef = TableColumnFactory.MOTION_AVG_PACE_DIFFERENCE.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final float paceDiff = segment.pace_Diff; if (paceDiff == 0) { cell.setText(UI.EMPTY_STRING); } else { cell.setText(net.tourbook.common.UI.format_mm_ss((long) paceDiff)); } } }); } /** * column: average speed */ private void defineColumn_Motion_AvgSpeed() { final ColumnDefinition colDef; colDef = TableColumnFactory.MOTION_AVG_SPEED.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_SPEED); _segmentViewer.refresh(); } }); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = segment.speed; colDef.printDetailValue(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: distance (km/mile) */ private void defineColumn_Motion_Distance(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.MOTION_DISTANCE.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final double value = (segment.distance_Diff) / (1000 * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE); colDef.printDetailValue(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: TOTAL distance (km/mile) */ private void defineColumn_Motion_DistanceTotal(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.MOTION_DISTANCE_TOTAL.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final float value = (segment.distance_Total) / (1000 * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE); colDef.printDetailValue(cell, value); } }); } /** * column: Average cadence */ private void defineColumn_Powertrain_AvgCadence() { final ColumnDefinition colDef; colDef = TableColumnFactory.POWERTRAIN_AVG_CADENCE.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_CADENCE); _segmentViewer.refresh(); } }); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final float value = segment.cadence; colDef.printDetailValue(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: driving time */ private void defineColumn_Time_Driving(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.TIME_DRIVING_TIME.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final int value = segment.time_Driving; colDef.printDetailValue(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: break time */ private void defineColumn_Time_Paused(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.TIME_PAUSED_TIME.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final int value = segment.time_Break; colDef.printDetailValue(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: recording time */ private void defineColumn_Time_Recording(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.TIME_RECORDING_TIME.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); final int value = segment.time_Recording; colDef.printDetailValue(cell, value); if (segment.isTotal) { setTotalStyle(cell); } } }); } /** * column: TOTAL recording time */ private void defineColumn_Time_RecordingTimeTotal(final SelectionAdapter defaultColumnSelectionListener) { final ColumnDefinition colDef; colDef = TableColumnFactory.TIME_RECORDING_TIME_TOTAL.createColumn(_columnManager, _pc); colDef.setIsDefaultColumn(); colDef.setColumnSelectionListener(defaultColumnSelectionListener); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TourSegment segment = (TourSegment) cell.getElement(); if (segment.isTotal) { cell.setText(UI.EMPTY_STRING); } else { final int value = segment.time_Total; colDef.printDetailValue(cell, value); } } }); } @Override public void dispose() { final IWorkbenchPage wbPage = getSite().getPage(); wbPage.removePostSelectionListener(_postSelectionListener); wbPage.removePartListener(_partListener); _prefStore.removePropertyChangeListener(_prefChangeListener); TourManager.getInstance().removeTourEventListener(_tourEventListener); _colorCache.dispose(); super.dispose(); } private void enableActions() { final boolean isTourAvailable = _tourData != null; _actionTCSegmenterConfig.setEnabled(isTourAvailable); } private void fillToolbar() { /* * fill view menu */ final IMenuManager menuMgr = getViewSite().getActionBars().getMenuManager(); menuMgr.add(_actionModifyColumns); /* * fill view toolbar */ final IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager(); tbm.add(_actionTCSegmenterConfig); tbm.update(true); } /** * Notify listeners to show/hide the segments. */ void fireSegmentLayerChanged() { if (_tourData == null) { // prevent NPE return; } /* * Ensure segments are created because they can be null when tour is saved and a new * instance is displayed */ if (isSegmentLayerVisible() && _tourData.segmentSerieIndex == null) { createSegments(false); } // show/hide the segments in the chart TourManager.fireEventWithCustomData(// TourEventId.SEGMENT_LAYER_CHANGED, _tourData, TourSegmenterView.this); } ColorCache getColorCache() { return _colorCache; } @Override public ColumnManager getColumnManager() { return _columnManager; } /** * @return Returns distance in meters from the spinner control */ private float getDistance() { final float selectedDistance = _spinnerDistance.getSelection(); float spinnerDistance; if (net.tourbook.ui.UI.UNIT_VALUE_DISTANCE == net.tourbook.ui.UI.UNIT_MILE) { // miles are displayed spinnerDistance = (selectedDistance) * 1000 / 8; if (spinnerDistance == 0) { spinnerDistance = 1000 / 8; } // convert mile -> meters spinnerDistance *= net.tourbook.ui.UI.UNIT_MILE; } else { // meters are displayed spinnerDistance = selectedDistance * MAX_DISTANCE_SPINNER_METRIC; // ensure the distance in not below 100m if (spinnerDistance < 100) { spinnerDistance = 100; } } return spinnerDistance; } /** * DB tolerance is saved with factor 10. * * @return Return tour tolerance divided by 10. */ private float getDPTolerance_FromTour() { if (_tourData.isMultipleTours()) { // DP tolerance is saved in the pref store return _dpToleranceAltitudeMultipleTours; } else { return (float) (_tourData.getDpTolerance() / 10.0); } } private BreakTimeMethod getSelectedBreakMethod() { int selectedIndex = _comboBreakMethod.getSelectionIndex(); if (selectedIndex == -1) { selectedIndex = 0; } return BreakTimeTool.BREAK_TIME_METHODS[selectedIndex]; } private TourSegmenter getSelectedSegmenter() { if (_availableSegmenter.size() == 0) { return null; } final int selectedIndex = _comboSegmenterType.getSelectionIndex(); if (selectedIndex != -1) { return _availableSegmenter.get(selectedIndex); } // should not happen return null; } private int[] getTourAndMarkerIndices() { final boolean isMultipleTours = _tourData.isMultipleTours(); final int[] timeSerie = _tourData.timeSerie; final int numTimeSlices = timeSerie.length; final Collection<TourMarker> tourMarkers = isMultipleTours // ? _tourData.multiTourMarkers : _tourData.getTourMarkers(); // sort markers by time - they can be unsorted final ArrayList<TourMarker> sortedMarkers = new ArrayList<TourMarker>(tourMarkers); Collections.sort(sortedMarkers, new Comparator<TourMarker>() { @Override public int compare(final TourMarker tm1, final TourMarker tm2) { final int result = isMultipleTours // ? tm1.getMultiTourSerieIndex() - tm2.getMultiTourSerieIndex() : tm1.getSerieIndex() - tm2.getSerieIndex(); return result; } }); final TIntArrayList forcedIndices = new TIntArrayList(); int prevSerieIndex = 0; // set first segment at tour start forcedIndices.add(prevSerieIndex); // Merge tour with marker indices final int[] tourSerieIndices; if (isMultipleTours) { final int[] tourIndices = getTourIndices(); tourSerieIndices = Arrays.copyOf(tourIndices, tourIndices.length + 1); // add last timeslice index tourSerieIndices[tourIndices.length] = numTimeSlices - 1; } else { tourSerieIndices = new int[] { 0, numTimeSlices - 1 }; } final TIntArrayList markerIndices = new TIntArrayList(); // get a list with all marker indices for (final TourMarker tourMarker : sortedMarkers) { if (tourMarker.isMarkerVisible() == false) { // irgnore hidden marker continue; } final int serieIndex = isMultipleTours // ? tourMarker.getMultiTourSerieIndex() : tourMarker.getSerieIndex(); markerIndices.add(serieIndex); } final int[] markerSerieIndices = markerIndices.toArray(); int markerIndex = 0; for (final int tourSerieIndex : tourSerieIndices) { for (; markerIndex < markerSerieIndices.length; markerIndex++) { final int markerSerieIndex = markerSerieIndices[markerIndex]; if (markerSerieIndex >= tourSerieIndex) { break; } // prevent to set a second segment at the same position if (markerSerieIndex != prevSerieIndex) { forcedIndices.add(markerSerieIndex); } prevSerieIndex = markerSerieIndex; } // prevent to set a second segment at the same position if (tourSerieIndex != prevSerieIndex) { forcedIndices.add(tourSerieIndex); } prevSerieIndex = tourSerieIndex; } return forcedIndices.toArray(); } private int[] getTourIndices() { final boolean isMultipleTours = _tourData.isMultipleTours(); final int[] multipleTourStartIndex = _tourData.multipleTourStartIndex; int[] forcedIndices = null; if (isMultipleTours) { /* * Create an extra segment between each tour that a segment do not cover more than 1 * tour. This algorithm was introduced that a selected segment do not show also the next * tour in the tour map. */ int forcedIndex = 0; final int forcedIndicesSize = multipleTourStartIndex.length * 2 - 1; forcedIndices = new int[forcedIndicesSize]; for (final int tourStartIndex : multipleTourStartIndex) { if (tourStartIndex == 0) { // the first tour do not have an extra segment forcedIndices[forcedIndex++] = 0; } else { if (forcedIndex == forcedIndicesSize - 1) { // this is the last tour, it do not have an extra segment forcedIndices[forcedIndex++] = tourStartIndex; } else { // these are all tours between the first and last forcedIndices[forcedIndex++] = tourStartIndex - 1; forcedIndices[forcedIndex++] = tourStartIndex; } } } } return forcedIndices; } public ArrayList<TourSegment> getTourSegments() { return _allTourSegments; } private String getTourTitle() { if (_tourData.isMultipleTours()) { return TourManager.getTourTitleMultiple(_tourData); } else { return TourManager.getTourTitleDetailed(_tourData); } } @Override public ColumnViewer getViewer() { return _segmentViewer; } private boolean isSegmentLayerVisible() { final boolean isSegmentLayerVisible = Util.getStateBoolean(_state, STATE_IS_SHOW_TOUR_SEGMENTS, STATE_IS_SHOW_TOUR_SEGMENTS_DEFAULT); return isSegmentLayerVisible; } /** * @param tourId * @return Returns <code>true</code> when the tour is already displayed in the tour segmenter. */ private boolean isTourDisplayed(final Long tourId) { if (_tourData != null) { if (_tourData.getTourId().equals(tourId)) { // don't reload the same tour return true; } } return false; } private void onPartClosed() { saveState(); _state.put(STATE_IS_SEGMENTER_ACTIVE, false); fireSegmentLayerChanged(); } private void onPartOpened() { _state.put(STATE_IS_SEGMENTER_ACTIVE, true); if (_tourData != null) { fireSegmentLayerChanged(); } } private void onSaveTourAltitude() { _tourData.setTourAltUp(_altitudeUp); _tourData.setTourAltDown(_altitudeDown); // update tolerance into the tour data _tourData.setDpTolerance((short) (_dpToleranceAltitude * 10)); _isTourDirty = true; _tourData = saveTour(); // create segments with newly saved tour that it can be displayed in the tour chart createSegments(false); updateUI_Altitude(); } private void onSelect_BreakTime() { createSegments(true); } private void onSelect_BreakTimeMethod() { final BreakTimeMethod selectedBreakMethod = getSelectedBreakMethod(); if (selectedBreakMethod.methodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_AVG_SPEED)) { _pageBookBreakTime.showPage(_pageBreakBy_AvgSpeed); _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeByAvgSpeed); } else if (selectedBreakMethod.methodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_SLICE_SPEED)) { _pageBookBreakTime.showPage(_pageBreakBy_SliceSpeed); _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeBySliceSpeed); } else if (selectedBreakMethod.methodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_AVG_SLICE_SPEED)) { _pageBookBreakTime.showPage(_pageBreakBy_AvgSliceSpeed); _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeByAvgSliceSpeed); } else if (selectedBreakMethod.methodId.equals(BreakTimeTool.BREAK_TIME_METHOD_BY_TIME_DISTANCE)) { _pageBookBreakTime.showPage(_pageBreakBy_TimeDistance); _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeByTime); } // break method pages have different heights, enforce layout of the whole view part _pageSegmenter.layout(true, true); onSelect_BreakTime(); } private void onSelect_Distance() { updateUI_Distance(); createSegments(true); } private void onSelect_Segment(final SelectionChangedEvent event) { final StructuredSelection selection = (StructuredSelection) event.getSelection(); if (selection != null) { /* * select the chart sliders according to the selected segment(s) */ final Object[] segments = selection.toArray(); if (segments.length > 0) { if (_tourChart == null) { _tourChart = TourManager.getActiveTourChart(_tourData); } final TourSegment firstTourSegment = (TourSegment) (segments[0]); final int serieStartIndex = firstTourSegment.serieIndex_Start; if (segments.length == 1 && firstTourSegment.isTotal) { // only the totals item is selected, do nothing return; } int lastIndex = segments.length - 1; TourSegment lastTourSegment = (TourSegment) (segments[lastIndex]); if (lastTourSegment.isTotal) { // the last index is the totals item, ignore this item lastIndex--; lastTourSegment = (TourSegment) (segments[lastIndex]); } final int serieEndIndex = lastTourSegment.serieIndex_End; final SelectionChartXSliderPosition selectionSliderPosition = new SelectionChartXSliderPosition( _tourChart, serieStartIndex, serieEndIndex, true); /* * Add custom data but only when the segment layer is visible */ if (isSegmentLayerVisible()) { /* * Extend default selection with the segment positions */ final SelectedTourSegmenterSegments selectedSegments = new SelectedTourSegmenterSegments(); selectedSegments.tourData = _tourData; selectedSegments.xSliderSerieIndexLeft = serieStartIndex; selectedSegments.xSliderSerieIndexRight = serieEndIndex; selectionSliderPosition.setCustomData(selectedSegments); } _postSelectionProvider.setSelection(selectionSliderPosition); } } } private void onSelect_SegmenterType(final boolean isUserSelected) { final TourSegmenter selectedSegmenter = getSelectedSegmenter(); if (selectedSegmenter == null) { clearView(); return; } final SegmenterType selectedSegmenterType = selectedSegmenter.segmenterType; /* * keep segmenter type which the user selected, try to reselect this segmenter when a new * tour is displayed */ if (isUserSelected) { _userSelectedSegmenterType = selectedSegmenterType; } if (selectedSegmenterType == SegmenterType.ByAltitudeWithDP || selectedSegmenterType == SegmenterType.ByAltitudeWithDPMerged || selectedSegmenterType == SegmenterType.ByAltitudeWithMarker) { _pageBookSegmenter.showPage(_pageSegType_DPAltitude); } else if (selectedSegmenterType == SegmenterType.ByPowerWithDP) { _pageBookSegmenter.showPage(_pageSegType_DPPower); } else if (selectedSegmenterType == SegmenterType.ByPulseWithDP) { _pageBookSegmenter.showPage(_pageSegType_DPPulse); } else if (selectedSegmenterType == SegmenterType.ByMarker) { _pageBookSegmenter.showPage(_pageSegType_ByMarker); } else if (selectedSegmenterType == SegmenterType.ByDistance) { _pageBookSegmenter.showPage(_pageSegType_ByDistance); } else if (selectedSegmenterType == SegmenterType.ByComputedAltiUpDown) { _pageBookSegmenter.showPage(_pageSegType_ByAltiUpDown); } else if (selectedSegmenterType == SegmenterType.ByBreakTime) { _pageBookSegmenter.showPage(_pageSegType_ByBreakTime); // update ui + layout onSelect_BreakTimeMethod(); } _pageSegmenter.layout(); createSegments(true); } private void onSelect_Tolerance() { final float dpToleranceAlti = (float) (_spinnerDPTolerance_Altitude.getSelection() / 10.0); final float dpTolerancePower = (float) (_spinnerDPTolerance_Power.getSelection() / 10.0); final float dpTolerancePulse = (float) (_spinnerDPTolerance_Pulse.getSelection() / 10.0); // check if tolerance has changed if (_tourData == null || // (_dpToleranceAltitude == dpToleranceAlti // && _dpTolerancePower == dpTolerancePower // && _dpTolerancePulse == dpTolerancePulse)) { return; } _dpToleranceAltitude = dpToleranceAlti; _dpTolerancePower = dpTolerancePower; _dpTolerancePulse = dpTolerancePulse; if (_tourData.isMultipleTours()) { _dpToleranceAltitudeMultipleTours = dpToleranceAlti; } setTourDirty(); createSegments(true); } /** * handle a tour selection event * * @param selection */ private void onSelectionChanged(final ISelection selection) { if (selection == null) { // this happens when view is created return; } _isClearView = false; if (_isSaving) { return; } // System.out.println((UI.timeStampNano() + " [" + getClass().getSimpleName() + "] ") // + ("\tonSelectionChanged:\t" + selection)); // // TODO remove SYSTEM.OUT.PRINTLN /* * run selection async because a tour could be modified and needs to be saved, modifications * are not reported to the tour data editor, saving needs also to be asynch with the tour * data editor */ Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { // check if view is disposed if (_pageBookUI.isDisposed() || _isClearView) { return; } if (_isGetInitialTours && _tourData != null) { // tours are already setup _isGetInitialTours = false; return; } TourData tourData = null; TourChart eventTourChart = null; if (selection instanceof SelectionTourData) { final SelectionTourData selectionTourData = (SelectionTourData) selection; tourData = selectionTourData.getTourData(); eventTourChart = selectionTourData.getTourChart(); } else if (selection instanceof SelectionTourId) { final SelectionTourId tourIdSelection = (SelectionTourId) selection; if (isTourDisplayed(tourIdSelection.getTourId())) { return; } tourData = TourManager.getInstance().getTourData(tourIdSelection.getTourId()); } else if (selection instanceof SelectionTourIds) { final ArrayList<Long> tourIds = ((SelectionTourIds) selection).getTourIds(); if (tourIds != null && tourIds.size() > 0) { if (tourIds.size() == 1) { final Long tourId = tourIds.get(0); if (isTourDisplayed(tourId)) { return; } tourData = TourManager.getInstance().getTourData(tourId); } else { tourData = TourManager.createMultipleTourData(tourIds); } } } else if (selection instanceof SelectionDeletedTours) { clearView(); } else { return; } if (checkDataValidation(tourData) == false) { return; } if (_tourData != null) { // it's possible that the break time serie was overwritten _tourData.setBreakTimeSerie(null); } /* * save previous tour when a new tour is selected */ if (_tourData != null && _tourData == tourData) { // nothing to do, it's the same tour } else { // disabled, tour is only saved with the save button since v14.7 // final TourData savedTour = saveTour(); // if (savedTour != null) { // // /* // * when a tour is saved, the change notification is not fired because // * another tour is already selected, but to update the tour in a TourViewer, // * a change nofification must be fired afterwords // */ //// Display.getCurrent().asyncExec(new Runnable() { //// public void run() { //// TourManager.fireEvent(TourEventId.TOUR_CHANGED, //// new TourEvent(savedTour), //// TourSegmenterView.this); //// } //// }); // } if (eventTourChart == null) { eventTourChart = TourManager.getActiveTourChart(tourData); } _tourChart = eventTourChart; setTour(tourData, false); } } }); } private void onSetDefaults(final Composite parent) { saveBreakTimeValuesInPrefStore(); PreferencesUtil.createPreferenceDialogOn(parent.getShell(), PrefPageComputedValues.ID, null, PrefPageComputedValues.TAB_FOLDER_BREAK_TIME).open(); } @Override public ColumnViewer recreateViewer(final ColumnViewer columnViewer) { _containerViewer.setRedraw(false); { _segmentViewer.getTable().dispose(); createUI_80_SegmentViewer(_containerViewer); _containerViewer.layout(); // update the viewer reloadViewer(); } _containerViewer.setRedraw(true); return _segmentViewer; } @Override public void reloadViewer() { // force content to be reloaded _segmentViewer.setInput(new Object[0]); } /** * defaults are the values which are stored in the pref store not the default-default which can * be set in the pref page */ private void restorePrefValues() { final BreakTimeTool btConfig = BreakTimeTool.getPrefValues(); /* * break method */ selectBreakMethod(btConfig.breakTimeMethodId); /* * break by avg + slice speed */ // _spinnerBreak_MinAvgSpeedAS.setSelection(// (int) (btConfig.breakMinAvgSpeedAS * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); _spinnerBreak_MinSliceSpeedAS.setSelection(// (int) (btConfig.breakMinSliceSpeedAS * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); _spinnerBreak_MinSliceTimeAS.setSelection(btConfig.breakMinSliceTimeAS); /* * break time by time/distance */ _spinnerBreak_ShortestTime.setSelection(btConfig.breakShortestTime); final float prefBreakDistance = btConfig.breakMaxDistance / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE_SMALL; _spinnerBreak_MaxDistance.setSelection((int) (prefBreakDistance + 0.5)); _spinnerBreak_SliceDiff.setSelection(btConfig.breakSliceDiff); /* * break time by speed */ _spinnerBreak_MinSliceSpeed.setSelection(// (int) (btConfig.breakMinSliceSpeed * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); _spinnerBreak_MinAvgSpeed.setSelection(// (int) (btConfig.breakMinAvgSpeed * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); onSelect_BreakTimeMethod(); /* * Altitude DP tolerance for multiple tours */ _dpToleranceAltitudeMultipleTours = (float) (STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS_DEFAULT / 10.0); if (_tourData != null && _tourData.isMultipleTours()) { _spinnerDPTolerance_Altitude.setSelection(STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS_DEFAULT); } } private void restoreState() { /* * Actions */ _actionTCSegmenterConfig.setSelected( Util.getStateBoolean(_state, STATE_IS_SHOW_TOUR_SEGMENTS, STATE_IS_SHOW_TOUR_SEGMENTS_DEFAULT)); // selected segmenter final String stateSegmenterName = Util.getStateString(_state, STATE_SELECTED_SEGMENTER_BY_USER, SegmenterType.ByAltitudeWithDP.name()); try { _userSelectedSegmenterType = SegmenterType.valueOf(SegmenterType.class, stateSegmenterName); } catch (final Exception e) { // set default value _userSelectedSegmenterType = SegmenterType.ByAltitudeWithDP; } // selected distance final int stateDistance = Util.getStateInt(_state, STATE_SELECTED_DISTANCE, 10); _spinnerDistance.setSelection(stateDistance); updateUI_Distance(); _spinnerMinAltitude.setSelection(Util.getStateInt(_state, STATE_MINIMUM_ALTITUDE, 50)); /* * DP tolerance */ final int stateDPTolerance_Power = Util.getStateInt(_state, STATE_DP_TOLERANCE_POWER, 200); _dpTolerancePower = stateDPTolerance_Power / 10.0f; _spinnerDPTolerance_Power.setSelection(stateDPTolerance_Power); final int stateDPTolerancePulse = Util.getStateInt(_state, STATE_DP_TOLERANCE_PULSE, 50); _dpTolerancePulse = stateDPTolerancePulse / 10.0f; _spinnerDPTolerance_Pulse.setSelection(stateDPTolerancePulse); final int stateDPToleranceMultipleTours = Util.getStateInt(_state, STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS, STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS_DEFAULT); _dpToleranceAltitudeMultipleTours = (float) (stateDPToleranceMultipleTours / 10.0); /* * break time */ final BreakTimeTool btConfig = BreakTimeTool.getPrefValues(); /* * break method */ selectBreakMethod(Util.getStateString(_state, STATE_SELECTED_BREAK_METHOD2, BreakTimeTool.BREAK_TIME_METHOD_BY_AVG_SLICE_SPEED)); /* * break by avg + slice speed */ final float stateAvgSpeedAS = Util.getStateFloat(_state, // STATE_BREAK_TIME_MIN_AVG_SPEED_AS, btConfig.breakMinAvgSpeedAS); final float stateSliceSpeedAS = Util.getStateFloat(_state, // STATE_BREAK_TIME_MIN_SLICE_SPEED_AS, btConfig.breakMinSliceSpeedAS); final int stateSliceTimeAS = Util.getStateInt(_state, // STATE_BREAK_TIME_MIN_SLICE_TIME_AS, btConfig.breakMinSliceTimeAS); _spinnerBreak_MinAvgSpeedAS.setSelection(// (int) (stateAvgSpeedAS * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); _spinnerBreak_MinSliceSpeedAS.setSelection(// (int) (stateSliceSpeedAS * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); _spinnerBreak_MinSliceTimeAS.setSelection(stateSliceTimeAS); /* * break by slice speed */ final float stateSliceSpeed = Util.getStateFloat(_state, STATE_BREAK_TIME_MIN_SLICE_SPEED, btConfig.breakMinSliceSpeed); _spinnerBreak_MinSliceSpeed .setSelection((int) (stateSliceSpeed * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); /* * break by avg speed */ final float stateAvgSpeed = Util.getStateFloat(_state, STATE_BREAK_TIME_MIN_AVG_SPEED, btConfig.breakMinAvgSpeed); _spinnerBreak_MinAvgSpeed .setSelection((int) (stateAvgSpeed * SPEED_DIGIT_VALUE * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE)); /* * break time by time/distance */ _spinnerBreak_ShortestTime.setSelection( Util.getStateInt(_state, STATE_BREAK_TIME_MIN_TIME_VALUE, btConfig.breakShortestTime)); final float breakDistance = Util.getStateFloat(_state, STATE_BREAK_TIME_MIN_DISTANCE_VALUE, btConfig.breakMaxDistance) / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE_SMALL; _spinnerBreak_MaxDistance.setSelection((int) (breakDistance + 0.5)); _spinnerBreak_SliceDiff .setSelection(Util.getStateInt(_state, STATE_BREAK_TIME_SLICE_DIFF, btConfig.breakSliceDiff)); /* * Setup colors */ _colorCache.setColor(STATE_COLOR_ALTITUDE_UP, Util.getStateRGB(_state, STATE_COLOR_ALTITUDE_UP, STATE_COLOR_ALTITUDE_UP_DEFAULT)); _colorCache.setColor(STATE_COLOR_ALTITUDE_DOWN, Util.getStateRGB(_state, STATE_COLOR_ALTITUDE_DOWN, STATE_COLOR_ALTITUDE_DOWN_DEFAULT)); _colorCache.setColor(STATE_COLOR_TOTALS, Util.getStateRGB(_state, STATE_COLOR_TOTALS, STATE_COLOR_TOTALS_DEFAULT)); } /** * saves break time values in the pref store */ private void saveBreakTimeValuesInPrefStore() { // break method _prefStore.setValue(ITourbookPreferences.BREAK_TIME_METHOD2, getSelectedBreakMethod().methodId); // break by avg+slice speed final float breakMinAvgSpeedAS = _spinnerBreak_MinAvgSpeedAS.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; final float breakMinSliceSpeedAS = _spinnerBreak_MinSliceSpeedAS.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; final int breakMinSliceTimeAS = _spinnerBreak_MinSliceTimeAS.getSelection(); _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MIN_AVG_SPEED_AS, breakMinAvgSpeedAS); _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MIN_SLICE_SPEED_AS, breakMinSliceSpeedAS); _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MIN_SLICE_TIME_AS, breakMinSliceTimeAS); // break by slice speed final float breakMinSliceSpeed = _spinnerBreak_MinSliceSpeed.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MIN_SLICE_SPEED, breakMinSliceSpeed); // break by avg speed final float breakMinAvgSpeed = _spinnerBreak_MinAvgSpeed.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MIN_AVG_SPEED, breakMinAvgSpeed); // break by time/distance _prefStore.setValue(ITourbookPreferences.BREAK_TIME_SHORTEST_TIME, _spinnerBreak_ShortestTime.getSelection()); final float breakDistance = _spinnerBreak_MaxDistance.getSelection() * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE_SMALL; _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MAX_DISTANCE, breakDistance); _prefStore.setValue(ITourbookPreferences.BREAK_TIME_SLICE_DIFF, _spinnerBreak_SliceDiff.getSelection()); } /** * save break time values in the viewer settings */ private void saveBreakTimeValuesInState() { // break method _state.put(STATE_SELECTED_BREAK_METHOD2, getSelectedBreakMethod().methodId); // break by avg+slice speed final float breakMinAvgSpeedAS = _spinnerBreak_MinAvgSpeedAS.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; final float breakMinSliceSpeedAS = _spinnerBreak_MinSliceSpeedAS.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; final int breakMinSliceTimeAS = _spinnerBreak_MinSliceTimeAS.getSelection(); _state.put(STATE_BREAK_TIME_MIN_AVG_SPEED_AS, breakMinAvgSpeedAS); _state.put(STATE_BREAK_TIME_MIN_SLICE_SPEED_AS, breakMinSliceSpeedAS); _state.put(STATE_BREAK_TIME_MIN_SLICE_TIME_AS, breakMinSliceTimeAS); // break by slice speed final float breakMinSliceSpeed = _spinnerBreak_MinSliceSpeed.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; _state.put(STATE_BREAK_TIME_MIN_SLICE_SPEED, breakMinSliceSpeed); // break by avg speed final float breakMinAvgSpeed = _spinnerBreak_MinAvgSpeed.getSelection() / SPEED_DIGIT_VALUE / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; _state.put(STATE_BREAK_TIME_MIN_AVG_SPEED, breakMinAvgSpeed); // break by time/distance final float breakDistance = _spinnerBreak_MaxDistance.getSelection() * net.tourbook.ui.UI.UNIT_VALUE_DISTANCE_SMALL; _state.put(STATE_BREAK_TIME_MIN_DISTANCE_VALUE, breakDistance); _state.put(STATE_BREAK_TIME_MIN_TIME_VALUE, _spinnerBreak_ShortestTime.getSelection()); _state.put(STATE_BREAK_TIME_SLICE_DIFF, _spinnerBreak_SliceDiff.getSelection()); } private void saveState() { _columnManager.saveState(_state); _state.put(STATE_SELECTED_SEGMENTER_BY_USER, _userSelectedSegmenterType.name()); _state.put(STATE_SELECTED_DISTANCE, _spinnerDistance.getSelection()); _state.put(STATE_MINIMUM_ALTITUDE, _spinnerMinAltitude.getSelection()); _state.put(STATE_DP_TOLERANCE_POWER, _spinnerDPTolerance_Power.getSelection()); _state.put(STATE_DP_TOLERANCE_PULSE, _spinnerDPTolerance_Pulse.getSelection()); _state.put(STATE_DP_TOLERANCE_ALTITUDE_MULTIPLE_TOURS, (int) (_dpToleranceAltitudeMultipleTours * 10)); saveBreakTimeValuesInState(); } private TourData saveTour() { if (_tourData != null) { // it's possible that the break time serie was overwritten _tourData.setBreakTimeSerie(null); } if (_isTourDirty == false || _tourData == null || _savedDpToleranceAltitude == -1) { // nothing to do return null; } TourData savedTour; _isSaving = true; { savedTour = TourManager.saveModifiedTour(_tourData); } _isSaving = false; _isTourDirty = false; return savedTour; } private void selectBreakMethod(final String methodId) { final BreakTimeMethod[] breakMethods = BreakTimeTool.BREAK_TIME_METHODS; int selectionIndex = -1; for (int methodIndex = 0; methodIndex < breakMethods.length; methodIndex++) { if (breakMethods[methodIndex].methodId.equals(methodId)) { selectionIndex = methodIndex; break; } } if (selectionIndex == -1) { selectionIndex = 0; } _comboBreakMethod.select(selectionIndex); } private void selectTourSegments(final SelectedTourSegmenterSegments selectedSegmenterConfig) { if (selectedSegmenterConfig.tourData != _tourData || _allTourSegments == null) { return; } /* * Find tour segments for the left and right slider */ final int selectedLeftIndex = selectedSegmenterConfig.xSliderSerieIndexLeft; final int selectedRightIndex = selectedSegmenterConfig.xSliderSerieIndexRight; TourSegment leftSegment = null; TourSegment rightSegment = null; final ArrayList<TourSegment> selectedSegments = new ArrayList<>(); for (final TourSegment tourSegment : _allTourSegments) { if (leftSegment == null && tourSegment.serieIndex_Start == selectedLeftIndex) { leftSegment = tourSegment; selectedSegments.add(leftSegment); } if (rightSegment == null && tourSegment.serieIndex_End == selectedRightIndex) { rightSegment = tourSegment; if (leftSegment != rightSegment) { selectedSegments.add(rightSegment); } } if (leftSegment != null && rightSegment == null) { // add all segments between the left and right selectedSegments.add(tourSegment); } if (leftSegment != null && rightSegment != null) { _segmentViewer.setSelection(new StructuredSelection(selectedSegments), true); return; } } } private void setCellColor(final ViewerCell cell, final double value) { if (value == 0) { cell.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); } else if (value > 0) { cell.setBackground(_colorCache.get(STATE_COLOR_ALTITUDE_UP)); } else if (value < 0) { cell.setBackground(_colorCache.get(STATE_COLOR_ALTITUDE_DOWN)); } } @Override public void setFocus() { } private void setMaxDistanceSpinner() { if (net.tourbook.ui.UI.UNIT_VALUE_DISTANCE == net.tourbook.ui.UI.UNIT_MILE) { // imperial _maxDistanceSpinner = MAX_DISTANCE_SPINNER_MILE; _spinnerDistancePage = 8; } else { // metric _maxDistanceSpinner = MAX_DISTANCE_SPINNER_METRIC; _spinnerDistancePage = 10; } } private void setTotalStyle(final ViewerCell cell) { // cell.setFont(_boldFont); cell.setBackground(_colorCache.get(STATE_COLOR_TOTALS)); } /** * Sets the tour for the segmenter * * @param tourData * @param forceUpdate */ private void setTour(final TourData tourData, final boolean forceUpdate) { if (tourData == null || (forceUpdate == false && tourData == _tourData)) { return; } _isDirtyDisabled = true; { _tourData = tourData; _pageBookUI.showPage(_pageSegmenter); enableActions(); // update tour title _lblTitle.setText(getTourTitle()); // keep original dp tolerance _savedDpToleranceAltitude = _dpToleranceAltitude = getDPTolerance_FromTour(); // segmenter value _spinnerDPTolerance_Altitude.setSelection((int) (getDPTolerance_FromTour() * 10)); final boolean canSaveTour = _tourData.getTourPerson() != null; _btnSaveTourDP.setEnabled(canSaveTour); _btnSaveTourMin.setEnabled(canSaveTour); } _isDirtyDisabled = false; updateUI_SegmenterSelector(); onSelect_SegmenterType(false); } /** * when dp tolerance was changed set the tour dirty */ private void setTourDirty() { if (_isDirtyDisabled) { return; } if (_tourData != null && _savedDpToleranceAltitude != getDPTolerance_FromTour()) { _isTourDirty = true; } } private void showTour() { // update viewer with current selection onSelectionChanged(getSite().getWorkbenchWindow().getSelectionService().getSelection()); // when previous onSelectionChanged did not display a tour, get tour from tour manager if (_tourData == null) { _isGetInitialTours = true; Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { onSelectionChanged(TourManager.getSelectedToursSelection()); } }); } } @Override public void updateColumnHeader(final ColumnDefinition colDef) { // TODO Auto-generated method stub } /** * update ascending altitude computed value */ private void updateUI_Altitude() { final TourSegmenter selectedSegmenter = getSelectedSegmenter(); if (selectedSegmenter == null) { clearView(); return; } Label lblInfo; float[] altitudeSegments = null; // compute total alti up/down from the segments _altitudeUp = 0; _altitudeDown = 0; if (selectedSegmenter.segmenterType == SegmenterType.ByComputedAltiUpDown) { // Minimum altitude altitudeSegments = _tourData.segmentSerie_Altitude_Diff_Computed; lblInfo = _lblAltitudeUpMin; } else { // DP tolerance if (selectedSegmenter.segmenterType == SegmenterType.ByAltitudeWithMarker) { _altitudeDown = _tourData.segmentSerieTotal_Altitude_Down; _altitudeUp = _tourData.segmentSerieTotal_Altitude_Up; } else { altitudeSegments = _tourData.segmentSerie_Altitude_Diff; } lblInfo = _lblAltitudeUpDP; } if (altitudeSegments == null && _altitudeDown == 0 && _altitudeUp == 0) { lblInfo.setText(UI.EMPTY_STRING); lblInfo.setToolTipText(UI.EMPTY_STRING); return; } if (altitudeSegments != null) { for (final float altitude : altitudeSegments) { if (altitude > 0) { _altitudeUp += altitude; } else { _altitudeDown += -altitude; } } } /* * Show altitude values not as negative values because the values are displayed left aligned * and it's easier to compare them visually when a minus sign is not displayed. */ final float compAltiUp = _altitudeUp / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; final float compAltiDown = _altitudeDown / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; final int tourAltiUp = Math.round(_tourData.getTourAltUp() / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE); final int tourAltiDown = Math.round(_tourData.getTourAltDown() / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE); lblInfo.setText(String.format(FORMAT_ALTITUDE_DIFF, Math.round(compAltiUp), tourAltiUp, net.tourbook.common.UI.UNIT_LABEL_ALTITUDE)); lblInfo.setToolTipText(NLS.bind(Messages.Tour_Segmenter_Label_AltitudeUpDown_Tooltip, new Object[] { // Up _nf_1_1.format(compAltiUp), tourAltiUp, net.tourbook.common.UI.UNIT_LABEL_ALTITUDE, // // Down _nf_1_1.format(compAltiDown), tourAltiDown, net.tourbook.common.UI.UNIT_LABEL_ALTITUDE, // // Diff _nf_1_1.format(compAltiUp - compAltiDown), tourAltiUp - tourAltiDown, net.tourbook.common.UI.UNIT_LABEL_ALTITUDE, // DP _nf_1_1.format(_dpToleranceAltitude), _nf_1_1.format(_tourData.getDpTolerance() / 10.0f), // })); } private void updateUI_BreakTime() { _lblTourBreakTime.setText(Long.toString(_tourBreakTime) + UI.SPACE + Messages.App_Unit_Seconds_Small + UI.SPACE4 + net.tourbook.common.UI.format_hh_mm_ss(_tourBreakTime)); _containerBreakTime.layout(); } private void updateUI_Distance() { float spinnerDistance = getDistance() / net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; if (net.tourbook.ui.UI.UNIT_VALUE_DISTANCE == net.tourbook.ui.UI.UNIT_MILE) { // imperial spinnerDistance /= 1000; final int distanceInt = (int) spinnerDistance; final float distanceFract = spinnerDistance - distanceInt; // create distance for imperials which shows the fraction with 1/8, 1/4, 3/8 ... final StringBuilder sb = new StringBuilder(); if (distanceInt > 0) { sb.append(Integer.toString(distanceInt)); sb.append(UI.SPACE); } if (Math.abs(distanceFract - 0.125f) <= 0.01) { sb.append(DISTANCE_MILES_1_8); sb.append(UI.SPACE); } else if (Math.abs(distanceFract - 0.25f) <= 0.01) { sb.append(DISTANCE_MILES_1_4); sb.append(UI.SPACE); } else if (Math.abs(distanceFract - 0.375) <= 0.01) { sb.append(DISTANCE_MILES_3_8); sb.append(UI.SPACE); } else if (Math.abs(distanceFract - 0.5f) <= 0.01) { sb.append(DISTANCE_MILES_1_2); sb.append(UI.SPACE); } else if (Math.abs(distanceFract - 0.625) <= 0.01) { sb.append(DISTANCE_MILES_5_8); sb.append(UI.SPACE); } else if (Math.abs(distanceFract - 0.75f) <= 0.01) { sb.append(DISTANCE_MILES_3_4); sb.append(UI.SPACE); } else if (Math.abs(distanceFract - 0.875) <= 0.01) { sb.append(DISTANCE_MILES_7_8); sb.append(UI.SPACE); } sb.append(net.tourbook.common.UI.UNIT_LABEL_DISTANCE); // update UI _lblDistanceValue.setText(sb.toString()); } else { // metric // update UI, the spinner already displays the correct value _lblDistanceValue.setText(net.tourbook.common.UI.UNIT_LABEL_DISTANCE); } } private void updateUI_SegmenterInfo(final Object[] tourSegments) { final String numSegments = Integer.toString(tourSegments.length - 1); _lblNumSegments.setText(numSegments); } private void updateUI_SegmenterSelector() { final TourSegmenter currentSegmenter = getSelectedSegmenter(); final int availableSegmenterData = checkSegmenterData(_tourData); // get all segmenters which can segment current tour _availableSegmenter.clear(); for (final TourSegmenter tourSegmenter : _allTourSegmenter) { final int requiredDataSeries = tourSegmenter.requiredDataSeries; if ((availableSegmenterData & requiredDataSeries) == requiredDataSeries) { _availableSegmenter.add(tourSegmenter); } } // sort by name // Collections.sort(_availableSegmenter); /* * fill list box */ int segmenterIndex = 0; int previousSegmenterIndex = -1; int userSelectedSegmenterIndex = -1; _comboSegmenterType.removeAll(); for (final TourSegmenter tourSegmenter : _availableSegmenter) { _comboSegmenterType.add(tourSegmenter.name); if (tourSegmenter.segmenterType == _userSelectedSegmenterType) { userSelectedSegmenterIndex = segmenterIndex; } if (tourSegmenter == currentSegmenter) { previousSegmenterIndex = segmenterIndex; } segmenterIndex++; } // reselect previous segmenter if (userSelectedSegmenterIndex != -1) { _comboSegmenterType.select(userSelectedSegmenterIndex); } else { if (previousSegmenterIndex != -1) { _comboSegmenterType.select(previousSegmenterIndex); } else { _comboSegmenterType.select(0); } } } }