Java tutorial
/******************************************************************************* * Copyright (C) 2005, 2017 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.tourChart; import gnu.trove.list.array.TIntArrayList; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import net.tourbook.Messages; import net.tourbook.application.TourbookPlugin; import net.tourbook.chart.Chart; import net.tourbook.chart.ChartComponentAxis; import net.tourbook.chart.ChartComponents; import net.tourbook.chart.ChartCursor; import net.tourbook.chart.ChartDataModel; import net.tourbook.chart.ChartDataSerie; import net.tourbook.chart.ChartDataYSerie; import net.tourbook.chart.ChartDrawingData; import net.tourbook.chart.ChartKeyEvent; import net.tourbook.chart.ChartMouseEvent; import net.tourbook.chart.ChartTitleSegment; import net.tourbook.chart.ChartTitleSegmentConfig; import net.tourbook.chart.ChartType; import net.tourbook.chart.ChartYDataMinMaxKeeper; import net.tourbook.chart.GraphDrawingData; import net.tourbook.chart.IChartLayer; import net.tourbook.chart.IFillPainter; import net.tourbook.chart.IHoveredValueListener; import net.tourbook.chart.IKeyListener; import net.tourbook.chart.ILineSelectionPainter; import net.tourbook.chart.IMouseListener; import net.tourbook.chart.ITooltipOwner; import net.tourbook.chart.MouseAdapter; import net.tourbook.chart.SelectionChartXSliderPosition; import net.tourbook.common.PointLong; import net.tourbook.common.UI; import net.tourbook.common.action.ActionOpenPrefDialog; import net.tourbook.common.color.GraphColorManager; import net.tourbook.common.tooltip.ActionToolbarSlideout; import net.tourbook.common.tooltip.IOpeningDialog; import net.tourbook.common.tooltip.OpenDialogManager; import net.tourbook.common.tooltip.ToolbarSlideout; import net.tourbook.common.util.IToolTipHideListener; import net.tourbook.common.util.TourToolTip; import net.tourbook.common.util.Util; import net.tourbook.data.TourData; import net.tourbook.data.TourMarker; import net.tourbook.data.TourSegment; import net.tourbook.photo.Photo; import net.tourbook.preferences.ITourbookPreferences; import net.tourbook.preferences.PrefPageAppearanceTourChart; import net.tourbook.tour.ActionOpenMarkerDialog; import net.tourbook.tour.IDataModelListener; import net.tourbook.tour.ITourMarkerModifyListener; import net.tourbook.tour.ITourModifyListener; import net.tourbook.tour.SelectionTourMarker; import net.tourbook.tour.TourEventId; import net.tourbook.tour.TourInfoIconToolTipProvider; import net.tourbook.tour.TourManager; import net.tourbook.tour.photo.TourPhotoLink; import net.tourbook.ui.ITourProvider; import net.tourbook.ui.action.ActionEditQuick; import net.tourbook.ui.tourChart.action.ActionCanAutoZoomToSlider; import net.tourbook.ui.tourChart.action.ActionCanMoveSlidersWhenZoomed; import net.tourbook.ui.tourChart.action.ActionGraph; import net.tourbook.ui.tourChart.action.ActionGraphOverlapped; import net.tourbook.ui.tourChart.action.ActionHrZoneDropDownMenu; import net.tourbook.ui.tourChart.action.ActionHrZoneGraphType; import net.tourbook.ui.tourChart.action.ActionTourChartInfo; import net.tourbook.ui.tourChart.action.ActionTourChartMarker; import net.tourbook.ui.tourChart.action.ActionTourPhotos; import net.tourbook.ui.tourChart.action.ActionXAxisDistance; import net.tourbook.ui.tourChart.action.ActionXAxisTime; import net.tourbook.ui.views.tourSegmenter.SelectedTourSegmenterSegments; import net.tourbook.ui.views.tourSegmenter.TourSegmenterView; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.ListenerList; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Path; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPart; /** * The tour chart extends the chart with all the functionality for a tour chart */ public class TourChart extends Chart implements ITourProvider, ITourMarkerUpdater, ILineSelectionPainter { private static final String ID = "net.tourbook.ui.tourChart"; //$NON-NLS-1$ // private static final int PAGE_NAVIGATION_SEGMENTS = 10; // private static final String GRAPH_LABEL_ALTIMETER = net.tourbook.common.Messages.Graph_Label_Altimeter; private static final String GRAPH_LABEL_ALTITUDE = net.tourbook.common.Messages.Graph_Label_Altitude; private static final String GRAPH_LABEL_CADENCE = net.tourbook.common.Messages.Graph_Label_Cadence; private static final String GRAPH_LABEL_GEARS = net.tourbook.common.Messages.Graph_Label_Gears; private static final String GRAPH_LABEL_GRADIENT = net.tourbook.common.Messages.Graph_Label_Gradient; private static final String GRAPH_LABEL_HEARTBEAT = net.tourbook.common.Messages.Graph_Label_Heartbeat; private static final String GRAPH_LABEL_PACE = net.tourbook.common.Messages.Graph_Label_Pace; private static final String GRAPH_LABEL_POWER = net.tourbook.common.Messages.Graph_Label_Power; private static final String GRAPH_LABEL_SPEED = net.tourbook.common.Messages.Graph_Label_Speed; private static final String GRAPH_LABEL_TEMPERATURE = net.tourbook.common.Messages.Graph_Label_Temperature; private static final String GRAPH_LABEL_TOUR_COMPARE = net.tourbook.common.Messages.Graph_Label_Tour_Compare; // public static final String ACTION_ID_CAN_AUTO_ZOOM_TO_SLIDER = "ACTION_ID_CAN_AUTO_ZOOM_TO_SLIDER"; //$NON-NLS-1$ public static final String ACTION_ID_CAN_MOVE_SLIDERS_WHEN_ZOOMED = "ACTION_ID_CAN_MOVE_SLIDERS_WHEN_ZOOMED"; //$NON-NLS-1$ public static final String ACTION_ID_EDIT_CHART_PREFERENCES = "ACTION_ID_EDIT_CHART_PREFERENCES"; //$NON-NLS-1$ private static final String ACTION_ID_IS_GRAPH_OVERLAPPED = "ACTION_ID_IS_GRAPH_OVERLAPPED"; //$NON-NLS-1$ public static final String ACTION_ID_IS_SHOW_TOUR_PHOTOS = "ACTION_ID_IS_SHOW_TOUR_PHOTOS"; //$NON-NLS-1$ public static final String ACTION_ID_HR_ZONE_DROPDOWN_MENU = "ACTION_ID_HR_ZONE_DROPDOWN_MENU"; //$NON-NLS-1$ public static final String ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP = "ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP"; //$NON-NLS-1$ public static final String ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT = "ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT"; //$NON-NLS-1$ public static final String ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM = "ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM"; //$NON-NLS-1$ public static final String ACTION_ID_HR_ZONE_STYLE_WHITE_TOP = "ACTION_ID_HR_ZONE_STYLE_WHITE_TOP"; //$NON-NLS-1$ public static final String ACTION_ID_X_AXIS_DISTANCE = "ACTION_ID_X_AXIS_DISTANCE"; //$NON-NLS-1$ public static final String ACTION_ID_X_AXIS_TIME = "ACTION_ID_X_AXIS_TIME"; //$NON-NLS-1$ // private static final String GRID_PREF_PREFIX = "GRID_TOUR_CHART__"; //$NON-NLS-1$ //SET_FORMATTING_OFF private static final String GRID_IS_SHOW_VERTICAL_GRIDLINES = (GRID_PREF_PREFIX + ITourbookPreferences.CHART_GRID_IS_SHOW_VERTICAL_GRIDLINES); private static final String GRID_IS_SHOW_HORIZONTAL_GRIDLINES = (GRID_PREF_PREFIX + ITourbookPreferences.CHART_GRID_IS_SHOW_HORIZONTAL_GRIDLINES); private static final String GRID_VERTICAL_DISTANCE = (GRID_PREF_PREFIX + ITourbookPreferences.CHART_GRID_VERTICAL_DISTANCE); private static final String GRID_HORIZONTAL_DISTANCE = (GRID_PREF_PREFIX + ITourbookPreferences.CHART_GRID_HORIZONTAL_DISTANCE); //SET_FORMATTING_ON /** * 1e-5 is too small for the min value, it do not correct the graph. */ public static final double MIN_ADJUSTMENT = 1e-3; public static final double MAX_ADJUSTMENT = 1e-5; // private final IPreferenceStore _prefStore = TourbookPlugin.getPrefStore(); private final IDialogSettings _state = TourbookPlugin.getState(ID); private final IDialogSettings _tourSegmenterState = TourSegmenterView.getState(); // /** * Part in which the tour chart is created, can be <code>null</code> when created in a dialog. */ private IWorkbenchPart _part;; // private TourData _tourData; private TourChartConfiguration _tcc; // private Map<String, Action> _allTourChartActions; private ActionEditQuick _actionEditQuick; private ActionGraphMinMax _actionGraphMinMax; private ActionOpenMarkerDialog _actionOpenMarkerDialog; private ActionTourChartOptions _actionTourChartOptions; private ActionTourChartSmoothing _actionTourChartSmoothing; private ActionTourChartInfo _actionTourInfo; private ActionTourChartMarker _actionTourMarker; // /** * datamodel listener is called when the chart data is created */ private IDataModelListener _chartDataModelListener; private IPropertyChangeListener _prefChangeListener; private final ListenerList _tourMarkerModifyListener = new ListenerList(); private final ListenerList _tourMarkerSelectionListener = new ListenerList(); private final ListenerList _tourModifyListener = new ListenerList(); private final ListenerList _xAxisSelectionListener = new ListenerList(); // private boolean _is2ndAltiLayerVisible; private boolean _isDisplayedInDialog; private boolean _isMouseModeSet; private boolean _isTourChartToolbarCreated; private TourMarker _firedTourMarker; /** * The {@link TourMarker} selection state is <b>only</b> be displayed when the mouse is hovering * it. */ private TourMarker _selectedTourMarker; //SET_FORMATTING_OFF private ImageDescriptor _imagePhoto = TourbookPlugin.getImageDescriptor(Messages.Image__PhotoPhotos); private ImageDescriptor _imagePhotoTooltip = TourbookPlugin.getImageDescriptor(Messages.Image__PhotoImage); //SET_FORMATTING_ON private IFillPainter _hrZonePainter; private OpenDialogManager _openDlgMgr = new OpenDialogManager(); private ChartPhotoToolTip _photoTooltip; private TourToolTip _tourInfoIconTooltip; private TourInfoIconToolTipProvider _tourInfoIconTooltipProvider; private ChartMarkerToolTip _tourMarkerTooltip; private TourSegmenterTooltip _tourSegmenterTooltip; private ChartTitleToolTip _tourTitleTooltip; private ValuePointToolTipUI _valuePointTooltip; // private ControlListener _ttControlListener = new ControlListener(); private IKeyListener _chartKeyListener = new ChartKeyListener(); private IMouseListener _mouseMarkerListener = new MouseMarkerListener(); private IMouseListener _mousePhotoListener = new MousePhotoListener(); private IMouseListener _mouseSegmentLabel_Listener = new MouseListener_SegmenterSegment(); private IMouseListener _mouseSegmentLabel_MoveListener = new MouseListener_SegmenterSegment_Move(); private IMouseListener _mouseSegmentTitle_Listener = new MouseListener_SegmentTitle(); private IMouseListener _mouseSegmentTitle_MoveListener = new MouseListener_SegmentTitle_Move(); private long _hoveredSegmentTitleEventTime; // private boolean _isSegmenterSegmentHovered; private long _hoveredSegmenterSegmentEventTime; private SegmenterSegment _hoveredSegmenterSegment; private SegmenterSegment _selectedSegmenterSegment_1; private SegmenterSegment _selectedSegmenterSegment_2; private boolean _isRecomputeLineSelection; private TIntArrayList _selectedAltitudePoints; private ArrayList<RGB> _selectedAltitudeRGB; private ArrayList<TIntArrayList> _selectedOtherPoints; private ArrayList<RGB> _selectedPathsRGB; // private boolean _isSegmentTitleHovered; private ChartTitleSegment _chartTitleSegment; private TourMarker _lastHoveredTourMarker; /** * Hide tour segments when tour chart is displayed in dialogs. */ private boolean _canShowTourSegments; private boolean _isTourSegmenterVisible; private boolean _isShowSegmenterTooltip; private SelectedTourSegmenterSegments _segmenterSelection; private Font _segmenterValueFont; private int _oldTourSegmentsHash; /* * UI controls */ private Composite _parent; // private I2ndAltiLayer _layer2ndAlti; private ChartLayerMarker _layerMarker; private ChartLayer2ndAltiSerie _layer2ndAltiSerie; private ChartLayerPhoto _layerPhoto; private ChartLayerSegmentAltitude _layerTourSegmenterAltitude; private ChartLayerSegmentValue _layerTourSegmenterOther; // private Color _photoOverlayBGColorLink; private Color _photoOverlayBGColorTour; private class ActionGraphMinMax extends ActionToolbarSlideout { public ActionGraphMinMax(final ImageDescriptor imageDescriptor, final ImageDescriptor imageDescriptorDisabled) { super(imageDescriptor, imageDescriptorDisabled); } @Override protected ToolbarSlideout createSlideout(final ToolBar toolbar) { return new SlideoutGraphMinMax(_parent, toolbar); } @Override protected void onBeforeOpenSlideout() { closeOpenedDialogs(this); } } private class ActionTourChartOptions extends ActionToolbarSlideout { @Override protected ToolbarSlideout createSlideout(final ToolBar toolbar) { return new SlideoutTourChartOptions(_parent, toolbar, TourChart.this, GRID_PREF_PREFIX); } @Override protected void onBeforeOpenSlideout() { closeOpenedDialogs(this); } } private class ActionTourChartSmoothing extends ActionToolbarSlideout { public ActionTourChartSmoothing(final ImageDescriptor imageDescriptor, final ImageDescriptor imageDescriptorDisabled) { super(imageDescriptor, imageDescriptorDisabled); } @Override protected ToolbarSlideout createSlideout(final ToolBar toolbar) { return new SlideoutTourChartSmoothing(_parent, toolbar, TourChart.this); } @Override protected void onBeforeOpenSlideout() { closeOpenedDialogs(this); } } private class ChartKeyListener implements IKeyListener { @Override public void keyDown(final ChartKeyEvent keyEvent) { onChart_KeyDown(keyEvent); } } /** * This listener is added to ALL widgets within the tooltip shell. */ private class ControlListener implements Listener { TourChart __tourChart = TourChart.this; @Override public void handleEvent(final Event event) { if (__tourChart.isDisposed()) { return; } if (event.widget instanceof Control) { switch (event.type) { case SWT.MouseEnter: break; case SWT.MouseExit: // check if photo tooltip is displayed final Shell photoTTShell = _photoTooltip.getShell(); if (photoTTShell == null || photoTTShell.isDisposed()) { return; } boolean isHide = false; // check what is hovered with the mouse after the MouseExit event is fired, can be null final Control hoveredControl = __tourChart.getDisplay().getCursorControl(); if (hoveredControl == null) { isHide = true; } else { /* * check if the hovered control is the owner control, if not, hide the * tooltip */ final ChartComponents chartComponents = getChartComponents(); final ChartComponentAxis axisLeft = chartComponents.getAxisLeft(); final ChartComponentAxis axisRight = chartComponents.getAxisRight(); Control parent = hoveredControl; while (true) { if (parent == photoTTShell) { // mouse is over the photo tooltip break; } if (parent == axisLeft || parent == axisRight) { // mouse is hovering the y axis break; } parent = parent.getParent(); if (parent == null) { // mouse has left the tourchart and the photo tooltip isHide = true; break; } } } if (isHide) { _photoTooltip.hide(); } break; } } } } private class HoveredValueListener implements IHoveredValueListener { @Override public void hideTooltip() { if (_photoTooltip != null) { _photoTooltip.hide(); } } @Override public void hoveredValue(final long eventTime, final int devXMouseMove, final int devYMouseMove, final int hoveredValueIndex, final PointLong devHoveredValue) { if (_tourData == null) { return; } if (_tcc.isShowTourPhotos == false) { return; } // check if photos are available if (_tourData.tourPhotoLink == null && _tourData.getTourPhotos().size() == 0) { return; } if (_layerPhoto == null) { return; } if (_tcc.isShowTourPhotoTooltip) { _photoTooltip.showChartPhotoToolTip(// _layerPhoto, eventTime, devHoveredValue, devXMouseMove, devYMouseMove); } } } private class MouseListener_SegmenterSegment extends MouseAdapter { @Override public void chartResized() { onSegmenterSegment_Resize(); } @Override public void mouseDown(final ChartMouseEvent event) { onSegmenterSegment_MouseDown(event); } @Override public void mouseExit() { onSegmenterSegment_MouseExit(); } @Override public void mouseUp(final ChartMouseEvent event) { onSegmenterSegment_MouseUp(event); } } /** * This mouse move listener is used to get mouse move events to show the tour tooltip when the * y-slider is dragged. */ private class MouseListener_SegmenterSegment_Move extends MouseAdapter { @Override public void mouseMove(final ChartMouseEvent event) { onSegmenterSegment_MouseMove(event); } } private class MouseListener_SegmentTitle extends MouseAdapter { @Override public void chartResized() { onSegmentTitle_Resized(); } @Override public void mouseDoubleClick(final ChartMouseEvent event) { onSegmentTitle_MouseDoubleClick(event); } @Override public void mouseDown(final ChartMouseEvent event) { onSegmentTitle_MouseDown(event); } @Override public void mouseExit() { onSegmentTitle_MouseExit(); } } /** * This mouse move listener is used to get mouse move events to show the tour tooltip when the * y-slider is dragged. */ private class MouseListener_SegmentTitle_Move extends MouseAdapter { @Override public void mouseMove(final ChartMouseEvent event) { onSegmentTitle_MouseMove(event); } } private class MouseMarkerListener extends MouseAdapter { @Override public void chartResized() { onMarker_ChartResized(); } @Override public void mouseDoubleClick(final ChartMouseEvent event) { onMarker_MouseDoubleClick(event); } @Override public void mouseDown(final ChartMouseEvent event) { onMarker_MouseDown(event); } @Override public void mouseExit() { onMarker_MouseExit(); } @Override public void mouseMove(final ChartMouseEvent event) { onMarker_MouseMove(event); } @Override public void mouseUp(final ChartMouseEvent event) { onMarker_MouseUp(event); } } private class MousePhotoListener extends MouseAdapter { @Override public void chartResized() { onPhoto_ChartResized(); } @Override public void mouseExit() { onPhoto_MouseExit(); } @Override public void mouseMove(final ChartMouseEvent event) { onPhoto_MouseMove(event); } } /** * @param parent * @param style * @param part * Part in which the tour chart is created, can be <code>null</code> when created in * a dialog. */ public TourChart(final Composite parent, final int style, final IWorkbenchPart part) { super(parent, style); _parent = parent; _part = part; // /* // * when the focus is changed, fire a tour chart selection, this is neccesarry to update the // * tour markers when a tour chart got the focus // */ // addFocusListener(new Listener() { // public void handleEvent(final Event event) { //// fireTourChartSelection(); // } // }); addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(final DisposeEvent e) { onDispose(); } }); addControlListener(this); addPrefListeners(); final GraphColorManager colorProvider = GraphColorManager.getInstance(); _photoOverlayBGColorLink = new Color(getDisplay(), // colorProvider.getGraphColorDefinition(GraphColorManager.PREF_GRAPH_HISTORY).getLineColor_Active()); _photoOverlayBGColorTour = new Color(getDisplay(), // colorProvider.getGraphColorDefinition(GraphColorManager.PREF_GRAPH_TOUR).getLineColor_Active()); setupChartConfig(); setShowMouseMode(); /* * setup tour info icon into the left axis */ _tourInfoIconTooltipProvider = new TourInfoIconToolTipProvider(); _tourInfoIconTooltip = new TourToolTip(getToolTipControl()); _tourInfoIconTooltip.addToolTipProvider(_tourInfoIconTooltipProvider); _tourInfoIconTooltip.addHideListener(new IToolTipHideListener() { @Override public void afterHideToolTip(final Event event) { // hide hovered image getToolTipControl().afterHideToolTip(event); } }); setTourInfoIconToolTipProvider(_tourInfoIconTooltipProvider); // Setup tooltips _tourTitleTooltip = new ChartTitleToolTip(this); _tourSegmenterTooltip = new TourSegmenterTooltip(this); /* * setup value point tooltip */ final ITooltipOwner vpToolTipOwner = new ITooltipOwner() { @Override public Control getControl() { return getValuePointControl(); } @Override public void handleMouseEvent(final Event event, final Point mouseDisplayPosition) { handleTooltipMouseEvent(event, mouseDisplayPosition); } }; _valuePointTooltip = new ValuePointToolTipUI(vpToolTipOwner, _state); setValuePointToolTipProvider(_valuePointTooltip); _photoTooltip = new ChartPhotoToolTip(this, _state); _tourMarkerTooltip = new ChartMarkerToolTip(this); // show delayed that it is not flickering when moving the mouse fast _tourMarkerTooltip.setFadeInDelayTime(50); setHoveredListener(new HoveredValueListener()); setLineSelectionPainter(this); } public void actionCanAutoMoveSliders(final boolean isItemChecked) { setCanAutoMoveSliders(isItemChecked); // apply setting to the chart if (isItemChecked) { onExecuteMoveSlidersToBorder(); // onExecuteZoomInWithSlider(); } updateZoomOptionActionHandlers(); } public void actionCanAutoZoomToSlider(final Boolean isItemChecked) { setCanAutoZoomToSlider(isItemChecked); // apply setting to the chart // if (isItemChecked) { // zoomInWithSlider(); // } else { // zoomOut(true); // } updateZoomOptionActionHandlers(); } public void actionCanScrollChart(final Boolean isItemChecked) { // apply setting to the chart if (isItemChecked) { onExecuteZoomInWithSlider(); } else { onExecuteZoomOut(true, 1.0); } updateZoomOptionActionHandlers(); } public void actionGraphOverlapped(final boolean isItemChecked) { _prefStore.setValue(ITourbookPreferences.GRAPH_IS_GRAPH_OVERLAPPED, isItemChecked); _tcc.isGraphOverlapped = isItemChecked; updateTourChart(); setActionChecked(ACTION_ID_IS_GRAPH_OVERLAPPED, isItemChecked); } /** * Toggle HR zone background */ public void actionShowHrZones() { final boolean isHrZonevisible = !_tcc.isHrZoneDisplayed; _prefStore.setValue(ITourbookPreferences.GRAPH_IS_HR_ZONE_BACKGROUND_VISIBLE, isHrZonevisible); _tcc.isHrZoneDisplayed = isHrZonevisible; updateTourChart(); } /** * @param isActionChecked * @param selectedGraphType */ public void actionShowHrZoneStyle(final Boolean isActionChecked, final String selectedGraphType) { final String previousGraphType = _tcc.hrZoneStyle; // check if the same action was selected if (isActionChecked && selectedGraphType.equals(previousGraphType)) { // return; } _prefStore.setValue(ITourbookPreferences.GRAPH_HR_ZONE_STYLE, selectedGraphType); _tcc.hrZoneStyle = selectedGraphType; setActionChecked(ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP, // ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP.equals(selectedGraphType)); setActionChecked(ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT, // ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT.equals(selectedGraphType)); setActionChecked(ACTION_ID_HR_ZONE_STYLE_WHITE_TOP, // ACTION_ID_HR_ZONE_STYLE_WHITE_TOP.equals(selectedGraphType)); setActionChecked(ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM, // ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM.equals(selectedGraphType)); if (_tcc.isHrZoneDisplayed == false) { // HR zones are not yet displayed actionShowHrZones(); } else { updateTourChart(); } } public void actionShowTourInfo(final boolean isTourInfoVisible) { _prefStore.setValue(ITourbookPreferences.GRAPH_TOUR_INFO_IS_VISIBLE, isTourInfoVisible); _tcc.isTourInfoVisible = isTourInfoVisible; updateUI_TourTitleInfo(); } public void actionShowTourMarker(final Boolean isMarkerVisible) { _prefStore.setValue(ITourbookPreferences.GRAPH_IS_MARKER_VISIBLE, isMarkerVisible); updateUI_Marker(isMarkerVisible); } public void actionShowTourPhotos() { boolean isShowPhotos = _tcc.isShowTourPhotos; boolean isShowTooltip = _tcc.isShowTourPhotoTooltip; if (isShowPhotos && isShowTooltip) { isShowPhotos = true; isShowTooltip = false; } else if (isShowPhotos) { isShowPhotos = false; isShowTooltip = false; } else { isShowPhotos = true; isShowTooltip = true; } _tcc.isShowTourPhotos = isShowPhotos; _tcc.isShowTourPhotoTooltip = isShowTooltip; _prefStore.setValue(ITourbookPreferences.GRAPH_IS_TOUR_PHOTO_VISIBLE, isShowPhotos); _prefStore.setValue(ITourbookPreferences.GRAPH_IS_TOUR_PHOTO_TOOLTIP_VISIBLE, isShowTooltip); updatePhotoAction(); updateTourChart(); } /** * Set the X-axis to distance * * @param isChecked */ public void actionXAxisDistance(final boolean isChecked) { // check if the distance axis button was pressed if (isChecked && !_tcc.isShowTimeOnXAxis) { return; } if (isChecked) { // show distance on x axis _tcc.isShowTimeOnXAxis = !_tcc.isShowTimeOnXAxis; _tcc.isShowTimeOnXAxisBackup = _tcc.isShowTimeOnXAxis; switchSlidersTo2ndXData(); updateTourChart(false); } // toggle time and distance buttons setActionChecked(ACTION_ID_X_AXIS_TIME, !isChecked); setActionChecked(ACTION_ID_X_AXIS_DISTANCE, isChecked); } /** * @param isChecked */ public void actionXAxisTime(final boolean isChecked) { // check if the time axis button was already pressed if (isChecked && _tcc.isShowTimeOnXAxis) { // x-axis already shows time, toggle between tour start time and tour time final X_AXIS_START_TIME configXAxisTime = _tcc.xAxisTime; if (_tourData.getPhotoTimeAdjustment() > 0) { if (configXAxisTime == X_AXIS_START_TIME.START_WITH_0) { _tcc.xAxisTime = X_AXIS_START_TIME.TOUR_START_TIME; } else if (configXAxisTime == X_AXIS_START_TIME.TOUR_START_TIME) { _tcc.xAxisTime = X_AXIS_START_TIME.PHOTO_TIME; } else { _tcc.xAxisTime = X_AXIS_START_TIME.START_WITH_0; } } else { /* * there is no photo time adjustment, toggle between relative and absolute time */ _tcc.xAxisTime = configXAxisTime == X_AXIS_START_TIME.START_WITH_0 ? X_AXIS_START_TIME.TOUR_START_TIME : X_AXIS_START_TIME.START_WITH_0; } /** * keepMinMaxValues must be set to false, that a deeply zoomed in chart can display * x-axis units */ updateTourChart(false); return; } if (isChecked) { // show time on x axis _tcc.isShowTimeOnXAxis = !_tcc.isShowTimeOnXAxis; _tcc.isShowTimeOnXAxisBackup = _tcc.isShowTimeOnXAxis; switchSlidersTo2ndXData(); updateTourChart(false); } // toggle time and distance buttons setActionChecked(ACTION_ID_X_AXIS_TIME, isChecked); setActionChecked(ACTION_ID_X_AXIS_DISTANCE, !isChecked); fireXAxisSelection(_tcc.isShowTimeOnXAxis); } /** * ########################### Recursive #########################################<br> * <p> * Add listener to all controls within the tour chart * <p> * ########################### Recursive #########################################<br> * * @param control */ private void addControlListener(final Control control) { control.addListener(SWT.MouseExit, _ttControlListener); control.addListener(SWT.MouseEnter, _ttControlListener); if (control instanceof Composite) { final Control[] children = ((Composite) control).getChildren(); for (final Control child : children) { addControlListener(child); } } } /** * add a data model listener which is fired when the data model has changed * * @param dataModelListener */ public void addDataModelListener(final IDataModelListener dataModelListener) { _chartDataModelListener = dataModelListener; } private void addPrefListeners() { _prefChangeListener = new IPropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent event) { if (_tcc == null) { return; } final String property = event.getProperty(); boolean isChartModified = false; boolean keepMinMax = true; if (property.equals(ITourbookPreferences.GRAPH_MOVE_SLIDERS_WHEN_ZOOMED) || property.equals(ITourbookPreferences.GRAPH_ZOOM_AUTO_ZOOM_TO_SLIDER)) { // zoom preferences has changed _tcc.updateZoomOptions(); isChartModified = true; } else if (property.equals(ITourbookPreferences.GRAPH_COLORS_HAS_CHANGED)) { /* * when the chart is computed, the modified colors are read from the preferences */ isChartModified = true; // dispose old colors disposeColors(); } else if (property.equals(ITourbookPreferences.GRAPH_ANTIALIASING) || property.equals(ITourbookPreferences.GRAPH_IS_SEGMENT_ALTERNATE_COLOR) || property.equals(ITourbookPreferences.GRAPH_SEGMENT_ALTERNATE_COLOR) || property.equals(ITourbookPreferences.GRAPH_TRANSPARENCY_LINE) || property.equals(ITourbookPreferences.GRAPH_TRANSPARENCY_FILLING) || property.equals(GRID_IS_SHOW_HORIZONTAL_GRIDLINES) || property.equals(GRID_IS_SHOW_VERTICAL_GRIDLINES) || property.equals(GRID_HORIZONTAL_DISTANCE) || property.equals(GRID_VERTICAL_DISTANCE) // ) { setupChartConfig(); isChartModified = true; } else if (property.equals(ITourbookPreferences.TOUR_SEGMENTER_CHART_VALUE_FONT)) { setupSegmenterValueFont(); } else if (property.equals(ITourbookPreferences.MEASUREMENT_SYSTEM)) { // measurement system has changed isChartModified = true; keepMinMax = false; _valuePointTooltip.reopen(); } final boolean isMinMaxEnabled = _prefStore .getBoolean(ITourbookPreferences.GRAPH_IS_MIN_MAX_ENABLED); /* * Altitude */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_ALTITUDE_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_ALTITUDE_MIN_VALUE, TourManager.GRAPH_ALTITUDE, 0, MIN_ADJUSTMENT, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_ALTITUDE_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_ALTITUDE_MAX_VALUE, TourManager.GRAPH_ALTITUDE, 0, 1e-2, isMinMaxEnabled); /* * Altimeter */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_ALTIMETER_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_ALTIMETER_MIN_VALUE, TourManager.GRAPH_ALTIMETER, 0, MIN_ADJUSTMENT, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_ALTIMETER_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_ALTIMETER_MAX_VALUE, TourManager.GRAPH_ALTIMETER, 0, 1e-2, isMinMaxEnabled); /* * Gradient */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_GRADIENT_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_GRADIENT_MIN_VALUE, TourManager.GRAPH_GRADIENT, 0, MIN_ADJUSTMENT, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_GRADIENT_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_GRADIENT_MAX_VALUE, TourManager.GRAPH_GRADIENT, 0, MAX_ADJUSTMENT, isMinMaxEnabled); /* * Pulse */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_PULSE_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_PULSE_MIN_VALUE, TourManager.GRAPH_PULSE, 0, MIN_ADJUSTMENT, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_PULSE_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_PULSE_MAX_VALUE, TourManager.GRAPH_PULSE, 0, 1e-3, isMinMaxEnabled); /* * Speed */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_SPEED_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_SPEED_MIN_VALUE, TourManager.GRAPH_SPEED, 0, Double.MIN_VALUE, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_SPEED_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_SPEED_MAX_VALUE, TourManager.GRAPH_SPEED, 0, Double.MIN_VALUE, isMinMaxEnabled); /* * Pace */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_PACE_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_PACE_MIN_VALUE, TourManager.GRAPH_PACE, 60, Double.MIN_VALUE, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_PACE_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_PACE_MAX_VALUE, TourManager.GRAPH_PACE, 60, Double.MIN_VALUE, isMinMaxEnabled); /* * Cadence */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_CADENCE_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_CADENCE_MIN_VALUE, TourManager.GRAPH_CADENCE, 0, MIN_ADJUSTMENT, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_CADENCE_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_CADENCE_MAX_VALUE, TourManager.GRAPH_CADENCE, 0, 1e-3, isMinMaxEnabled); /* * Power */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_POWER_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_POWER_MIN_VALUE, TourManager.GRAPH_POWER, 0, MIN_ADJUSTMENT, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_POWER_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_POWER_MAX_VALUE, TourManager.GRAPH_POWER, 0, 1e-3, isMinMaxEnabled); /* * Temperature */ isChartModified |= setMinDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_TEMPERATURE_IS_MIN_ENABLED, ITourbookPreferences.GRAPH_TEMPERATURE_MIN_VALUE, TourManager.GRAPH_TEMPERATURE, 0, MIN_ADJUSTMENT, isMinMaxEnabled); isChartModified |= setMaxDefaultValue(property, isChartModified, ITourbookPreferences.GRAPH_TEMPERATURE_IS_MAX_ENABLED, ITourbookPreferences.GRAPH_TEMPERATURE_MAX_VALUE, TourManager.GRAPH_TEMPERATURE, 0, 1e-3, isMinMaxEnabled); if (isChartModified) { updateTourChart(keepMinMax); } } }; _prefStore.addPropertyChangeListener(_prefChangeListener); } public void addTourMarkerModifyListener(final ITourMarkerModifyListener listener) { _tourMarkerModifyListener.add(listener); } public void addTourMarkerSelectionListener(final ITourMarkerSelectionListener listener) { _tourMarkerSelectionListener.add(listener); } public void addTourModifyListener(final ITourModifyListener listener) { _tourModifyListener.add(listener); } public void addXAxisSelectionListener(final IXAxisSelectionListener listener) { _xAxisSelectionListener.add(listener); } /** * Close all opened dialogs except the opening dialog. * * @param openingDialog */ public void closeOpenedDialogs(final IOpeningDialog openingDialog) { _openDlgMgr.closeOpenedDialogs(openingDialog); } /** * Create all tour chart actions. */ private void createActions() { // create actions only once if (_allTourChartActions != null) { return; } _allTourChartActions = new HashMap<String, Action>(); /* * graph actions */ createActions_10_GraphActions(); /* * other actions */ _actionOpenMarkerDialog = new ActionOpenMarkerDialog(this, true); _actionTourChartOptions = new ActionTourChartOptions(); _actionTourChartSmoothing = new ActionTourChartSmoothing( TourbookPlugin.getImageDescriptor(Messages.Image__Smoothing), TourbookPlugin.getImageDescriptor(Messages.Image__Smoothing_Disabled)); _actionGraphMinMax = new ActionGraphMinMax(TourbookPlugin.getImageDescriptor(Messages.Image__GraphMinMax), TourbookPlugin.getImageDescriptor(Messages.Image__GraphMinMax_Disabled)); _actionTourInfo = new ActionTourChartInfo(this, _parent); _actionTourMarker = new ActionTourChartMarker(this, _parent); _allTourChartActions.put(ACTION_ID_CAN_AUTO_ZOOM_TO_SLIDER, new ActionCanAutoZoomToSlider(this)); _allTourChartActions.put(ACTION_ID_CAN_MOVE_SLIDERS_WHEN_ZOOMED, new ActionCanMoveSlidersWhenZoomed(this)); _allTourChartActions.put(ACTION_ID_IS_SHOW_TOUR_PHOTOS, new ActionTourPhotos(this)); _allTourChartActions.put(ACTION_ID_IS_GRAPH_OVERLAPPED, new ActionGraphOverlapped(this)); _allTourChartActions.put(ACTION_ID_X_AXIS_DISTANCE, new ActionXAxisDistance(this)); _allTourChartActions.put(ACTION_ID_X_AXIS_TIME, new ActionXAxisTime(this)); _allTourChartActions.put(ACTION_ID_EDIT_CHART_PREFERENCES, new ActionOpenPrefDialog(// Messages.Tour_Action_EditChartPreferences, PrefPageAppearanceTourChart.ID)); /* * hr zone actions */ _allTourChartActions.put(ACTION_ID_HR_ZONE_DROPDOWN_MENU, new ActionHrZoneDropDownMenu(this)); _allTourChartActions.put(ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP, new ActionHrZoneGraphType(this, ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP, Messages.Tour_Action_HrZoneGraphType_Default)); _allTourChartActions.put(ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT, new ActionHrZoneGraphType(this, ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT, Messages.Tour_Action_HrZoneGraphType_NoGradient)); _allTourChartActions.put(ACTION_ID_HR_ZONE_STYLE_WHITE_TOP, new ActionHrZoneGraphType(this, ACTION_ID_HR_ZONE_STYLE_WHITE_TOP, Messages.Tour_Action_HrZoneGraphType_WhiteTop)); _allTourChartActions.put(ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM, new ActionHrZoneGraphType(this, ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM, Messages.Tour_Action_HrZoneGraphType_WhiteBottom)); /* * This is a special case because the quick edit action in the tour info tooltip can not yet * been initialized and creates a NPE when this action is run, therfore a hidden action is * used. */ _actionEditQuick = new ActionEditQuick(this); } /** * Create all graph actions */ private void createActions_10_GraphActions() { createActions_12_GraphAction(TourManager.GRAPH_ALTITUDE, GRAPH_LABEL_ALTITUDE, Messages.Tour_Action_graph_altitude_tooltip, Messages.Image__graph_altitude, Messages.Image__graph_altitude_disabled); createActions_12_GraphAction(TourManager.GRAPH_SPEED, GRAPH_LABEL_SPEED, Messages.Tour_Action_graph_speed_tooltip, Messages.Image__graph_speed, Messages.Image__graph_speed_disabled); createActions_12_GraphAction(TourManager.GRAPH_PACE, GRAPH_LABEL_PACE, Messages.Tour_Action_graph_pace_tooltip, Messages.Image__graph_pace, Messages.Image__graph_pace_disabled); createActions_12_GraphAction(TourManager.GRAPH_POWER, GRAPH_LABEL_POWER, Messages.Tour_Action_graph_power_tooltip, Messages.Image__graph_power, Messages.Image__graph_power_disabled); createActions_12_GraphAction(TourManager.GRAPH_ALTIMETER, GRAPH_LABEL_ALTIMETER, Messages.Tour_Action_graph_altimeter_tooltip, Messages.Image__graph_altimeter, Messages.Image__graph_altimeter_disabled); createActions_12_GraphAction(TourManager.GRAPH_PULSE, GRAPH_LABEL_HEARTBEAT, Messages.Tour_Action_graph_heartbeat_tooltip, Messages.Image__graph_heartbeat, Messages.Image__graph_heartbeat_disabled); createActions_12_GraphAction(TourManager.GRAPH_TEMPERATURE, GRAPH_LABEL_TEMPERATURE, Messages.Tour_Action_graph_temperature_tooltip, Messages.Image__graph_temperature, Messages.Image__graph_temperature_disabled); createActions_12_GraphAction(TourManager.GRAPH_CADENCE, GRAPH_LABEL_CADENCE, Messages.Tour_Action_graph_cadence_tooltip, Messages.Image__graph_cadence, Messages.Image__graph_cadence_disabled); createActions_12_GraphAction(TourManager.GRAPH_GEARS, GRAPH_LABEL_GEARS, Messages.Tour_Action_GraphGears, Messages.Image__Graph_Gears, Messages.Image__Graph_Gears_disabled); createActions_12_GraphAction(TourManager.GRAPH_GRADIENT, GRAPH_LABEL_GRADIENT, Messages.Tour_Action_graph_gradient_tooltip, Messages.Image__graph_gradient, Messages.Image__graph_gradient_disabled); createActions_12_GraphAction(TourManager.GRAPH_TOUR_COMPARE, GRAPH_LABEL_TOUR_COMPARE, Messages.Tour_Action_graph_tour_compare_tooltip, Messages.Image__graph_tour_compare, Messages.Image__graph_tour_compare_disabled); } /** * Create a graph action * * @param graphId * @param label * @param toolTip * @param imageEnabled * @param imageDisabled */ private void createActions_12_GraphAction(final int graphId, final String label, final String toolTip, final String imageEnabled, final String imageDisabled) { final ActionGraph graphAction = new ActionGraph(this, graphId, label, toolTip, imageEnabled, imageDisabled); _allTourChartActions.put(getGraphActionId(graphId), graphAction); } /** * Creates the handlers for the tour chart actions * * @param workbenchWindow * @param tourChartConfig */ public void createActions_TourEditor(final TourChartConfiguration tourChartConfig) { _tcc = tourChartConfig; createActions(); createChartActions(); } /** * Create chart photos, these are photos which contain the time slice position. * * @param srcPhotos * @param chartPhotos * @param isPhotoSavedInTour */ private void createChartPhotos(final ArrayList<Photo> srcPhotos, final ArrayList<ChartPhoto> chartPhotos, final boolean isPhotoSavedInTour) { final int[] timeSerie = _tourData.timeSerie; final long[] historySerie = _tourData.timeSerieHistory; final boolean isTimeSerie = timeSerie != null; final boolean isMultipleTours = _tourData.isMultipleTours(); final int[] multipleStartTimeIndex = _tourData.multipleTourStartIndex; final long[] multipleStartTime = _tourData.multipleTourStartTime; final long tourStart = _tourData.getTourStartTimeMS() / 1000; final int numberOfTimeSlices = isTimeSerie ? timeSerie.length : historySerie.length; /* * set photos for tours which has max 1 value point */ if (numberOfTimeSlices <= 1) { for (final Photo photo : srcPhotos) { chartPhotos.add(new ChartPhoto(photo, 0, 0)); } return; } /* * set photos for tours which has more than 1 value point */ final int numberOfPhotos = srcPhotos.size(); // set value serie for the x-axis, it can be time or distance double[] xAxisSerie = null; xAxisSerie = _tcc.isShowTimeOnXAxis // ? _tourData.getTimeSerieWithTimeZoneAdjusted() : _tourData.getDistanceSerieDouble(); long timeSliceEnd; if (isTimeSerie) { timeSliceEnd = tourStart + (long) (timeSerie[1] / 2.0); } else { // is history timeSliceEnd = tourStart + (long) (historySerie[1] / 2.0); } int photoIndex = 0; Photo photo = srcPhotos.get(photoIndex); // tour time serie, fit photos into a tour int serieIndex = 0; int nextTourIndex = 1; int nextTourSerieIndex = 0; long firstTourStartTime = 0; long tourStartTime = 0; long tourRecordingTime = 0; // setup first multiple tour if (isMultipleTours) { firstTourStartTime = multipleStartTime[0]; // this is the first tour tourStartTime = firstTourStartTime; nextTourSerieIndex = multipleStartTimeIndex[nextTourIndex]; } // loop serieIndex while (true) { // check if a photo is in the current time slice while (true) { final long imageAdjustedTime = isPhotoSavedInTour // ? photo.adjustedTimeTour : photo.adjustedTimeLink; long imageTime = 0; if (imageAdjustedTime != Long.MIN_VALUE) { imageTime = imageAdjustedTime; } else { imageTime = photo.imageExifTime; } if (isMultipleTours) { // adjust image time because multiple tours do not have a gap between tours // it took me several hours to find this algorithm, but now it works :-) if (serieIndex >= nextTourSerieIndex) { // setup next tour final int tourDuration = timeSerie[nextTourSerieIndex - 1]; tourRecordingTime = tourDuration; tourStartTime = multipleStartTime[nextTourIndex]; if (nextTourIndex < multipleStartTimeIndex.length - 1) { nextTourIndex++; nextTourSerieIndex = multipleStartTimeIndex[nextTourIndex]; } } final long tourTimeOffset = tourStartTime - firstTourStartTime; final long xAxisOffset = tourRecordingTime * 1000; final long timeOffset = tourTimeOffset - xAxisOffset; imageTime -= timeOffset; } final long photoTime = imageTime / 1000; if (photoTime <= timeSliceEnd) { // photo is available in the current time slice final double xValue = xAxisSerie[serieIndex]; chartPhotos.add(new ChartPhoto(photo, xValue, serieIndex)); photoIndex++; } else { // advance to the next time slice break; } if (photoIndex < numberOfPhotos) { photo = srcPhotos.get(photoIndex); } else { break; } } if (photoIndex >= numberOfPhotos) { // no more photos break; } /* * photos are still available */ // advance to the next time slice on the x-axis serieIndex++; if (serieIndex >= numberOfTimeSlices - 1) { /* * end of tour is reached but there are still photos available, set remaining photos * at the end of the tour */ while (true) { final double xValue = xAxisSerie[numberOfTimeSlices - 1]; chartPhotos.add(new ChartPhoto(photo, xValue, serieIndex)); photoIndex++; if (photoIndex < numberOfPhotos) { photo = srcPhotos.get(photoIndex); } else { break; } } } else { // calculate next time slice long valuePointTime; long sliceDuration; if (isTimeSerie) { valuePointTime = timeSerie[serieIndex]; sliceDuration = timeSerie[serieIndex + 1] - valuePointTime; } else { // is history valuePointTime = historySerie[serieIndex]; sliceDuration = historySerie[serieIndex + 1] - valuePointTime; } timeSliceEnd = tourStart + valuePointTime + (sliceDuration / 2); } } } private void createLayer_2ndAlti() { if (_is2ndAltiLayerVisible && (_layer2ndAlti != null)) { _layer2ndAltiSerie = _layer2ndAlti.create2ndAltiLayer(); } else { _layer2ndAltiSerie = null; } } /** * create the layer which displays the tour marker * * @param isForcedMarker * When <code>true</code> the marker must be drawn, otherwise * {@link TourChartConfiguration#isShowTourMarker} determines if the markers are * drawn or not. * <p> * Marker must be drawn in the marker dialog. */ private void createLayer_Marker(final boolean isForcedMarker) { if (isForcedMarker == false && _tcc.isShowTourMarker == false) { // marker layer is not displayed hideMarkerLayer(); return; } // marker layer is visible final ChartMarkerConfig cmc = new ChartMarkerConfig(); cmc.isDrawMarkerWithDefaultColor = _tcc.isDrawMarkerWithDefaultColor; cmc.isShowAbsoluteValues = _tcc.isShowAbsoluteValues; cmc.isShowHiddenMarker = _tcc.isShowHiddenMarker; cmc.isShowMarkerLabel = _tcc.isShowMarkerLabel; cmc.isShowMarkerTooltip = _tcc.isShowMarkerTooltip; cmc.isShowMarkerPoint = _tcc.isShowMarkerPoint; cmc.isShowOnlyWithDescription = _tcc.isShowOnlyWithDescription; cmc.isShowSignImage = _tcc.isShowSignImage; cmc.isShowLabelTempPos = _tcc.isShowLabelTempPos; cmc.markerLabelTempPos = _tcc.markerLabelTempPos; cmc.markerTooltipPosition = _tcc.markerTooltipPosition; cmc.markerHoverSize = _tcc.markerHoverSize; cmc.markerLabelOffset = _tcc.markerLabelOffset; cmc.markerPointSize = _tcc.markerPointSize; cmc.markerSignImageSize = _tcc.markerSignImageSize; cmc.markerColorDefault = _tcc.markerColorDefault; cmc.markerColorDevice = _tcc.markerColorDevice; cmc.markerColorHidden = _tcc.markerColorHidden; if (_layerMarker == null) { // setup marker layer, a layer is created only once _layerMarker = new ChartLayerMarker(this); // set overlay painter addChartOverlay(_layerMarker); addChartMouseListener(_mouseMarkerListener); } _layerMarker.setChartMarkerConfig(cmc); _tourMarkerTooltip.setChartMarkerConfig(cmc); // set data serie for the x-axis final double[] xAxisSerie = _tcc.isShowTimeOnXAxis ? _tourData.getTimeSerieDouble() : _tourData.getDistanceSerieDouble(); if (_tourData.isMultipleTours()) { final int[] multipleStartTimeIndex = _tourData.multipleTourStartIndex; final int[] multipleNumberOfMarkers = _tourData.multipleNumberOfMarkers; int tourIndex = 0; int numberOfMultiMarkers = 0; int tourSerieIndex = 0; // setup first multiple tour tourSerieIndex = multipleStartTimeIndex[tourIndex]; numberOfMultiMarkers = multipleNumberOfMarkers[tourIndex]; final ArrayList<TourMarker> allTourMarkers = _tourData.multiTourMarkers; for (int markerIndex = 0; markerIndex < allTourMarkers.size(); markerIndex++) { while (markerIndex >= numberOfMultiMarkers) { // setup next tour tourIndex++; if (tourIndex <= multipleStartTimeIndex.length - 1) { tourSerieIndex = multipleStartTimeIndex[tourIndex]; numberOfMultiMarkers += multipleNumberOfMarkers[tourIndex]; } } final TourMarker tourMarker = allTourMarkers.get(markerIndex); final int xAxisSerieIndex = tourSerieIndex + tourMarker.getSerieIndex(); tourMarker.setMultiTourSerieIndex(xAxisSerieIndex); final ChartLabel chartLabel = createLayer_Marker_ChartLabel(// tourMarker, xAxisSerie, xAxisSerieIndex, tourMarker.getLabelPosition()); cmc.chartLabels.add(chartLabel); } } else { for (final TourMarker tourMarker : _tourData.getTourMarkers()) { final ChartLabel chartLabel = createLayer_Marker_ChartLabel(tourMarker, xAxisSerie, tourMarker.getSerieIndex(), tourMarker.getLabelPosition()); cmc.chartLabels.add(chartLabel); } } } /** * @param tourMarker * @param xAxisSerie * @param xAxisSerieIndex * @param labelPosition * @return */ private ChartLabel createLayer_Marker_ChartLabel(final TourMarker tourMarker, final double[] xAxisSerie, final int xAxisSerieIndex, final int labelPosition) { final ChartLabel chartLabel = new ChartLabel(); chartLabel.data = tourMarker; // create marker label String markerLabel = tourMarker.getLabel(); final boolean isDescription = tourMarker.getDescription().length() > 0; final boolean isUrlAddress = tourMarker.getUrlAddress().length() > 0; final boolean isUrlText = tourMarker.getUrlText().length() > 0; if (isDescription | isUrlAddress | isUrlText) { markerLabel += UI.SPACE2 + UI.SYMBOL_FOOT_NOTE; } chartLabel.graphX = xAxisSerie[xAxisSerieIndex]; chartLabel.serieIndex = xAxisSerieIndex; chartLabel.markerLabel = markerLabel; chartLabel.isDescription = isDescription; chartLabel.visualPosition = labelPosition; chartLabel.type = tourMarker.getType(); chartLabel.visualType = tourMarker.getVisibleType(); chartLabel.labelXOffset = tourMarker.getLabelXOffset(); chartLabel.labelYOffset = tourMarker.getLabelYOffset(); chartLabel.isVisible = tourMarker.isMarkerVisible(); // final TourSign tourSign = tourMarker.getTourSign(); // if (tourSign != null) { // chartLabel.markerSignPhoto = tourSign.getSignImagePhoto(); // } return chartLabel; } private void createLayer_Photo() { while (true) { if (_tcc.isShowTourPhotos == false) { break; } final int[] timeSerie = _tourData.timeSerie; final long[] historySerie = _tourData.timeSerieHistory; final boolean isTimeSerie = timeSerie != null; final boolean isHistorySerie = historySerie != null; if (isTimeSerie == false && isHistorySerie == false) { // this is a manually created tour break; } final ArrayList<PhotoCategory> chartPhotoGroups = new ArrayList<PhotoCategory>(); /* * get saved photos */ final ArrayList<Photo> srcTourPhotos = _tourData.getGalleryPhotos(); if (srcTourPhotos != null && srcTourPhotos.size() > 0) { final ArrayList<ChartPhoto> chartPhotos = new ArrayList<ChartPhoto>(); createChartPhotos(srcTourPhotos, chartPhotos, true); final PhotoCategory chartPhotoGroup = new PhotoCategory(chartPhotos, ChartPhotoType.TOUR); chartPhotoGroups.add(chartPhotoGroup); } /* * get link photos, they are painted below saved photos that the mouse hit area is * larger */ final TourPhotoLink tourPhotoLink = _tourData.tourPhotoLink; if (tourPhotoLink != null) { final ArrayList<Photo> srcLinkPhotos = tourPhotoLink.linkPhotos; if (srcLinkPhotos.size() > 0) { final ArrayList<ChartPhoto> chartPhotos = new ArrayList<ChartPhoto>(); createChartPhotos(srcLinkPhotos, chartPhotos, false); final PhotoCategory chartPhotoGroup = new PhotoCategory(chartPhotos, ChartPhotoType.LINK); chartPhotoGroups.add(chartPhotoGroup); } } if (chartPhotoGroups.size() == 0) { // there are no photos break; } /* * at least 1 photo is available */ /* * The old photo layer can still be available. This happens because the action button is * a three state button. */ if (_layerPhoto == null) { _layerPhoto = new ChartLayerPhoto(chartPhotoGroups); _layerPhoto.setBackgroundColor(_photoOverlayBGColorLink, _photoOverlayBGColorTour); // set overlay painter addChartOverlay(_layerPhoto); addChartMouseListener(_mousePhotoListener); } if (isTimeSerie == false && isHistorySerie) { // hide x slider in history chart setShowSlider(false); } else { // ensure sliders are displayed for real tours setShowSlider(true); } return; } hidePhotoLayer(); } /** * Creates the layers from the segmented tour data */ private void createLayer_TourSegmenter() { setupTourSegmenter(); if (_isTourSegmenterVisible == false) { return; } if (_tourData == null) { return; } final int[] segmentSerieIndex = _tourData.segmentSerieIndex; if (segmentSerieIndex == null) { // no segmented tour data available or segments are invisible return; } // show hidden values final boolean isHideSmallValues = Util.getStateBoolean(_tourSegmenterState, TourSegmenterView.STATE_IS_HIDE_SMALL_VALUES, TourSegmenterView.STATE_IS_HIDE_SMALL_VALUES_DEFAULT); final int smallValueSize = Util.getStateInt(_tourSegmenterState, TourSegmenterView.STATE_SMALL_VALUE_SIZE, TourSegmenterView.STATE_SMALL_VALUE_SIZE_DEFAULT); // show segment lines final boolean isShowSegmenterLine = Util.getStateBoolean(_tourSegmenterState, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_LINE, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_LINE_DEFAULT); final int lineOpacity = Util.getStateInt(_tourSegmenterState, // TourSegmenterView.STATE_LINE_OPACITY, TourSegmenterView.STATE_LINE_OPACITY_DEFAULT); final boolean isShowSegmenterMarker = Util.getStateBoolean(_tourSegmenterState, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_MARKER, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_MARKER_DEFAULT); final boolean isShowSegmenterValue = Util.getStateBoolean(_tourSegmenterState, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_VALUE, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_VALUE_DEFAULT); final boolean isShowDecimalPlaces = Util.getStateBoolean(_tourSegmenterState, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_DECIMAL_PLACES, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_DECIMAL_PLACES_DEFAULT); final int stackedValues = Util.getStateInt(_tourSegmenterState, TourSegmenterView.STATE_STACKED_VISIBLE_VALUES, TourSegmenterView.STATE_STACKED_VISIBLE_VALUES_DEFAULT); final int graphOpacity = Util.getStateInt(_tourSegmenterState, // TourSegmenterView.STATE_GRAPH_OPACITY, TourSegmenterView.STATE_GRAPH_OPACITY_DEFAULT); final double[] xDataSerie = _tcc.isShowTimeOnXAxis ? _tourData.getTimeSerieDouble() : _tourData.getDistanceSerieDouble(); /* * Create/update altitude layer */ if (_layerTourSegmenterAltitude == null) { _layerTourSegmenterAltitude = new ChartLayerSegmentAltitude(this); // set overlay painter to paint hovered segments addChartOverlay(_layerTourSegmenterAltitude); } _layerTourSegmenterAltitude.setTourData(_tourData); _layerTourSegmenterAltitude.setIsShowDecimalPlaces(isShowDecimalPlaces); _layerTourSegmenterAltitude.setIsShowSegmenterMarker(isShowSegmenterMarker); _layerTourSegmenterAltitude.setIsShowSegmenterValue(isShowSegmenterValue); _layerTourSegmenterAltitude.setLineProperties(isShowSegmenterLine, lineOpacity); _layerTourSegmenterAltitude.setSmallHiddenValuesProperties(isHideSmallValues, smallValueSize); _layerTourSegmenterAltitude.setStackedValues(stackedValues); _layerTourSegmenterAltitude.setXDataSerie(xDataSerie); /* * Create/update value layer */ if (_layerTourSegmenterOther == null) { _layerTourSegmenterOther = new ChartLayerSegmentValue(this); } _layerTourSegmenterOther.setTourData(_tourData); _layerTourSegmenterOther.setIsShowDecimalPlaces(isShowDecimalPlaces); _layerTourSegmenterOther.setIsShowSegmenterValues(isShowSegmenterValue); _layerTourSegmenterOther.setLineProperties(isShowSegmenterLine, lineOpacity); _layerTourSegmenterOther.setSmallHiddenValuesProperties(isHideSmallValues, smallValueSize); _layerTourSegmenterOther.setStackedValues(stackedValues); _layerTourSegmenterOther.setXDataSerie(xDataSerie); // draw the graph lighter that the segments are more visible setGraphAlpha(graphOpacity / 100.0); } private void createPainter_HrZone() { if (_tcc.isHrZoneDisplayed) { _hrZonePainter = new HrZonePainter(); } else { _hrZonePainter = null; } } private void createSelectedLines() { if (_selectedSegmenterSegment_1 == null || _layerTourSegmenterAltitude == null) { _selectedAltitudePoints = null; _selectedAltitudeRGB = null; _selectedOtherPoints = null; _selectedPathsRGB = null; return; } /* * Get segment start/end indices */ int selectedSegmentIndexStart = _selectedSegmenterSegment_1.segmentIndex; int selectedSegmentIndexEnd; if (_selectedSegmenterSegment_2 == null) { selectedSegmentIndexEnd = selectedSegmentIndexStart; } else { selectedSegmentIndexEnd = _selectedSegmenterSegment_2.segmentIndex; } // depending how the segments are selected in the UI, start can be larger than the end if (selectedSegmentIndexStart > selectedSegmentIndexEnd) { // swap indices final int tempIndex = selectedSegmentIndexEnd; selectedSegmentIndexEnd = selectedSegmentIndexStart; selectedSegmentIndexStart = tempIndex; } /* * Create poline for all selected segments */ TIntArrayList selectedAltitudePath = null; final ArrayList<SegmenterSegment> paintedSegments_Altitude = _layerTourSegmenterAltitude .getPaintedSegments(); final ArrayList<RGB> selectedAltitudeRGB = new ArrayList<>(); if (paintedSegments_Altitude.size() > 0) { selectedAltitudePath = createSelectedLines_Values(paintedSegments_Altitude, selectedSegmentIndexStart, selectedSegmentIndexEnd, selectedAltitudeRGB); } _selectedAltitudePoints = selectedAltitudePath; _selectedAltitudeRGB = selectedAltitudeRGB; /* * */ final ArrayList<TIntArrayList> selectedOtherPaths = new ArrayList<>(); final ArrayList<RGB> selectedPathsRGB = new ArrayList<>(); final ArrayList<ArrayList<SegmenterSegment>> paintedSegemntsOther = _layerTourSegmenterOther .getPaintedSegments(); for (final ArrayList<SegmenterSegment> paintedSegments : paintedSegemntsOther) { final TIntArrayList selectedPath = createSelectedLines_Values(paintedSegments, selectedSegmentIndexStart, selectedSegmentIndexEnd, selectedPathsRGB); selectedOtherPaths.add(selectedPath); } _selectedOtherPoints = selectedOtherPaths; _selectedPathsRGB = selectedPathsRGB; } private TIntArrayList createSelectedLines_Values(final ArrayList<SegmenterSegment> paintedSegments, final int selectedSegmentIndexStart, final int selectedSegmentIndexEnd, final ArrayList<RGB> allValueRGBs) { final TIntArrayList lineSegments = new TIntArrayList(); // check bounds final int allLabelSize = paintedSegments.size(); boolean isFirstPainted = false; for (int labelIndex = 0; labelIndex < allLabelSize; labelIndex++) { final SegmenterSegment paintedSegment = paintedSegments.get(labelIndex); final int labelSegmentIndex = paintedSegment.segmentIndex; if (labelSegmentIndex > selectedSegmentIndexEnd) { // last segment is painted break; } if (isFirstPainted) { // create following segments lineSegments.add(paintedSegment.paintedX2); lineSegments.add(paintedSegment.paintedY2); allValueRGBs.add(paintedSegment.paintedRGB); } else if (labelSegmentIndex >= selectedSegmentIndexStart // // check if this value is valid && paintedSegment.paintedX1 != Integer.MIN_VALUE // ) { // create first segment isFirstPainted = true; lineSegments.add(paintedSegment.paintedX1); lineSegments.add(paintedSegment.paintedY1); allValueRGBs.add(paintedSegment.paintedRGB); lineSegments.add(paintedSegment.paintedX2); lineSegments.add(paintedSegment.paintedY2); allValueRGBs.add(paintedSegment.paintedRGB); } } return lineSegments; } @Override public void deleteTourMarker(final TourMarker tourMarker) { if (_isDisplayedInDialog) { /* * Do not confirm deletion when tour chart is displayed in a dialog, because the dialog * and the removal can be canceled. */ // this will update the chart fireTourMarkerModifyEvent(tourMarker, true); } else { // confirm to delete the marker if (MessageDialog.openQuestion(getShell(), Messages.Dlg_TourMarker_MsgBox_delete_marker_title, NLS.bind(Messages.Dlg_TourMarker_MsgBox_delete_marker_message, (tourMarker).getLabel()))) { // remove tourmarker from the model final boolean isRemoved = _tourData.getTourMarkers().remove(tourMarker); Assert.isTrue(isRemoved); // tour will be saved and the chart will also be updated fireTourModifyEvent_Globally(); } } } @Override public void drawSelectedLines(final GC gc, final ArrayList<GraphDrawingData> allGraphDrawingData, final boolean isFocusActive) { if (_isRecomputeLineSelection) { _isRecomputeLineSelection = false; createSelectedLines(); } final Display display = getDisplay(); gc.setLineWidth(9); gc.setLineStyle(SWT.LINE_SOLID); gc.setLineJoin(SWT.JOIN_ROUND); gc.setAntialias(SWT.ON); // SET CLIPPING FOR EACH GRAPH, this is a bit complex // SET CLIPPING FOR EACH GRAPH // SET CLIPPING FOR EACH GRAPH // SET CLIPPING FOR EACH GRAPH // SET CLIPPING FOR EACH GRAPH // SET CLIPPING FOR EACH GRAPH // final int devYTop = graphDrawingData.getDevYTop(); // final int devGraphHeight = graphDrawingData.devGraphHeight; // // gc.setClipping(0, devYTop, gc.getClipping().width, devGraphHeight); // gc.setClipping((Rectangle) null); // paint altitude line if (_selectedAltitudePoints != null) { gc.setAlpha(isFocusActive ? 0xa0 : 0x60); final Path path1 = new Path(display); final Path path2 = new Path(display); { final int[] pathPoints = _selectedAltitudePoints.toArray(); int graphX1; int graphY1; int graphXPrev = 200; int graphYPrev = 200; RGB rgb1 = null; RGB rgb2 = null; RGB prevRGB = null; for (int pointIndex = 0; pointIndex < pathPoints.length;) { /* * get segment colors */ final RGB segmentRGB = _selectedAltitudeRGB.get(pointIndex / 2); if (rgb1 == null) { rgb1 = segmentRGB; } if (rgb2 == null && rgb1 != segmentRGB) { rgb2 = segmentRGB; } graphX1 = pathPoints[pointIndex++]; graphY1 = pathPoints[pointIndex++]; if (pointIndex == 4) { // draw start segment path1.moveTo(graphXPrev, graphYPrev); path1.lineTo(graphX1, graphY1); } else if (pointIndex > 4) { // draw following segments // optimize, draw only when changed if (graphX1 != graphXPrev || graphY1 != graphYPrev || prevRGB != segmentRGB) { if (segmentRGB == prevRGB) { // use the same color as the previous if (segmentRGB == rgb1) { path1.lineTo(graphX1, graphY1); } else { path2.lineTo(graphX1, graphY1); } } else { // color has changed, paint into other path if (segmentRGB == rgb1) { path1.moveTo(graphXPrev, graphYPrev); path1.lineTo(graphX1, graphY1); } else { path2.moveTo(graphXPrev, graphYPrev); path2.lineTo(graphX1, graphY1); } } } } graphXPrev = graphX1; graphYPrev = graphY1; prevRGB = segmentRGB; } gc.setLineCap(SWT.CAP_ROUND); gc.setClipping(_layerTourSegmenterAltitude.getGraphArea()); if (rgb1 != null) { final Color color1 = new Color(display, rgb1); { gc.setForeground(color1); gc.drawPath(path1); } color1.dispose(); } if (rgb2 != null) { final Color color2 = new Color(display, rgb2); { gc.setForeground(color2); gc.drawPath(path2); } color2.dispose(); } } path1.dispose(); path2.dispose(); } // paint paths if (_selectedOtherPoints != null) { gc.setAlpha(isFocusActive ? 0x60 : 0x30); final ArrayList<Rectangle> allGraphAreas = _layerTourSegmenterOther.getAllGraphAreas(); for (int pathIndex = 0; pathIndex < _selectedOtherPoints.size(); pathIndex++) { final TIntArrayList graphLine = _selectedOtherPoints.get(pathIndex); if (graphLine.size() == 0) { // can be empty when small values are hidden continue; } final Rectangle graphArea = allGraphAreas.get(pathIndex); final RGB pathRGB = _selectedPathsRGB.get(pathIndex * (graphLine.size() / 2)); final Path path = new Path(display); final Color pathColor = new Color(display, pathRGB); { final int[] pathPoints = graphLine.toArray(); int graphX1; int graphY1; int graphX2 = 0; for (int pointIndex = 0; pointIndex < pathPoints.length;) { graphX1 = pathPoints[pointIndex++]; graphY1 = pathPoints[pointIndex++]; if (pointIndex == 4) { path.moveTo(graphX2, graphY1); path.lineTo(graphX1, graphY1); } else if (pointIndex > 4) { path.moveTo(graphX2, graphY1); path.lineTo(graphX1, graphY1); } graphX2 = graphX1; } gc.setForeground(pathColor); gc.setClipping(graphArea); gc.drawPath(path); } pathColor.dispose(); path.dispose(); } } gc.setLineCap(SWT.CAP_FLAT); gc.setClipping((Rectangle) null); } public void enableGraphAction(final int graphId, final boolean isEnabled) { if (_allTourChartActions == null) { return; } final Action action = _allTourChartActions.get(getGraphActionId(graphId)); if (action != null) { action.setEnabled(isEnabled); } } private void enableZoomOptions() { if (_allTourChartActions == null) { return; } final boolean canAutoZoom = getMouseMode().equals(Chart.MOUSE_MODE_ZOOM); final Action action = _allTourChartActions.get(ACTION_ID_CAN_MOVE_SLIDERS_WHEN_ZOOMED); if (action != null) { action.setEnabled(canAutoZoom); } } /** * create the tour specific action bar, they are defined in the chart configuration */ private void fillToolbar() { // check if toolbar is created if (_isTourChartToolbarCreated) { return; } _isTourChartToolbarCreated = true; final IToolBarManager tbm = getToolBarManager(); /* * add actions to the toolbar */ if (_tcc.canShowTourCompareGraph) { tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_TOUR_COMPARE))); } tbm.add(new Separator()); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_ALTITUDE))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_PULSE))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_SPEED))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_PACE))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_POWER))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_TEMPERATURE))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_GRADIENT))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_ALTIMETER))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_CADENCE))); tbm.add(_allTourChartActions.get(getGraphActionId(TourManager.GRAPH_GEARS))); tbm.add(new Separator()); tbm.add(_allTourChartActions.get(ACTION_ID_IS_GRAPH_OVERLAPPED)); tbm.add(_allTourChartActions.get(ACTION_ID_HR_ZONE_DROPDOWN_MENU)); tbm.add(_allTourChartActions.get(ACTION_ID_X_AXIS_TIME)); tbm.add(_allTourChartActions.get(ACTION_ID_X_AXIS_DISTANCE)); tbm.add(new Separator()); tbm.add(_allTourChartActions.get(ACTION_ID_IS_SHOW_TOUR_PHOTOS)); tbm.add(_actionTourMarker); tbm.add(_actionTourInfo); tbm.add(_actionTourChartSmoothing); tbm.add(_actionGraphMinMax); tbm.add(_actionTourChartOptions); tbm.update(true); } /** * Fires an event when the a tour marker is modified. * * @param tourMarker * @param isTourMarkerDeleted */ private void fireTourMarkerModifyEvent(final TourMarker tourMarker, final boolean isTourMarkerDeleted) { final Object[] listeners = _tourMarkerModifyListener.getListeners(); for (final Object listener2 : listeners) { final ITourMarkerModifyListener listener = (ITourMarkerModifyListener) listener2; listener.tourMarkerIsModified(tourMarker, isTourMarkerDeleted); } } private void fireTourMarkerSelection(final TourMarker tourMarker) { // update selection locally (e.g. in a dialog) final ArrayList<TourMarker> allTourMarker = new ArrayList<TourMarker>(); allTourMarker.add(tourMarker); final SelectionTourMarker tourMarkerSelection = new SelectionTourMarker(_tourData, allTourMarker); final Object[] listeners = _tourMarkerSelectionListener.getListeners(); for (final Object listener2 : listeners) { final ITourMarkerSelectionListener listener = (ITourMarkerSelectionListener) listener2; listener.selectionChanged(tourMarkerSelection); } if (_isDisplayedInDialog) { return; } TourManager.fireEventWithCustomData(// TourEventId.MARKER_SELECTION, tourMarkerSelection, _part); } /** * Fires an event when a tour is modified. */ private void fireTourModifyEvent_Globally() { final Object[] listeners = _tourModifyListener.getListeners(); for (final Object listener2 : listeners) { final ITourModifyListener listener = (ITourModifyListener) listener2; listener.tourIsModified(_tourData); } } /** * Fires an event when the x-axis values were changed by the user * * @param isShowTimeOnXAxis */ private void fireXAxisSelection(final boolean showTimeOnXAxis) { final Object[] listeners = _xAxisSelectionListener.getListeners(); for (final Object listener2 : listeners) { final IXAxisSelectionListener listener = (IXAxisSelectionListener) listener2; listener.selectionChanged(showTimeOnXAxis); } } /** * Converts the graph Id into an action Id * * @param graphId * @return */ private String getGraphActionId(final int graphId) { return "graphId." + Integer.toString(graphId); //$NON-NLS-1$ } /** * @return Returns the hovered marker or <code>null</code> when a marker is not hovered. */ private ChartLabel getHoveredMarkerLabel() { if (_layerMarker == null) { return null; } return _layerMarker.getHoveredLabel(); } SegmenterSegment getHoveredSegmenterSegment() { return _hoveredSegmenterSegment; } private SegmenterSegment getHoveredSegmenterSegment(final ChartMouseEvent mouseEvent) { if (_layerTourSegmenterAltitude == null) { // this occured, it is possible that an event is fired but this layer is not yet set return null; } SegmenterSegment hoveredSegmenterSegment = _layerTourSegmenterAltitude.getHoveredSegment(mouseEvent); if (hoveredSegmenterSegment == null) { hoveredSegmenterSegment = _layerTourSegmenterOther.getHoveredSegment(mouseEvent); } return hoveredSegmenterSegment; } /** * @param mouseEvent * @return Returns the hovered title or <code>null</code> when a title is not hovered. */ private ChartTitleSegment getHoveredTitleSegment(final ChartMouseEvent mouseEvent) { final int devXMouse = mouseEvent.devXMouse; final int devYMouse = mouseEvent.devYMouse; final ChartDrawingData chartDrawingData = getChartDrawingData(); final ArrayList<ChartTitleSegment> chartTitleSegments = chartDrawingData.chartTitleSegments; for (final ChartTitleSegment chartTitleSegment : chartTitleSegments) { final int devXSegment = chartTitleSegment.devXSegment; final int devYLabel = chartTitleSegment.devYTitle; if (devXMouse > devXSegment && devXMouse < devXSegment + chartTitleSegment.devSegmentWidth && devYMouse > 0 && devYMouse < devYLabel + chartTitleSegment.titleHeight) { return chartTitleSegment; } } return null; } /** * @return Returns a {@link TourMarker} when a {@link ChartLabel} (marker) is hovered or * <code>null</code> when a {@link ChartLabel} is not hovered. */ public TourMarker getHoveredTourMarker() { TourMarker tourMarker = null; final ChartLabel hoveredMarkerLabel = getHoveredMarkerLabel(); if (hoveredMarkerLabel != null) { if (hoveredMarkerLabel.data instanceof TourMarker) { tourMarker = (TourMarker) hoveredMarkerLabel.data; } } _lastHoveredTourMarker = tourMarker; return tourMarker; } public TourMarker getLastHoveredTourMarker() { return _lastHoveredTourMarker; } ChartLayerMarker getLayerTourMarker() { return _layerMarker; } TourMarker getSelectedTourMarker() { return _selectedTourMarker; } @Override public ArrayList<TourData> getSelectedTours() { final ArrayList<TourData> tourChartTours = new ArrayList<TourData>(); if (_tourData != null) { TourData tourData = null; if (_tourData.isMultipleTours()) { if (_selectedTourMarker != null) { // get tour from hovered marker tourData = _selectedTourMarker.getTourData(); } } else { tourData = _tourData; } if (tourData != null) { tourChartTours.add(tourData); } } return tourChartTours; } public Map<String, Action> getTourChartActions() { return _allTourChartActions; } public TourChartConfiguration getTourChartConfig() { return _tcc; } public TourData getTourData() { return _tourData; } public ArrayList<TourSegment> getTourSegments() { final IViewPart viewPart = Util.getView(TourSegmenterView.ID); if (viewPart instanceof TourSegmenterView) { return ((TourSegmenterView) viewPart).getTourSegments(); } return null; } Font getValueFont() { if (_segmenterValueFont == null) { setupSegmenterValueFont(); } return _segmenterValueFont; } private ChartDataYSerie getYData(final int yDataInfoId) { final ArrayList<ChartDataYSerie> yDataList = getChartDataModel().getYData(); // get y-data serie from custom data ChartDataYSerie yData = null; for (final ChartDataYSerie yDataIterator : yDataList) { final Integer yDataInfo = (Integer) yDataIterator.getCustomData(ChartDataYSerie.YDATA_INFO); if (yDataInfo == yDataInfoId) { yData = yDataIterator; break; } } return yData; } /** * Disable marker layer. */ private void hideMarkerLayer() { if (_layerMarker != null) { removeChartOverlay(_layerMarker); _layerMarker = null; } removeChartMouseListener(_mouseMarkerListener); } void hideMarkerTooltip() { // disable selection _selectedTourMarker = null; _tourMarkerTooltip.hideNow(); } private void hidePhotoLayer() { if (_layerPhoto != null) { removeChartOverlay(_layerPhoto); _layerPhoto = null; } removeChartMouseListener(_mousePhotoListener); } private void onChart_KeyDown(final ChartKeyEvent keyEvent) { if (_isTourSegmenterVisible) { selectSegmenterSegment(keyEvent); } } private void onDispose() { if (_segmenterValueFont != null) { _segmenterValueFont.dispose(); } _prefStore.removePropertyChangeListener(_prefChangeListener); _photoOverlayBGColorLink.dispose(); _photoOverlayBGColorTour.dispose(); _valuePointTooltip.hide(); } private void onMarker_ChartResized() { // hide tooltip otherwise it has the wrong location, disable selection _selectedTourMarker = null; // ensure that a marker do not keeps hovered state when chart is zoomed _layerMarker.resetHoveredState(); _tourMarkerTooltip.hideNow(); } private void onMarker_MouseDoubleClick(final ChartMouseEvent event) { final TourMarker tourMarker = getHoveredTourMarker(); if (tourMarker != null) { // notify the chart mouse listener that no other actions should be done event.isWorked = true; // prevent that this action will open another marker dialog if (_isDisplayedInDialog == false) { // open marker dialog with the hovered/selected tour marker _actionOpenMarkerDialog.setTourMarker(tourMarker); _actionOpenMarkerDialog.run(); } } } private void onMarker_MouseDown(final ChartMouseEvent mouseEvent) { final TourMarker tourMarker = getHoveredTourMarker(); if (tourMarker != null) { // notify the chart mouse listener that no other actions should be done mouseEvent.isWorked = true; _selectedTourMarker = tourMarker; fireTourMarkerSelection(tourMarker); _firedTourMarker = tourMarker; // redraw chart setChartOverlayDirty(); } } private void onMarker_MouseExit() { // mouse has exited the chart, reset hovered label if (_layerMarker == null) { return; } // disable selection _selectedTourMarker = null; _layerMarker.resetHoveredState(); // redraw chart setChartOverlayDirty(); } private void onMarker_MouseMove(final ChartMouseEvent mouseEvent) { if (_layerMarker == null) { return; } final ChartLabel hoveredLabel = _layerMarker.retrieveHoveredLabel(mouseEvent); final boolean isLabelHovered = hoveredLabel != null; if (isLabelHovered) { // set worked that no other actions are done in this event mouseEvent.isWorked = isLabelHovered; mouseEvent.cursor = ChartCursor.Arrow; } // check if the selected marker is hovered final TourMarker hoveredMarker = getHoveredTourMarker(); if (_selectedTourMarker != null && hoveredLabel == null || (hoveredMarker != _selectedTourMarker)) { _selectedTourMarker = null; // redraw chart setChartOverlayDirty(); } // ensure that a selected tour marker is drawn in the overlay if (_selectedTourMarker != null) { // redraw chart setChartOverlayDirty(); } if (_tcc.isShowMarkerTooltip) { // marker tooltip is displayed _tourMarkerTooltip.open(hoveredLabel); } } private void onMarker_MouseUp(final ChartMouseEvent mouseEvent) { if (_layerMarker == null) { return; } final ChartLabel hoveredLabel = _layerMarker.retrieveHoveredLabel(mouseEvent); final boolean isLabelHovered = hoveredLabel != null; if (isLabelHovered) { // set marker default cursor when the mouse is still hovering a marker mouseEvent.isWorked = isLabelHovered; mouseEvent.cursor = ChartCursor.Arrow; /* * Fire tourmarker selection */ final TourMarker hoveredTourMarker = getHoveredTourMarker(); // ensure that the tour marker selection is fired only once if (_firedTourMarker == null || _firedTourMarker != hoveredTourMarker) { // select the tour marker when the context menu is opened fireTourMarkerSelection(hoveredTourMarker); } _firedTourMarker = null; } } private void onPhoto_ChartResized() { // reset hovered state otherwise hovered photo marker are still displayed when chart is zoomed in onPhoto_MouseExit(); } private void onPhoto_MouseExit() { // mouse has exited the chart, reset hovered marker if (_layerPhoto == null) { return; } _layerPhoto.setHoveredData(null, null); // redraw chart overlay setChartOverlayDirty(); } private void onPhoto_MouseMove(final ChartMouseEvent mouseEvent) { if (_layerPhoto == null) { return; } // check if photos are hovered final PhotoCategory hoveredPhotoCategory = _layerPhoto.getHoveredPhotoCategory(// mouseEvent.eventTime, mouseEvent.devXMouse, mouseEvent.devYMouse); final PhotoPaintGroup hoveredPhotoGroup = _layerPhoto.getHoveredPaintGroup(); _layerPhoto.setHoveredData(hoveredPhotoCategory, hoveredPhotoGroup); final boolean isHovered = hoveredPhotoGroup != null; if (isHovered) { mouseEvent.isWorked = isHovered; } } /** * Fire selection for the selected segment label. * * @param mouseEvent */ private void onSegmenterSegment_MouseDown(final ChartMouseEvent mouseEvent) { final SegmenterSegment hoveredSegment = getHoveredSegmenterSegment(mouseEvent); if (hoveredSegment == null) { _selectedSegmenterSegment_1 = null; _selectedSegmenterSegment_2 = null; // redraw chart setSelectedLines(false); return; } // notify the chart mouse listener that no other actions should be done mouseEvent.isWorked = true; mouseEvent.cursor = ChartCursor.Arrow; mouseEvent.isDisableSliderDragging = true; final boolean isShift = (mouseEvent.stateMask & SWT.SHIFT) != 0; if (isShift) { if (_selectedSegmenterSegment_1 == null) { // start new selection _selectedSegmenterSegment_1 = hoveredSegment; _selectedSegmenterSegment_2 = null; } else { // extend selection _selectedSegmenterSegment_2 = hoveredSegment; } } else { // start new selection _selectedSegmenterSegment_1 = hoveredSegment; _selectedSegmenterSegment_2 = null; } // redraw chart _isRecomputeLineSelection = true; setSelectedLines(true); final boolean isMoveChartToShowSlider = _selectedSegmenterSegment_2 == null; selectSegmenterSegments(// _selectedSegmenterSegment_1, _selectedSegmenterSegment_2, isMoveChartToShowSlider); } private void onSegmenterSegment_MouseExit() { _hoveredSegmenterSegment = null; _tourSegmenterTooltip.hide(); setChartOverlayDirty(); } private void onSegmenterSegment_MouseMove(final ChartMouseEvent mouseEvent) { // ignore events with the same time if (mouseEvent.eventTime == _hoveredSegmenterSegmentEventTime) { mouseEvent.isWorked = _isSegmenterSegmentHovered; mouseEvent.cursor = ChartCursor.Arrow; return; } _hoveredSegmenterSegmentEventTime = mouseEvent.eventTime; final SegmenterSegment hoveredSegment = getHoveredSegmenterSegment(mouseEvent); _isSegmenterSegmentHovered = hoveredSegment != null; if (_isSegmenterSegmentHovered) { // set worked that no other actions are done in this event mouseEvent.isWorked = _isSegmenterSegmentHovered; mouseEvent.cursor = ChartCursor.Arrow; } boolean isUpdateUI = false; final SegmenterSegment prevHoveredSegment = _hoveredSegmenterSegment; if (hoveredSegment != _hoveredSegmenterSegment) { // hovered label has changed _hoveredSegmenterSegment = hoveredSegment; isUpdateUI = true; if (_isShowSegmenterTooltip) { // show/hide tooltip if (hoveredSegment == null) { _tourSegmenterTooltip.hide(); } else { // close other tooltips _openDlgMgr.closeOpenedDialogs(_tourSegmenterTooltip); _tourSegmenterTooltip.open(hoveredSegment); } } } else if (hoveredSegment == null && prevHoveredSegment != null) { // hide previous tooltip when not yet hidden _tourSegmenterTooltip.hide(); } if (isUpdateUI) { setChartOverlayDirty(); } } private void onSegmenterSegment_MouseUp(final ChartMouseEvent mouseEvent) { final SegmenterSegment segmentSegment = getHoveredSegmenterSegment(mouseEvent); if (segmentSegment == null) { return; } final boolean isHovered = segmentSegment != null; if (isHovered) { // set marker default cursor when the mouse is still hovering a marker mouseEvent.isWorked = true; mouseEvent.cursor = ChartCursor.Arrow; } } private void onSegmenterSegment_Resize() { /* * Only visible segments are painted, during a resize the selection has not changed but the * visible segments -> make the selected segements visible again. */ if (_segmenterSelection != null) { selectXSliders_Segments(_segmenterSelection); } _hoveredSegmenterSegment = null; _isRecomputeLineSelection = true; setChartOverlayDirty(); } /** * Open tour quick editor. * * @param mouseEvent */ private void onSegmentTitle_MouseDoubleClick(final ChartMouseEvent mouseEvent) { final ChartTitleSegment chartTitleSegment = getHoveredTitleSegment(mouseEvent); _chartTitleSegment = chartTitleSegment; if (chartTitleSegment == null) { return; } // title is hovered and double clicked, quick edit the tour mouseEvent.isWorked = _isSegmentTitleHovered; _actionEditQuick.run(); } private void onSegmentTitle_MouseDown(final ChartMouseEvent mouseEvent) { final ChartTitleSegment chartTitleSegment = getHoveredTitleSegment(mouseEvent); _chartTitleSegment = chartTitleSegment; if (chartTitleSegment == null) { return; } // title is hovered and clicked, select tour and fire tour selection mouseEvent.isWorked = selectTour(chartTitleSegment); } private void onSegmentTitle_MouseExit() { _chartTitleSegment = null; setHoveredTitleSegment(null); _tourTitleTooltip.hide(); } private void onSegmentTitle_MouseMove(final ChartMouseEvent mouseEvent) { // ignore events with the same time if (mouseEvent.eventTime == _hoveredSegmentTitleEventTime) { mouseEvent.isWorked = _isSegmentTitleHovered; mouseEvent.cursor = ChartCursor.Arrow; return; } _hoveredSegmentTitleEventTime = mouseEvent.eventTime; final ChartTitleSegment chartTitleSegment = getHoveredTitleSegment(mouseEvent); _isSegmentTitleHovered = chartTitleSegment != null; if (_isSegmentTitleHovered) { // set worked that no other actions are done in this event mouseEvent.isWorked = _isSegmentTitleHovered; mouseEvent.cursor = ChartCursor.Arrow; } final ChartTitleSegment prevHoveredPart = _chartTitleSegment; if (_chartTitleSegment != chartTitleSegment) { // hovered title has changed, show or hide tooltip // update internal state _chartTitleSegment = chartTitleSegment; // update state in the chart setHoveredTitleSegment(chartTitleSegment); if (_tcc.isShowInfoTooltip) { // show/hide tooltip if (chartTitleSegment == null) { _tourTitleTooltip.hide(); } else { // close other tooltips _openDlgMgr.closeOpenedDialogs(_tourTitleTooltip); _tourTitleTooltip.open(chartTitleSegment); } } } else if (chartTitleSegment == null && prevHoveredPart != null) { // hide tooltip when not yet hidden _tourTitleTooltip.hide(); } } private void onSegmentTitle_Resized() { _chartTitleSegment = null; setHoveredTitleSegment(null); _tourTitleTooltip.hide(); } public void partIsDeactivated() { // hide photo tooltip _photoTooltip.hide(); } public void partIsHidden() { // hide value point tooltip _valuePointTooltip.setShellVisible(false); // hide photo tooltip _photoTooltip.hide(); } public void partIsVisible() { // show tool tip again _valuePointTooltip.setShellVisible(true); } public void removeTourMarkerSelectionListener(final ITourMarkerSelectionListener listener) { _tourMarkerSelectionListener.remove(listener); } public void removeTourModifySelectionListener(final ITourModifyListener listener) { _tourModifyListener.remove(listener); } public void removeXAxisSelectionListener(final IXAxisSelectionListener listener) { _xAxisSelectionListener.remove(listener); } /** * Reset tour segmenter selection. */ private void resetSegmenterSelection() { final int[] tourSegments = _tourData.segmentSerieIndex; int tourSegmentHash = Integer.MIN_VALUE; if (tourSegments != null) { tourSegmentHash = tourSegments.hashCode(); } if (tourSegments == null || tourSegmentHash != _oldTourSegmentsHash) { // reset selection when tour or segments have changed /* * I'm not shure if this condition is sufficient to disable the segmenter selection but * the selection should be kept as long as possible. */ _selectedSegmenterSegment_1 = null; _selectedSegmenterSegment_2 = null; _segmenterSelection = null; } _oldTourSegmentsHash = tourSegmentHash; setLineSelectionDirty(); } void restoreState() { _photoTooltip.restoreState(); } void saveState() { _photoTooltip.saveState(); _valuePointTooltip.saveState(); } private void selectSegmenterSegment(final ChartKeyEvent keyEvent) { final int[] segmentSerieIndex = _tourData.segmentSerieIndex; final int numSegments = segmentSerieIndex.length; int segmentIndex1 = -1; int segmentIndex2 = -1; final boolean isCtrlKey = (keyEvent.stateMask & SWT.MOD1) > 0; final boolean isShiftKey = (keyEvent.stateMask & SWT.MOD2) > 0; boolean isLeftKey = false; boolean isHomeKey = false; boolean isEndKey = false; int navigationSteps = 1; switch (keyEvent.keyCode) { case SWT.PAGE_DOWN: navigationSteps = PAGE_NAVIGATION_SEGMENTS; break; case SWT.ARROW_RIGHT: // support this key break; case SWT.PAGE_UP: navigationSteps = PAGE_NAVIGATION_SEGMENTS; isLeftKey = true; break; case SWT.ARROW_LEFT: isLeftKey = true; break; case SWT.HOME: isHomeKey = true; break; case SWT.END: isEndKey = true; break; default: // nothing can be navigated, do other actions return; } boolean isNavigated = false; final int lastSegmentIndex = numSegments - 1; if (_selectedSegmenterSegment_1 == null || isHomeKey || isEndKey) { // nothing is selected, start from the right or left border if (isLeftKey || isEndKey) { // navigate to the left, start navigation from the right border segmentIndex1 = lastSegmentIndex; } else { // navigate to the right, start navigation from the left border segmentIndex1 = 1; } isNavigated = true; } else { // at least one segment is selected int selectedSegmentIndex1 = _selectedSegmenterSegment_1.segmentIndex; int selectedSegmentIndex2 = -1; if (_selectedSegmenterSegment_2 != null) { selectedSegmentIndex2 = _selectedSegmenterSegment_2.segmentIndex; // swap index because with the mouse the left segment can be segment2 depending how they are selected if (selectedSegmentIndex2 < selectedSegmentIndex1) { final int tempIndex1 = selectedSegmentIndex1; selectedSegmentIndex1 = selectedSegmentIndex2; selectedSegmentIndex2 = tempIndex1; } } if (isShiftKey) { // select multiple segments if (_selectedSegmenterSegment_2 == null) { // start multiple selection if (isLeftKey) { // expand to the left if (selectedSegmentIndex1 > 1) { // expand to the next left segment segmentIndex1 = Math.max(1, selectedSegmentIndex1 - navigationSteps); segmentIndex2 = selectedSegmentIndex1; } } else { // expand to the right if (selectedSegmentIndex1 < lastSegmentIndex) { // expand to the next left segment segmentIndex1 = selectedSegmentIndex1; segmentIndex2 = Math.min(lastSegmentIndex, selectedSegmentIndex1 + navigationSteps); } } } else { // expand multiple selection if (isLeftKey) { if (isCtrlKey) { // reduce selection from the right border to the left if (selectedSegmentIndex2 - selectedSegmentIndex1 > 0) { segmentIndex1 = selectedSegmentIndex1; segmentIndex2 = selectedSegmentIndex2 - 1; } } else { // expand to the left if (selectedSegmentIndex1 > 1) { // expand to the next left segment segmentIndex1 = Math.max(1, selectedSegmentIndex1 - navigationSteps); segmentIndex2 = selectedSegmentIndex2; } } } else { if (isCtrlKey) { // reduce selection from the left border to the right if (selectedSegmentIndex2 - selectedSegmentIndex1 > 0) { segmentIndex1 = selectedSegmentIndex1 + 1; segmentIndex2 = selectedSegmentIndex2; } } else { // expand to the right if (selectedSegmentIndex2 < lastSegmentIndex) { // expand to the next right segment segmentIndex1 = selectedSegmentIndex1; segmentIndex2 = Math.min(lastSegmentIndex, selectedSegmentIndex2 + navigationSteps); } } } } if (segmentIndex2 != -1) { // 2nd index is set isNavigated = true; } if (segmentIndex1 == segmentIndex2) { // this case can occure when selection is reduced -> disable multiple selections segmentIndex2 = -1; } } else { // select a SINGLE segment if (isLeftKey) { // navigate to the left if (selectedSegmentIndex2 == -1) { // there is no multiple selection if (selectedSegmentIndex1 > 1) { // select previous segment segmentIndex1 = Math.max(1, selectedSegmentIndex1 - navigationSteps); } else { // start navigation from the right border segmentIndex1 = lastSegmentIndex; } } else { // there is multiple selection, navigate to the left segment segmentIndex1 = selectedSegmentIndex1; } } else { // navigate to the right if (selectedSegmentIndex2 == -1) { // there is no multiple selection if (selectedSegmentIndex1 < lastSegmentIndex) { // select following segment segmentIndex1 = Math.min(lastSegmentIndex, selectedSegmentIndex1 + navigationSteps); } else { // start navigation from the left border segmentIndex1 = 1; } } else { // there is multiple selection, navigate to the right segment segmentIndex1 = selectedSegmentIndex2; } } isNavigated = true; } } if (isNavigated) { _selectedSegmenterSegment_1 = new SegmenterSegment(); _selectedSegmenterSegment_1.segmentIndex = segmentIndex1; _selectedSegmenterSegment_1.xSliderSerieIndexLeft = segmentSerieIndex[segmentIndex1 - 1]; _selectedSegmenterSegment_1.xSliderSerieIndexRight = segmentSerieIndex[segmentIndex1]; if (segmentIndex2 == -1) { // disable multiple selections _selectedSegmenterSegment_2 = null; } else { // setup multiple selection _selectedSegmenterSegment_2 = new SegmenterSegment(); _selectedSegmenterSegment_2.segmentIndex = segmentIndex2; _selectedSegmenterSegment_2.xSliderSerieIndexLeft = segmentSerieIndex[segmentIndex2 - 1]; _selectedSegmenterSegment_2.xSliderSerieIndexRight = segmentSerieIndex[segmentIndex2]; } // redraw chart _isRecomputeLineSelection = true; setSelectedLines(true); final boolean isMoveChartToShowSlider = _selectedSegmenterSegment_2 == null; selectSegmenterSegments(// _selectedSegmenterSegment_1, _selectedSegmenterSegment_2, isMoveChartToShowSlider); } // prevent other actions in the chart even when a navigation cannot be done keyEvent.isWorked = true; /* * Hide segmenter tooltip, I tried to open the tooltip but this needs some more work to get * it running, THIS IS NOT A SIMPLE TASK. */ _tourSegmenterTooltip.hide(); } /** * Set sliders to the selected segments and fire these positions. * * @param selectedSegment_1 * @param selectedSegment_2 * Can be <code>null</code> when only 1 segment is selected. * @param isMoveChartToShowSlider */ private void selectSegmenterSegments(final SegmenterSegment selectedSegment_1, final SegmenterSegment selectedSegment_2, final boolean isMoveChartToShowSlider) { // prevent selection from previous selection event _segmenterSelection = null; // get start/end index depending which segments are selected SegmenterSegment startSegment = selectedSegment_1; SegmenterSegment endSegment; if (selectedSegment_2 == null) { endSegment = startSegment; } else { endSegment = selectedSegment_2; } // depending how the segments are selected, start can be larger than the end if (startSegment.segmentIndex > endSegment.segmentIndex) { // switch segments final SegmenterSegment tempSegment = endSegment; endSegment = startSegment; startSegment = tempSegment; } final int xSliderSerieIndexLeft = startSegment.xSliderSerieIndexLeft; final int xSliderSerieIndexRight = endSegment.xSliderSerieIndexRight; final SelectionChartXSliderPosition selectionSliderPosition = new SelectionChartXSliderPosition(this, xSliderSerieIndexLeft, xSliderSerieIndexRight); /* * Extend default selection with the sement positions */ final SelectedTourSegmenterSegments selectedSegments = new SelectedTourSegmenterSegments(); selectedSegments.tourData = _tourData; selectedSegments.xSliderSerieIndexLeft = xSliderSerieIndexLeft; selectedSegments.xSliderSerieIndexRight = xSliderSerieIndexRight; selectionSliderPosition.setCustomData(selectedSegments); selectionSliderPosition.setMoveChartToShowSlider(isMoveChartToShowSlider); selectionSliderPosition.setCenterZoomPositionWithKey(true); /* * Set x slider position in the chart but do not fire an event because the event would be * fired separately for each slider :-( */ setXSliderPosition(selectionSliderPosition, false); // fire event for both x sliders TourManager.fireEventWithCustomData(// TourEventId.SLIDER_POSITION_CHANGED, selectionSliderPosition, _part); } private boolean selectTour(final ChartTitleSegment selectedTitleSegment) { // exclude which is currently not yet supported // if (!_tourData.isMultipleTours || !_isTourSegmenterVisible) { // return false; // } // // final long titleTourId = selectedTitleSegment.getTourId(); // // final int[] segmentSerieIndex = _tourData.segmentSerieIndex; // // if (_layerTourSegmenterAltitude != null) { // // final Long[] multipleTourIds = _tourData.multipleTourIds; // // for (int tourIndex = 0; tourIndex < multipleTourIds.length; tourIndex++) { // // final Long tourId = multipleTourIds[tourIndex]; // // if (titleTourId == tourId) { // // final int tourStartIndex = _tourData.multipleTourStartIndex[tourIndex]; // } // } // // } // // final int leftSerieIndex = selectedSegments.xSliderSerieIndexLeft; // final int rightSerieIndex = selectedSegments.xSliderSerieIndexRight; // // _selectedSegmenterSegment_1 = null; // _selectedSegmenterSegment_2 = null; // // for (final SegmenterSegment paintedLabel : paintedLabelsAltitude) { // // if (_selectedSegmenterSegment_1 == null && paintedLabel.serieIndex > leftSerieIndex) { // _selectedSegmenterSegment_1 = paintedLabel; // } // // if (_selectedSegmenterSegment_2 == null && paintedLabel.serieIndex >= rightSerieIndex) { // _selectedSegmenterSegment_2 = paintedLabel; // } // // if (_selectedSegmenterSegment_1 != null && _selectedSegmenterSegment_2 != null) { // break; // } // } // // // redraw chart // setSelectedLines(true); // _isRecomputeLineSelection = true; return true; } void selectXSliders(final SelectionChartXSliderPosition xSliderPosition) { // set position for the x-sliders setXSliderPosition(xSliderPosition); final Object customData = xSliderPosition.getCustomData(); if (customData instanceof SelectedTourSegmenterSegments) { final SelectedTourSegmenterSegments selectedSegments = (SelectedTourSegmenterSegments) customData; // select segments if (_isTourSegmenterVisible) { selectXSliders_Segments(selectedSegments); } } else { /* * Select tour marker */ if (_layerMarker != null) { final int leftSliderValueIndex = xSliderPosition.getLeftSliderValueIndex(); if (leftSliderValueIndex != SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION) { // check if a marker is selected for (final TourMarker tourMarker : _tourData.getTourMarkers()) { if (tourMarker.getSerieIndex() == leftSliderValueIndex) { // marker is selected, get marker from chart label _selectedTourMarker = tourMarker; // redraw chart setChartOverlayDirty(); break; } } } } } } private void selectXSliders_Segments(final SelectedTourSegmenterSegments selectedSegments) { if (_layerTourSegmenterAltitude == null) { // is not fully initialized return; } // keep selection which is needed when the chart is resized and more/less/other segments are displayed _segmenterSelection = selectedSegments; _selectedSegmenterSegment_1 = null; _selectedSegmenterSegment_2 = null; ArrayList<SegmenterSegment> paintedSegmentLabels = _layerTourSegmenterAltitude.getPaintedSegments(); if (paintedSegmentLabels.size() == 0) { for (final ArrayList<SegmenterSegment> paintedLabels : _layerTourSegmenterOther.getPaintedSegments()) { if (paintedLabels.size() > 0) { paintedSegmentLabels = paintedLabels; break; } } } if (paintedSegmentLabels.size() == 0) { // this case do propably not occure; return; } final int leftSerieIndex = selectedSegments.xSliderSerieIndexLeft; final int rightSerieIndex = selectedSegments.xSliderSerieIndexRight; for (final SegmenterSegment segmentSegment : paintedSegmentLabels) { if (_selectedSegmenterSegment_1 == null && segmentSegment.serieIndex > leftSerieIndex) { _selectedSegmenterSegment_1 = segmentSegment; } if (_selectedSegmenterSegment_2 == null && segmentSegment.serieIndex >= rightSerieIndex) { _selectedSegmenterSegment_2 = segmentSegment; } if (_selectedSegmenterSegment_1 != null && _selectedSegmenterSegment_2 != null) { /* * Prevent that both segments have the same index, the 2nd segment is used for * multiple selection, otherwise the selection with the mouse is a bit strange. */ if (_selectedSegmenterSegment_1.serieIndex == _selectedSegmenterSegment_2.serieIndex) { _selectedSegmenterSegment_2 = null; // } else { } break; } } // redraw chart _isRecomputeLineSelection = true; setSelectedLines(true); } /** * Set the check state for a command and update the UI * * @param commandId * @param isChecked */ public void setActionChecked(final String commandId, final Boolean isChecked) { _allTourChartActions.get(commandId).setChecked(isChecked); } /** * Set the enable state for a command and update the UI */ public void setActionEnabled(final String commandId, final boolean isEnabled) { final Action action = _allTourChartActions.get(commandId); if (action != null) { action.setEnabled(isEnabled); } } /** * Set the enable/check state for a command and update the UI */ public void setActionState(final String commandId, final boolean isEnabled, final boolean isChecked) { final Action action = _allTourChartActions.get(commandId); if (action != null) { action.setEnabled(isEnabled); action.setChecked(isChecked); } } public void setCanShowTourSegments(final boolean canShowTourSegments) { _canShowTourSegments = canShowTourSegments; } /** * When a tour chart is opened in a dialog, some actions should not be done. * * @param isDisplayedInDialog */ public void setIsDisplayedInDialog(final boolean isDisplayedInDialog) { _isDisplayedInDialog = isDisplayedInDialog; } /** * Set line selection dirty that it is recomputed the next time when painted. */ void setLineSelectionDirty() { _isRecomputeLineSelection = true; } /** * @param property * @param isChartModified * @param prefName_IsMaxEnabled * @param prefName_MaxValue * @param yDataInfoId * @param valueDivisor * @param maxAdjustment * Is disabled when set to {@link Double#MIN_VALUE}. * @param isMinMaxEnabled * @return */ private boolean setMaxDefaultValue(final String property, boolean isChartModified, final String prefName_IsMaxEnabled, final String prefName_MaxValue, final int yDataInfoId, final int valueDivisor, final double maxAdjustment, final boolean isMinMaxEnabled) { boolean isAllMinMaxModified = false; if (property.equals(ITourbookPreferences.GRAPH_IS_MIN_MAX_ENABLED)) { // global min/max value is set isAllMinMaxModified = true; } if (isMinMaxEnabled) { if (isAllMinMaxModified || property.equals(prefName_IsMaxEnabled) || property.equals(prefName_MaxValue)) { final ChartDataYSerie yData = getYData(yDataInfoId); if (yData != null) { final boolean isMaxEnabled = _prefStore.getBoolean(prefName_IsMaxEnabled); if (isMinMaxEnabled && isMaxEnabled) { // set visible max value from the preferences double maxValue = _prefStore.getInt(prefName_MaxValue); if (maxAdjustment != Double.MIN_VALUE) { maxValue = maxValue > 0 // ? maxValue - maxAdjustment : maxValue + maxAdjustment; } yData.setVisibleMaxValue(valueDivisor == 0 ? maxValue : maxValue * valueDivisor); } else { // reset visible max value to the original max value yData.setVisibleMaxValue(yData.getOriginalMaxValue()); } isChartModified = true; } } } else { // min/max is disabled, use default min value final ChartDataYSerie yData = getYData(yDataInfoId); if (yData != null) { // reset visible max value to the original max value yData.setVisibleMaxValue(yData.getOriginalMaxValue()); isChartModified = true; } } return isChartModified; } /** * @param property * @param isChartModified * @param prefName_IsMinEnabled * @param prefName_MinValue * @param yDataInfoId * @param valueDivisor * @param minAdjustment * Is disabled when set to {@link Double#MIN_VALUE}. * @param isMinMaxEnabled * @return */ private boolean setMinDefaultValue(final String property, boolean isChartModified, final String prefName_IsMinEnabled, final String prefName_MinValue, final int yDataInfoId, final int valueDivisor, final double minAdjustment, final boolean isMinMaxEnabled) { boolean isAllMinMaxModified = false; if (property.equals(ITourbookPreferences.GRAPH_IS_MIN_MAX_ENABLED)) { // global min/max value is set isAllMinMaxModified = true; } if (isMinMaxEnabled) { if (isAllMinMaxModified || property.equals(prefName_IsMinEnabled) || property.equals(prefName_MinValue)) { final ChartDataYSerie yData = getYData(yDataInfoId); if (yData != null) { final boolean isMinEnabled = _prefStore.getBoolean(prefName_IsMinEnabled); if (isMinMaxEnabled && isMinEnabled) { // set visible min value from the preferences double minValue = _prefStore.getInt(prefName_MinValue); if (minAdjustment != Double.MIN_VALUE) { minValue += minAdjustment; } yData.setVisibleMinValue(valueDivisor == 0 ? minValue : minValue * valueDivisor); } else { // reset visible min value to the original min value yData.setVisibleMinValue(yData.getOriginalMinValue()); } isChartModified = true; } } } else { // min/max is disabled, use default min value final ChartDataYSerie yData = getYData(yDataInfoId); if (yData != null) { // reset visible min value to the original min value yData.setVisibleMinValue(yData.getOriginalMinValue()); isChartModified = true; } } return isChartModified; } @Override public void setMouseMode(final boolean isChecked) { super.setMouseMode(isChecked); enableZoomOptions(); } @Override public void setMouseMode(final Object newMouseMode) { super.setMouseMode(newMouseMode); enableZoomOptions(); } /** * Enable or disable the edit actions in the tour info tooltip, by default the edit actions are * disabled. * * @param isEnabled */ public void setTourInfoActionsEnabled(final boolean isEnabled) { if (_tourInfoIconTooltipProvider != null) { _tourInfoIconTooltipProvider.setActionsEnabled(isEnabled); } } void setupChartConfig() { graphAntialiasing = _prefStore.getBoolean(// ITourbookPreferences.GRAPH_ANTIALIASING) ? SWT.ON : SWT.OFF; isShowSegmentAlternateColor = _prefStore.getBoolean(// ITourbookPreferences.GRAPH_IS_SEGMENT_ALTERNATE_COLOR); segmentAlternateColor = PreferenceConverter.getColor(_prefStore, // ITourbookPreferences.GRAPH_SEGMENT_ALTERNATE_COLOR); graphTransparencyLine = _prefStore.getInt(// ITourbookPreferences.GRAPH_TRANSPARENCY_LINE); graphTransparencyFilling = _prefStore.getInt(// ITourbookPreferences.GRAPH_TRANSPARENCY_FILLING); isShowHorizontalGridLines = Util.getPrefixPrefBoolean(_prefStore, GRID_PREF_PREFIX, ITourbookPreferences.CHART_GRID_IS_SHOW_HORIZONTAL_GRIDLINES); isShowVerticalGridLines = Util.getPrefixPrefBoolean(_prefStore, GRID_PREF_PREFIX, ITourbookPreferences.CHART_GRID_IS_SHOW_VERTICAL_GRIDLINES); gridVerticalDistance = Util.getPrefixPrefInt(// _prefStore, GRID_PREF_PREFIX, ITourbookPreferences.CHART_GRID_VERTICAL_DISTANCE); gridHorizontalDistance = Util.getPrefixPrefInt(// _prefStore, GRID_PREF_PREFIX, ITourbookPreferences.CHART_GRID_HORIZONTAL_DISTANCE); } private void setupChartSegmentTitle() { final ChartTitleSegmentConfig ctsConfig = getChartTitleSegmentConfig(); if (_tcc.isTourInfoVisible) { // show tour info addChartMouseListener(_mouseSegmentTitle_Listener); addChartMouseMoveListener(_mouseSegmentTitle_MoveListener); ctsConfig.isShowSegmentBackground = true; ctsConfig.isShowSegmentSeparator = _tcc.isShowInfoTourSeparator; ctsConfig.isShowSegmentTitle = _tcc.isShowInfoTitle; } else { // hide tour info removeChartMouseListener(_mouseSegmentTitle_Listener); removeChartMouseMoveListener(_mouseSegmentTitle_MoveListener); ctsConfig.isShowSegmentBackground = false; ctsConfig.isShowSegmentSeparator = false; ctsConfig.isShowSegmentTitle = false; } ctsConfig.isMultipleSegments = _tourData.isMultipleTours(); _tourTitleTooltip.setFadeInDelayTime(_tcc.tourInfoTooltipDelay); } /** * set custom data for all graphs */ private void setupGraphLayer() { final ChartDataModel dataModel = getChartDataModel(); if (dataModel == null) { return; } /* * the tour markers are displayed in the altitude graph, when this graph is not available, * the markers are painted in one of the other graphs */ ChartDataYSerie yDataWithLabels = (ChartDataYSerie) dataModel .getCustomData(TourManager.CUSTOM_DATA_ALTITUDE); if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_PULSE); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_SPEED); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_PACE); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_POWER); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_GRADIENT); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_ALTIMETER); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_TEMPERATURE); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_CADENCE); } if (yDataWithLabels == null) { yDataWithLabels = (ChartDataYSerie) dataModel.getCustomData(TourManager.CUSTOM_DATA_GEAR_RATIO); } ConfigGraphSegment cfgAltitude = null; ConfigGraphSegment cfgPulse = null; ConfigGraphSegment cfgSpeed = null; ConfigGraphSegment cfgPace = null; ConfigGraphSegment cfgPower = null; ConfigGraphSegment cfgGradient = null; ConfigGraphSegment cfgAltimeter = null; ConfigGraphSegment cfgCadence = null; /* * Setup tour segmenter data */ if (_isTourSegmenterVisible) { final IValueLabelProvider labelProviderInt = TourManager.getLabelProviderInt(); final IValueLabelProvider labelProviderMMSS = TourManager.getLabelProviderMMSS(); cfgAltitude = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_ALTITUDE); cfgAltitude.segmentDataSerie = _tourData.segmentSerie_Altitude_Diff; cfgAltitude.labelProvider = labelProviderInt; cfgAltitude.canHaveNegativeValues = true; cfgAltitude.minValueAdjustment = 0.1; cfgPulse = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_HEARTBEAT); cfgPulse.segmentDataSerie = _tourData.segmentSerie_Pulse; cfgPulse.labelProvider = null; cfgPulse.canHaveNegativeValues = false; cfgPulse.minValueAdjustment = Double.MIN_VALUE; cfgSpeed = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_SPEED); cfgSpeed.segmentDataSerie = _tourData.segmentSerie_Speed; cfgSpeed.labelProvider = null; cfgSpeed.canHaveNegativeValues = false; cfgSpeed.minValueAdjustment = Double.MIN_VALUE; cfgPace = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_PACE); cfgPace.segmentDataSerie = _tourData.segmentSerie_Pace; cfgPace.labelProvider = labelProviderMMSS; cfgPace.canHaveNegativeValues = false; cfgPace.minValueAdjustment = Double.MIN_VALUE; cfgPower = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_POWER); cfgPower.segmentDataSerie = _tourData.segmentSerie_Power; cfgPower.labelProvider = labelProviderInt; cfgPower.canHaveNegativeValues = false; cfgPower.minValueAdjustment = 1.0; cfgGradient = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_GRADIENT); cfgGradient.segmentDataSerie = _tourData.segmentSerie_Gradient; cfgGradient.labelProvider = null; cfgGradient.canHaveNegativeValues = true; cfgGradient.minValueAdjustment = 1.6; cfgAltimeter = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_ALTIMETER); cfgAltimeter.segmentDataSerie = _tourData.segmentSerie_Altitude_UpDown_Hour; cfgAltimeter.labelProvider = labelProviderInt; cfgAltimeter.canHaveNegativeValues = true; cfgAltimeter.minValueAdjustment = 1.0; cfgCadence = new ConfigGraphSegment(GraphColorManager.PREF_GRAPH_CADENCE); cfgCadence.segmentDataSerie = _tourData.segmentSerie_Cadence; cfgCadence.labelProvider = null; cfgCadence.canHaveNegativeValues = false; cfgCadence.minValueAdjustment = Double.MIN_VALUE; } setupGraphLayer_Layer(TourManager.CUSTOM_DATA_ALTIMETER, yDataWithLabels, cfgAltimeter); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_ALTITUDE, yDataWithLabels, cfgAltitude); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_CADENCE, yDataWithLabels, cfgCadence); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_GEAR_RATIO, yDataWithLabels, null); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_GRADIENT, yDataWithLabels, cfgGradient); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_HISTORY, null, null); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_PULSE, yDataWithLabels, cfgPulse); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_SPEED, yDataWithLabels, cfgSpeed); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_PACE, yDataWithLabels, cfgPace); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_POWER, yDataWithLabels, cfgPower); setupGraphLayer_Layer(TourManager.CUSTOM_DATA_TEMPERATURE, yDataWithLabels, null); } /** * Set data for each graph * * @param customDataKey * @param segmentDataSerie * @param yDataWithLabels */ private void setupGraphLayer_Layer(final String customDataKey, final ChartDataYSerie yDataWithLabels, final ConfigGraphSegment segmentConfig) { final ChartDataModel dataModel = getChartDataModel(); final ChartDataYSerie yData = (ChartDataYSerie) dataModel.getCustomData(customDataKey); if (yData == null) { return; } final ArrayList<IChartLayer> customFgLayers = new ArrayList<IChartLayer>(); /** * Sequence of the graph layer is the z-order, last added layer is on the top */ /* * marker layer */ // show label layer only for ONE visible graph if (_layerMarker != null && yData == yDataWithLabels) { customFgLayers.add(_layerMarker); } /* * photo layer */ // show photo layer only for ONE visible graph if (_layerPhoto != null && _tcc.isShowTourPhotos == true && (yData == yDataWithLabels || dataModel.getChartType() == ChartType.HISTORY)) { customFgLayers.add(_layerPhoto); } /* * Tour segmenter layer */ final ChartDataYSerie yDataAltitude = (ChartDataYSerie) dataModel .getCustomData(TourManager.CUSTOM_DATA_ALTITUDE); if (yData == yDataAltitude) { if (_layerTourSegmenterAltitude != null) { customFgLayers.add(_layerTourSegmenterAltitude); } } else { if (_layerTourSegmenterOther != null) { customFgLayers.add(_layerTourSegmenterOther); } } /* * display merge layer only together with the altitude graph */ if ((_layer2ndAltiSerie != null) && customDataKey.equals(TourManager.CUSTOM_DATA_ALTITUDE)) { customFgLayers.add(_layer2ndAltiSerie); } /* * HR zone painter */ if (_hrZonePainter != null) { yData.setCustomFillPainter(_hrZonePainter); } // set custom layers, no layers are set when layer list is empty yData.setCustomForegroundLayers(customFgLayers); // set segment data series if (segmentConfig != null) { yData.setCustomData(TourManager.CUSTOM_DATA_SEGMENT_VALUES, segmentConfig); } } private void setupSegmenterValueFont() { if (_segmenterValueFont != null) { _segmenterValueFont.dispose(); } final FontData[] valueFontData = PreferenceConverter.getFontDataArray(// _prefStore, ITourbookPreferences.TOUR_SEGMENTER_CHART_VALUE_FONT); _segmenterValueFont = new Font(getDisplay(), valueFontData); } /** * Add/removes the toursegmenter mouse listeners and sets's the state * {@link #_isTourSegmenterVisible} if tour segmenter is visible or not. */ private void setupTourSegmenter() { final boolean isSegmenterActive = Util.getStateBoolean(// _tourSegmenterState, TourSegmenterView.STATE_IS_SEGMENTER_ACTIVE, false); final boolean isShowTourSegments = Util.getStateBoolean(_tourSegmenterState, TourSegmenterView.STATE_IS_SHOW_TOUR_SEGMENTS, TourSegmenterView.STATE_IS_SHOW_TOUR_SEGMENTS_DEFAULT); _isShowSegmenterTooltip = Util.getStateBoolean(_tourSegmenterState, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_TOOLTIP, TourSegmenterView.STATE_IS_SHOW_SEGMENTER_TOOLTIP_DEFAULT); _isTourSegmenterVisible = _canShowTourSegments && isSegmenterActive && isShowTourSegments; if (_isTourSegmenterVisible) { addChartMouseListener(_mouseSegmentLabel_Listener); addChartMouseMoveListener(_mouseSegmentLabel_MoveListener); addChartKeyListener(_chartKeyListener); } else { if (_layerTourSegmenterAltitude != null) { // disable segment layers removeChartOverlay(_layerTourSegmenterAltitude); _layerTourSegmenterAltitude = null; _layerTourSegmenterOther = null; } removeChartMouseListener(_mouseSegmentLabel_Listener); removeChartMouseMoveListener(_mouseSegmentLabel_MoveListener); removeChartKeyListener(_chartKeyListener); } } /** * set's the chart which is synched with this chart * * @param isSynchEnabled * <code>true</code> to enable synch, <code>false</code> to disable synch * @param synchedChart * contains the {@link Chart} which is synched with this chart * @param synchMode */ public void synchChart(final boolean isSynchEnabled, final TourChart synchedChart, final int synchMode) { // enable/disable synched chart super.setSynchedChart(isSynchEnabled ? synchedChart : null); final Map<String, Action> synchChartActions = synchedChart._allTourChartActions; if (synchChartActions == null) { return; } synchedChart.setSynchMode(synchMode); /* * when the position listener is set, the zoom actions will be deactivated */ if (isSynchEnabled) { // synchronize this chart with the synchedChart // disable zoom actions synchedChart.setZoomActionsEnabled(false); synchedChart.updateZoomOptions(false); // set the synched chart to auto-zoom synchedChart.setCanAutoZoomToSlider(true); // hide the x-sliders // fBackupIsXSliderVisible = synchedChart.isXSliderVisible(); synchedChart.setShowSlider(false); synchronizeChart(); } else { // disable chart synchronization // enable zoom action // actionProxies.get(COMMAND_ID_CAN_SCROLL_CHART).setChecked(synchedChart.getCanScrollZoomedChart()); synchChartActions.get(ACTION_ID_CAN_AUTO_ZOOM_TO_SLIDER) .setChecked(synchedChart.getCanAutoZoomToSlider()); synchedChart.setZoomActionsEnabled(true); synchedChart.updateZoomOptions(true); // restore the x-sliders synchedChart.setShowSlider(true); synchedChart.setSynchConfig(null); // show whole chart synchedChart.getChartDataModel().resetMinMaxValues(); // synchedChart.onExecuteZoomOut(true); synchedChart.onExecuteZoomFitGraph(); } } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(this.getClass().getSimpleName()); sb.append(UI.NEW_LINE); sb.append(_tourData); sb.append(UI.NEW_LINE); return sb.toString(); } @Override public void updateChart(final ChartDataModel chartDataModel, final boolean isShowAllData) { super.updateChart(chartDataModel, isShowAllData); if (chartDataModel == null) { _tourData = null; _tcc = null; _valuePointTooltip.setTourData(null); // disable all actions if (_allTourChartActions != null) { for (final Action action : _allTourChartActions.values()) { action.setEnabled(false); } } if (_actionTourInfo != null) { _actionTourInfo.setEnabled(false); } if (_actionTourMarker != null) { _actionTourMarker.setEnabled(false); } } } public void updateLayer2ndAlti(final I2ndAltiLayer alti2ndLayerProvider, final boolean isLayerVisible) { _is2ndAltiLayerVisible = isLayerVisible; _layer2ndAlti = alti2ndLayerProvider; createLayer_2ndAlti(); setupGraphLayer(); updateCustomLayers(); } @Override public void updateModifiedTourMarker(final TourMarker tourMarker) { if (_isDisplayedInDialog) { // this will update the chart fireTourMarkerModifyEvent(tourMarker, false); } else { // tour will be saved and the chart is also updated fireTourModifyEvent_Globally(); } } private void updatePhotoAction() { String toolTip; ImageDescriptor imageDescriptor; final boolean isShowPhotos = _tcc.isShowTourPhotos; final boolean isShowTooltip = _tcc.isShowTourPhotoTooltip; if (isShowPhotos && isShowTooltip) { toolTip = Messages.Tour_Action_TourPhotosWithTooltip_Tooltip; imageDescriptor = _imagePhotoTooltip; } else if (!isShowPhotos && !isShowTooltip) { toolTip = Messages.Tour_Action_TourPhotos; imageDescriptor = _imagePhoto; } else { toolTip = Messages.Tour_Action_TourPhotosWithoutTooltip_Tooltip; imageDescriptor = _imagePhoto; } final Action action = _allTourChartActions.get(ACTION_ID_IS_SHOW_TOUR_PHOTOS); action.setToolTipText(toolTip); action.setImageDescriptor(imageDescriptor); action.setChecked(isShowPhotos); } /** * Check and enable the graph actions, the visible state of a graph is defined in the tour chart * config. */ public void updateTourActions() { /* * all graph actions */ final int[] allGraphIds = TourManager.getAllGraphIDs(); final ArrayList<Integer> visibleGraphIds = _tcc.getVisibleGraphs(); final ArrayList<Integer> enabledGraphIds = new ArrayList<Integer>(); // get all graph ids which can be displayed for (final ChartDataSerie xyDataIterator : getChartDataModel().getXyData()) { if (xyDataIterator instanceof ChartDataYSerie) { final ChartDataYSerie yData = (ChartDataYSerie) xyDataIterator; final Integer graphId = (Integer) yData.getCustomData(ChartDataYSerie.YDATA_INFO); enabledGraphIds.add(graphId); } } Action tourAction; for (final int graphId : allGraphIds) { tourAction = _allTourChartActions.get(getGraphActionId(graphId)); tourAction.setChecked(visibleGraphIds.contains(graphId)); tourAction.setEnabled(enabledGraphIds.contains(graphId)); } /* * HR zones */ final boolean canShowHrZones = _tcc.canShowHrZones; final String currentHrZoneStyle = _tcc.hrZoneStyle; tourAction = _allTourChartActions.get(ACTION_ID_HR_ZONE_DROPDOWN_MENU); tourAction.setEnabled(canShowHrZones); tourAction = _allTourChartActions.get(ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP); tourAction.setEnabled(true); tourAction.setChecked(currentHrZoneStyle.equals(ACTION_ID_HR_ZONE_STYLE_GRAPH_TOP)); tourAction = _allTourChartActions.get(ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT); tourAction.setEnabled(true); tourAction.setChecked(currentHrZoneStyle.equals(ACTION_ID_HR_ZONE_STYLE_NO_GRADIENT)); tourAction = _allTourChartActions.get(ACTION_ID_HR_ZONE_STYLE_WHITE_TOP); tourAction.setEnabled(true); tourAction.setChecked(currentHrZoneStyle.equals(ACTION_ID_HR_ZONE_STYLE_WHITE_TOP)); tourAction = _allTourChartActions.get(ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM); tourAction.setEnabled(true); tourAction.setChecked(currentHrZoneStyle.equals(ACTION_ID_HR_ZONE_STYLE_WHITE_BOTTOM)); /* * Tour infos */ _actionTourInfo.setSelected(_tcc.isTourInfoVisible); _actionTourInfo.setEnabled(true); /* * Tour marker */ _actionTourMarker.setSelected(_tcc.isShowTourMarker); _actionTourMarker.setEnabled(true); /* * Tour photos */ tourAction = _allTourChartActions.get(ACTION_ID_IS_SHOW_TOUR_PHOTOS); tourAction.setEnabled(true); updatePhotoAction(); /* * Overlapped graphs */ tourAction = _allTourChartActions.get(ACTION_ID_IS_GRAPH_OVERLAPPED); tourAction.setEnabled(true); tourAction.setChecked(_tcc.isGraphOverlapped); /* * x-axis time/distance */ final boolean isShowTimeOnXAxis = _tcc.isShowTimeOnXAxis; tourAction = _allTourChartActions.get(ACTION_ID_X_AXIS_TIME); tourAction.setEnabled(true); // time data are always available tourAction.setChecked(isShowTimeOnXAxis); tourAction = _allTourChartActions.get(ACTION_ID_X_AXIS_DISTANCE); tourAction.setChecked(!isShowTimeOnXAxis); tourAction.setEnabled(!_tcc.isForceTimeOnXAxis); // get options check status from the configuration final boolean isMoveSlidersWhenZoomed = _tcc.moveSlidersWhenZoomed; final boolean isAutoZoomToSlider = _tcc.autoZoomToSlider; final boolean canAutoZoom = getMouseMode().equals(Chart.MOUSE_MODE_ZOOM); // update tour chart actions tourAction = _allTourChartActions.get(ACTION_ID_CAN_AUTO_ZOOM_TO_SLIDER); tourAction.setEnabled(true); tourAction.setChecked(isAutoZoomToSlider); tourAction = _allTourChartActions.get(ACTION_ID_CAN_MOVE_SLIDERS_WHEN_ZOOMED); tourAction.setEnabled(canAutoZoom); tourAction.setChecked(isMoveSlidersWhenZoomed); // update the chart actions setCanAutoMoveSliders(isMoveSlidersWhenZoomed); setCanAutoZoomToSlider(isAutoZoomToSlider); tourAction = _allTourChartActions.get(ACTION_ID_EDIT_CHART_PREFERENCES); tourAction.setEnabled(true); } /** * Update the tour chart with the previous data, configuration and min/max values. */ public void updateTourChart() { updateTourChartInternal(_tourData, _tcc, true, false); } /** * Update the tour chart with the previous data and configuration. * * @param keepMinMaxValues * <code>true</code> keeps the min/max values from the previous chart */ public void updateTourChart(final boolean keepMinMaxValues) { updateTourChartInternal(_tourData, _tcc, keepMinMaxValues, false); } /** * Update the tour chart with the previous data and configuration * * @param keepMinMaxValues * <code>true</code> keeps the min/max values from the previous chart * @param isPropertyChanged * when <code>true</code> the properties for the tour chart have changed */ public void updateTourChart(final boolean keepMinMaxValues, final boolean isPropertyChanged) { updateTourChartInternal(_tourData, _tcc, keepMinMaxValues, isPropertyChanged); } public void updateTourChart(final TourData tourData, final boolean keepMinMaxValues) { updateTourChartInternal(tourData, _tcc, keepMinMaxValues, false); } /** * Set {@link TourData} and {@link TourChartConfiguration} to create a new chart data model * * @param tourData * @param tourChartConfig * @param keepMinMaxValues * <code>true</code> keeps the min/max values from the previous chart */ public void updateTourChart(final TourData tourData, final TourChartConfiguration tourChartConfig, final boolean keepMinMaxValues) { updateTourChartInternal(tourData, tourChartConfig, keepMinMaxValues, false); } /** * This is the entry point for new tours. * <p> * This method is synchronized because when SRTM data are retrieved and the import view is * openened, the error occured that the chart config was deleted with {@link #updateChart(null)} * * @param newTourData * @param newTCC * @param keepMinMaxValues * @param isPropertyChanged */ private synchronized void updateTourChartInternal(final TourData newTourData, final TourChartConfiguration newTCC, final boolean keepMinMaxValues, final boolean isPropertyChanged) { if (newTourData == null || newTCC == null) { // there are no new tour data _valuePointTooltip.setTourData(null); return; } // keep min/max values for the 'old' chart in the chart config if (_tcc != null && keepMinMaxValues) { final ChartYDataMinMaxKeeper oldMinMaxKeeper = _tcc.getMinMaxKeeper(); if (oldMinMaxKeeper != null) { oldMinMaxKeeper.saveMinMaxValues(getChartDataModel()); } } // set current tour data and chart config to new values _tourData = newTourData; _tcc = newTCC; /* * Cleanup old data */ _selectedTourMarker = null; hidePhotoLayer(); resetSegmenterSelection(); final ChartDataModel newChartDataModel = TourManager.getInstance().createChartDataModel(// _tourData, _tcc, isPropertyChanged); // set the model BEFORE actions are created/enabled/checked setDataModel(newChartDataModel); // create actions createActions(); fillToolbar(); updateTourActions(); // restore min/max values from the chart config final ChartYDataMinMaxKeeper newMinMaxKeeper = _tcc.getMinMaxKeeper(); final boolean isMinMaxKeeper = (newMinMaxKeeper != null) && keepMinMaxValues; if (isMinMaxKeeper) { newMinMaxKeeper.setMinMaxValues(newChartDataModel); } if (_chartDataModelListener != null) { _chartDataModelListener.dataModelChanged(newChartDataModel); } createLayer_TourSegmenter(); createLayer_Marker(false); createLayer_2ndAlti(); createLayer_Photo(); createPainter_HrZone(); setupGraphLayer(); setupChartSegmentTitle(); updateChart(newChartDataModel, !isMinMaxKeeper); /* * this must be done after the chart is created because is sets an action, set it only once * when the chart is displayed the first time otherwise it's annoying */ if (_isMouseModeSet == false) { _isMouseModeSet = true; setMouseMode( _prefStore.getString(ITourbookPreferences.GRAPH_MOUSE_MODE).equals(Chart.MOUSE_MODE_SLIDER)); } _tourInfoIconTooltipProvider.setTourData(_tourData); _valuePointTooltip.setTourData(_tourData); _tourMarkerTooltip.setIsShowMarkerActions(_tourData.isMultipleTours() == false); } /** * Toursegmenter is modified, update its layers. * * @param tourSegmenterView */ public void updateTourSegmenter() { if (_tourData == null) { return; } resetSegmenterSelection(); setupTourSegmenter(); if (_isTourSegmenterVisible) { // tour segmenter is visible createLayer_TourSegmenter(); } else { // tour segmenter is hidden setSelectedLines(false); resetGraphAlpha(); } setupGraphLayer(); updateCustomLayers(); redrawChart(); } private void updateUI_Marker(final Boolean isMarkerVisible) { _tcc.isShowTourMarker = isMarkerVisible; updateUI_MarkerLayer(isMarkerVisible); // update actions _actionTourMarker.setSelected(isMarkerVisible); } void updateUI_MarkerLayer() { // hide hovered segment // this is NOT working // onSegmenterSegment_MouseExit(); updateUI_MarkerLayer(true); } /** * Updates the marker layer in the chart * * @param isMarkerVisible */ public void updateUI_MarkerLayer(final boolean isMarkerVisible) { // create/hide marker layer if (isMarkerVisible) { createLayer_Marker(true); } else { hideMarkerLayer(); } setupGraphLayer(); // update marker layer updateCustomLayers(); } void updateUI_TourTitleInfo() { setupChartSegmentTitle(); updateTourChart(); } /** * Update UI check state, the chart decides if the scroll/auto zoom options are available */ void updateZoomOptionActionHandlers() { setActionChecked(ACTION_ID_CAN_AUTO_ZOOM_TO_SLIDER, getCanAutoZoomToSlider()); setActionChecked(ACTION_ID_CAN_MOVE_SLIDERS_WHEN_ZOOMED, getCanAutoMoveSliders()); } /** * Enable/disable the zoom options in the tour chart * * @param isEnabled */ private void updateZoomOptions(final boolean isEnabled) { setActionEnabled(ACTION_ID_CAN_AUTO_ZOOM_TO_SLIDER, isEnabled); setActionEnabled(ACTION_ID_CAN_MOVE_SLIDERS_WHEN_ZOOMED, isEnabled); } }