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.views.tourDataEditor; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.text.NumberFormat; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import net.sf.swtaddons.autocomplete.combo.AutocompleteComboInput; import net.tourbook.Messages; import net.tourbook.application.TourbookPlugin; import net.tourbook.chart.Chart; import net.tourbook.chart.ChartDataModel; import net.tourbook.chart.SelectionChartInfo; import net.tourbook.chart.SelectionChartXSliderPosition; import net.tourbook.common.CommonActivator; import net.tourbook.common.UI; import net.tourbook.common.action.ActionOpenPrefDialog; import net.tourbook.common.preferences.ICommonPreferences; import net.tourbook.common.time.TimeTools; import net.tourbook.common.time.TimeZoneData; import net.tourbook.common.tooltip.ActionToolbarSlideout; import net.tourbook.common.tooltip.ToolbarSlideout; import net.tourbook.common.util.ColumnDefinition; import net.tourbook.common.util.ColumnManager; import net.tourbook.common.util.ITourViewer2; import net.tourbook.common.util.PostSelectionProvider; import net.tourbook.common.util.StatusUtil; import net.tourbook.common.util.Util; import net.tourbook.common.weather.IWeather; import net.tourbook.data.TourData; import net.tourbook.data.TourMarker; import net.tourbook.data.TourPerson; import net.tourbook.data.TourReference; import net.tourbook.data.TourTag; import net.tourbook.data.TourType; import net.tourbook.database.MyTourbookException; import net.tourbook.database.TourDatabase; import net.tourbook.extension.export.ActionExport; import net.tourbook.importdata.RawDataManager; import net.tourbook.map2.view.SelectionMapPosition; import net.tourbook.preferences.ITourbookPreferences; import net.tourbook.tag.TagMenuManager; import net.tourbook.tour.ActionOpenAdjustAltitudeDialog; import net.tourbook.tour.ActionOpenMarkerDialog; import net.tourbook.tour.ITourEventListener; import net.tourbook.tour.ITourSaveListener; import net.tourbook.tour.SelectionDeletedTours; import net.tourbook.tour.SelectionTourData; import net.tourbook.tour.SelectionTourId; import net.tourbook.tour.SelectionTourIds; import net.tourbook.tour.SelectionTourMarker; import net.tourbook.tour.TourEvent; import net.tourbook.tour.TourEventId; import net.tourbook.tour.TourManager; import net.tourbook.ui.ITourProvider2; import net.tourbook.ui.MessageManager; import net.tourbook.ui.TableColumnFactory; import net.tourbook.ui.action.ActionExtractTour; import net.tourbook.ui.action.ActionModifyColumns; import net.tourbook.ui.action.ActionSetTourTypeMenu; import net.tourbook.ui.action.ActionSplitTour; import net.tourbook.ui.tourChart.ChartLabel; import net.tourbook.ui.tourChart.TourChart; import net.tourbook.ui.views.tourCatalog.SelectionTourCatalogView; import net.tourbook.ui.views.tourCatalog.TVICatalogComparedTour; import net.tourbook.ui.views.tourCatalog.TVICatalogRefTourItem; import net.tourbook.ui.views.tourCatalog.TVICompareResultComparedTour; import org.eclipse.core.databinding.conversion.StringToNumberConverter; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MenuAdapter; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseWheelListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.DateTime; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Spinner; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.ISaveablePart2; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.PartInitException; import org.eclipse.ui.forms.events.ExpansionAdapter; import org.eclipse.ui.forms.events.ExpansionEvent; import org.eclipse.ui.forms.widgets.Form; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.ui.part.PageBook; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.progress.UIJob; // author: Wolfgang Schramm // create: 24.08.2007 /** * This editor can edit (when all is implemented) all data for a tour */ public class TourDataEditorView extends ViewPart implements ISaveablePart2, ITourViewer2, ITourProvider2 { public static final String ID = "net.tourbook.views.TourDataEditorView"; //$NON-NLS-1$ // private static final String GRAPH_LABEL_HEARTBEAT_UNIT = net.tourbook.common.Messages.Graph_Label_Heartbeat_Unit; private static final String VALUE_UNIT_K_CALORIES = net.tourbook.ui.Messages.Value_Unit_KCalories; // // private static final int COLUMN_SPACING = 20; // private static final String WIDGET_KEY = "widgetKey"; //$NON-NLS-1$ private static final String WIDGET_KEY_TOURDISTANCE = "tourDistance"; //$NON-NLS-1$ private static final String WIDGET_KEY_ALTITUDE_UP = "altitudeUp"; //$NON-NLS-1$ private static final String WIDGET_KEY_ALTITUDE_DOWN = "altitudeDown"; //$NON-NLS-1$ private static final String WIDGET_KEY_PERSON = "tourPerson"; //$NON-NLS-1$ // private static final String MESSAGE_KEY_ANOTHER_SELECTION = "anotherSelection"; //$NON-NLS-1$ /** * shows the busy indicator to load the slice viewer when there are more items as this value */ private static final int BUSY_INDICATOR_ITEMS = 5000; // private static final String STATE_SELECTED_TAB = "tourDataEditor.selectedTab"; //$NON-NLS-1$ private static final String STATE_ROW_EDIT_MODE = "tourDataEditor.rowEditMode"; //$NON-NLS-1$ private static final String STATE_IS_EDIT_MODE = "tourDataEditor.isEditMode"; //$NON-NLS-1$ private static final String STATE_CSV_EXPORT_PATH = "tourDataEditor.csvExportPath"; //$NON-NLS-1$ // private static final String STATE_SECTION_CHARACTERISTICS = "STATE_SECTION_CHARACTERISTICS"; //$NON-NLS-1$ private static final String STATE_SECTION_DATE_TIME = "STATE_SECTION_DATE_TIME"; //$NON-NLS-1$ private static final String STATE_SECTION_INFO = "STATE_SECTION_INFO"; //$NON-NLS-1$ private static final String STATE_SECTION_PERSONAL = "STATE_SECTION_PERSONAL"; //$NON-NLS-1$ private static final String STATE_SECTION_TITLE = "STATE_SECTION_TITLE"; //$NON-NLS-1$ private static final String STATE_SECTION_WEATHER = "STATE_SECTION_WEATHER"; //$NON-NLS-1$ // static final String STATE_LAT_LON_DIGITS = "STATE_LAT_LON_DIGITS"; //$NON-NLS-1$ static final int DEFAULT_LAT_LON_DIGITS = 5; // private final IPreferenceStore _prefStore = TourbookPlugin.getPrefStore(); private final IPreferenceStore _prefStoreCommon = CommonActivator.getPrefStore(); private final IDialogSettings _state = TourbookPlugin.getState(ID); private final IDialogSettings _stateSlice = TourbookPlugin.getState(ID + ".slice"); //$NON-NLS-1$ // private final boolean _isOSX = net.tourbook.common.UI.IS_OSX; private final boolean _isLinux = net.tourbook.common.UI.IS_LINUX; // // /** * Tour start daytime in seconds */ private int _tourStartDayTime; // /* * Data series which are displayed in the viewer, all are metric system */ private int[] _serieTime; private float[] _serieDistance; private float[] _serieAltitude; private float[] _serieTemperature; private float[] _serieCadence; private float[] _serieGradient; private float[] _serieSpeed; private float[] _seriePace; private float[] _seriePower; private float[] _seriePulse; private double[] _serieLatitude; private double[] _serieLongitude; private float[][] _serieGears; private boolean[] _serieBreakTime; // private ColumnDefinition _colDefAltitude; private ColumnDefinition _colDefCadence; private ColumnDefinition _colDefPulse; private ColumnDefinition _colDefTemperature; private ColumnDefinition _colDefLatitude; private ColumnDefinition _colDefLongitude; // private MessageManager _messageManager; private PostSelectionProvider _postSelectionProvider; private ISelectionListener _postSelectionListener; private IPartListener2 _partListener; private IPropertyChangeListener _prefChangeListener; private IPropertyChangeListener _prefChangeListenerCommon; private ITourEventListener _tourEventListener; private ITourSaveListener _tourSaveListener; // private final NumberFormat _nf1 = NumberFormat.getNumberInstance(); private final NumberFormat _nf1NoGroup = NumberFormat.getNumberInstance(); private final NumberFormat _nf2 = NumberFormat.getNumberInstance(); private final NumberFormat _nf3 = NumberFormat.getNumberInstance(); private final NumberFormat _nf6 = NumberFormat.getNumberInstance(); private final NumberFormat _nf3NoGroup = NumberFormat.getNumberInstance(); { _nf1.setMinimumFractionDigits(1); _nf1.setMaximumFractionDigits(1); _nf2.setMinimumFractionDigits(2); _nf2.setMaximumFractionDigits(2); _nf3.setMinimumFractionDigits(3); _nf3.setMaximumFractionDigits(3); _nf6.setMinimumFractionDigits(6); _nf6.setMaximumFractionDigits(6); _nf1NoGroup.setMinimumFractionDigits(1); _nf1NoGroup.setMaximumFractionDigits(1); _nf1NoGroup.setGroupingUsed(false); _nf3NoGroup.setMinimumFractionDigits(3); _nf3NoGroup.setMaximumFractionDigits(3); _nf3NoGroup.setGroupingUsed(false); } // /** * <code>true</code>: rows can be selected in the viewer<br> * <code>false</code>: cell can be selected in the viewer */ private boolean _isRowEditMode = true; private boolean _isEditMode; private long _sliceViewerTourId = -1; private SelectionChartXSliderPosition _sliceViewerXSliderPosition; private boolean _isTourDirty = false; // /** * is <code>true</code> when the tour is currently being saved to prevent a modify event or the * onSelectionChanged event */ private boolean _isSavingInProgress = false; /** * when <code>true</code> data are loaded into fields */ private boolean _isSetField = false; /** * contains the tour id from the last selection event */ private Long _selectionTourId; // private KeyAdapter _keyListener; private ModifyListener _modifyListener; private ModifyListener _verifyFloatValue; private ModifyListener _verifyIntValue; private MouseWheelListener _mouseWheelListener; private SelectionAdapter _selectionListener; private SelectionAdapter _tourTimeListener; private SelectionAdapter _dateTimeListener; private PixelConverter _pc; // /** * this width is used as a hint for the width of the description field, this value also * influences the width of the columns in this editor */ private final int _hintTextColumnWidth = _isOSX ? 200 : 150; private int _hintValueFieldWidth; private int _hintDefaultSpinnerWidth; /** * is <code>true</code> when {@link #_tourChart} contains reference tours */ private boolean _isReferenceTourAvailable; /** * range for the reference tours, is <code>null</code> when reference tours are not available<br> * 1st index = ref tour<br> * 2nd index: 0:start, 1:end */ private int[][] _refTourRange; private boolean _isPartVisible = false; /** * when <code>true</code> additional info is displayed in the title area */ private boolean _isInfoInTitle; /** * is <code>true</code> when a cell editor is activ, otherwise <code>false</code> */ private boolean _isCellEditorActive = false; /** * every requested UI update increased this counter */ private int _uiUpdateCounter; /** * counter when the UI update runnable is run, this will optimize performance to not update the * UI when the part is hidden */ private int _uiRunnableCounter = 0; private int _uiUpdateTitleCounter = 0; private TourData _uiRunnableTourData; private boolean _uiRunnableForceTimeSliceReload; private boolean _uiRunnableIsDirtyDisabled; private SliceFloatEditingSupport _altitudeEditingSupport; private SliceFloatEditingSupport _pulseEditingSupport; private SliceFloatEditingSupport _temperatureEditingSupport; private SliceFloatEditingSupport _cadenceEditingSupport; private SliceDoubleEditingSupport _latitudeEditingSupport; private SliceDoubleEditingSupport _longitudeEditingSupport; private int _enableActionCounter = 0; /** * contains all markers with the data serie index as key */ private final HashMap<Integer, TourMarker> _markerMap = new HashMap<Integer, TourMarker>(); /** * When <code>true</code> the tour is created with the tour editor */ private boolean _isManualTour; private boolean _isTitleModified; private boolean _isAltitudeManuallyModified; private boolean _isDistManuallyModified; private boolean _isLocationStartModified; private boolean _isLocationEndModified; private boolean _isTimeZoneManuallyModified; private boolean _isTemperatureManuallyModified; private boolean _isWindSpeedManuallyModified; private boolean _isSetDigits = false; /* * measurement unit values */ private float _unitValueAltitude; private float _unitValueDistance; private int[] _unitValueWindSpeed; // pages private PageBook _pageBook; /* * actions */ private ActionComputeDistanceValues _actionComputeDistanceValues; private ActionCreateTour _actionCreateTour; private ActionCreateTourMarker _actionCreateTourMarker; private ActionCSVTimeSliceExport _actionCsvTimeSliceExport; private ActionDeleteDistanceValues _actionDeleteDistanceValues; private ActionDeleteTimeSlicesKeepTime _actionDeleteTimeSlicesKeepTime; private ActionDeleteTimeSlicesRemoveTime _actionDeleteTimeSlicesRemoveTime; private ActionExport _actionExportTour; private ActionExtractTour _actionExtractTour; private ActionModifyColumns _actionModifyColumns; private ActionOpenAdjustAltitudeDialog _actionOpenAdjustAltitudeDialog; private ActionOpenMarkerDialog _actionOpenMarkerDialog; private ActionOpenPrefDialog _actionOpenTourTypePrefs; private ActionSaveTour _actionSaveTour; private ActionSetStartDistanceTo0 _actionSetStartDistanceTo_0; private ActionSplitTour _actionSplitTour; private ActionToggleReadEditMode _actionToggleReadEditMode; private ActionToggleRowSelectMode _actionToggleRowSelectMode; private ActionUndoChanges _actionUndoChanges; private ActionViewSettings _actionViewSettings; private TagMenuManager _tagMenuMgr; /** * Number of digits for the lat/lon columns. */ private int _latLonDigits; private final NumberFormat _nfLatLon = NumberFormat.getNumberInstance(); // // ################################################## UI controls ################################################## // private Composite _pageNoData; private Form _pageEditorForm; private CTabFolder _tabFolder; private CTabItem _tabTour; private CTabItem _tabSlices; private CTabItem _tabInfo; /** * contains the controls which are displayed in the first column, these controls are used to get * the maximum width and set the first column within the differenct section to the same width */ private final ArrayList<Control> _firstColumnControls = new ArrayList<Control>(); private final ArrayList<Control> _firstColumnContainerControls = new ArrayList<Control>(); private final ArrayList<Control> _secondColumnControls = new ArrayList<Control>(); private TourChart _tourChart; private TourData _tourData; /* * UI controls */ private Composite _tourContainer; private ScrolledComposite _tab1Container; private ScrolledComposite _tab4Container; private Composite _infoContainer; private Composite _tab3Container; private Composite _sliceViewerContainer; private Section _sectionTitle; private Section _sectionDateTime; private Section _sectionPersonal; private Section _sectionWeather; private Section _sectionCharacteristics; private Section _sectionInfo; private Label _timeSliceLabel; private TableViewer _sliceViewer; private Object[] _sliceViewerItems; private ColumnManager _sliceColumnManager; private FormToolkit _tk; /* * tab: tour */ private Combo _comboTitle; // private Button _rdoCadence_Rpm; private Button _rdoCadence_Spm; // private CLabel _lblCloudIcon; private CLabel _lblTourType; // private ControlDecoration _decoTimeZone; // private Combo _comboClouds; private Combo _comboLocation_Start; private Combo _comboLocation_End; private Combo _comboTimeZone; private Combo _comboWindDirectionText; private Combo _comboWindSpeedText; // private DateTime _dtStartTime; private DateTime _dtTourDate; // private Label _lblAltitudeUpUnit; private Label _lblAltitudeDownUnit; private Label _lblDistanceUnit; private Label _lblSpeedUnit; private Label _lblStartTime; private Label _lblTags; private Label _lblTemperatureUnit; private Label _lblTimeZone; // private Link _linkDefaultTimeZone; private Link _linkGeoTimeZone; private Link _linkRemoveTimeZone; private Link _linkTag; private Link _linkTourType; // private Spinner _spinBodyWeight; private Spinner _spinCalories; private Spinner _spinFTP; private Spinner _spinRestPuls; private Spinner _spinTemperature; private Spinner _spinWindDirectionValue; private Spinner _spinWindSpeedValue; // private Text _txtAltitudeDown; private Text _txtAltitudeUp; private Text _txtDescription; private Text _txtDistance; private Text _txtWeather; // private TimeDuration _timeDriving; private TimeDuration _timePaused; private TimeDuration _timeRecording; // /* * tab: info */ private Text _txtDateTimeCreated; private Text _txtDateTimeModified; private Text _txtDeviceName; private Text _txtDeviceFirmwareVersion; private Text _txtDistanceSensor; private Text _txtImportFilePath; private Text _txtMergeFromTourId; private Text _txtMergeIntoTourId; private Text _txtPulseSensor; private Text _txtPowerSensor; private Text _txtPerson; private Text _txtRefTour; private Text _txtStrideSensor; private Text _txtTimeSlicesCount; private Text _txtTourId; private class ActionViewSettings extends ActionToolbarSlideout { @Override protected ToolbarSlideout createSlideout(final ToolBar toolbar) { return new SlideoutViewSettings(_pageBook, toolbar, _state, TourDataEditorView.this); } } private final class SliceDoubleEditingSupport extends EditingSupport { private final TextCellEditor __cellEditor; private double[] __dataSerie; private SliceDoubleEditingSupport(final TextCellEditor cellEditor, final double[] dataSerie) { super(_sliceViewer); __cellEditor = cellEditor; __dataSerie = dataSerie; } @Override protected boolean canEdit(final Object element) { if ((__dataSerie == null) || (isTourInDb() == false) || (_isEditMode == false)) { return false; } return true; } @Override protected CellEditor getCellEditor(final Object element) { return __cellEditor; } @Override protected Object getValue(final Object element) { return new Float(__dataSerie[((TimeSlice) element).serieIndex]).toString(); } public void setDataSerie(final double[] dataSerie) { __dataSerie = dataSerie; } @Override protected void setValue(final Object element, final Object value) { if (value instanceof String) { try { final double enteredValue = Double.parseDouble((String) value); final int serieIndex = ((TimeSlice) element).serieIndex; if (enteredValue != __dataSerie[serieIndex]) { // value has changed // update dataserie __dataSerie[serieIndex] = enteredValue; /* * worldposition has changed, this is an absolute overkill, wenn only one * position has changed */ _tourData.clearWorldPositions(); updateUI_AfterSliceEdit(); } } catch (final Exception e) { // ignore invalid characters } finally { } } } } private final class SliceFloatEditingSupport extends EditingSupport { private final TextCellEditor __cellEditor; private float[] __dataSerie; private SliceFloatEditingSupport(final TextCellEditor cellEditor, final float[] dataSerie) { super(_sliceViewer); __cellEditor = cellEditor; __dataSerie = dataSerie; } @Override protected boolean canEdit(final Object element) { if ((__dataSerie == null) || (isTourInDb() == false) || (_isEditMode == false)) { return false; } return true; } @Override protected CellEditor getCellEditor(final Object element) { return __cellEditor; } @Override protected Object getValue(final Object element) { final float metricValue = __dataSerie[((TimeSlice) element).serieIndex]; float displayedValue = metricValue; /* * convert current measurement system into metric */ if (__dataSerie == _serieAltitude) { if (_unitValueAltitude != 1) { // none metric measurement systemm displayedValue /= _unitValueAltitude; } } else if (__dataSerie == _serieTemperature) { displayedValue = UI.convertTemperatureFromMetric(metricValue); } return Float.toString(displayedValue); } public void setDataSerie(final float[] dataSerie) { __dataSerie = dataSerie; } @Override protected void setValue(final Object element, final Object value) { if (value instanceof String) { try { /* * convert entered value into metric value */ final float enteredValue = Float.parseFloat((String) value); float metricValue = enteredValue; if (__dataSerie == _serieAltitude) { if (_unitValueAltitude != 1) { // none metric measurement systemm // ensure float is used final float noneMetricValue = enteredValue; metricValue = Math.round(noneMetricValue * _unitValueAltitude); } } final boolean isTemperatureSerie = __dataSerie == _serieTemperature; if (isTemperatureSerie) { metricValue = UI.convertTemperatureToMetric(enteredValue); } final int serieIndex = ((TimeSlice) element).serieIndex; if (metricValue != __dataSerie[serieIndex]) { // value has changed // update dataserie __dataSerie[serieIndex] = metricValue; updateUI_AfterSliceEdit(); } } catch (final Exception e) { // ignore invalid characters } finally { } } } } private class SliceViewerContentProvider implements IStructuredContentProvider { public SliceViewerContentProvider() { } @Override public void dispose() { } @Override public Object[] getElements(final Object parent) { return _sliceViewerItems; } @Override public void inputChanged(final Viewer v, final Object oldInput, final Object newInput) { } } /** * It took me hours to find this location where the editor is activated/deactivated without * using TableViewerEditor which is activated in setCellEditingSupport but not in the row edit * mode. */ private final class TextCellEditorCustomized extends TextCellEditor { private TextCellEditorCustomized(final Composite parent) { super(parent); } @Override public void activate() { super.activate(); _isCellEditorActive = true; enableActionsDelayed(); } @Override public void deactivate() { super.deactivate(); _isCellEditorActive = false; enableActionsDelayed(); } } private class TimeDuration { private static final String timeFormat = "%5d:%02d:%02d"; //$NON-NLS-1$ private PageBook _pageBook; private Composite _pageReadMode; private Composite _pageEditMode; private Text _txtTime; private Spinner _spinHours; private Spinner _spinMinutes; private Spinner _spinSeconds; private boolean _isTimeEditMode = false; public TimeDuration(final Composite parent) { createUI(parent); } private void createUI(final Composite parent) { // fixed bug: https://sourceforge.net/tracker/index.php?func=detail&aid=3292465&group_id=179799&atid=890601 // let the system decide which field width is used by setting min/max values // final int spinnerWidthHour = _pc.convertWidthInCharsToPixels(_isOSX ? 8 : 4); // final int spinnerWidth = _pc.convertWidthInCharsToPixels(_isOSX ? 6 : 3); _pageBook = new PageBook(parent, SWT.NONE); /* * page: read mode */ _pageReadMode = new Composite(_pageBook, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(_pageReadMode); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(_pageReadMode); { _txtTime = _tk.createText(_pageReadMode, UI.EMPTY_STRING, SWT.READ_ONLY); GridDataFactory.fillDefaults().grab(true, false).applyTo(_txtTime); _txtTime.setEnabled(false); _tk.createLabel(_pageReadMode, UI.UNIT_LABEL_TIME, SWT.READ_ONLY); } /* * page: edit mode */ _pageEditMode = new Composite(_pageBook, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(_pageEditMode); GridLayoutFactory.fillDefaults().numColumns(3).spacing(0, 0).applyTo(_pageEditMode); { /* * hour */ _spinHours = new Spinner(_pageEditMode, SWT.BORDER); GridDataFactory.fillDefaults()// // .hint(spinnerWidthHour, SWT.DEFAULT) .align(SWT.BEGINNING, SWT.CENTER).applyTo(_spinHours); _spinHours.setMinimum(-1); _spinHours.setMaximum(999); _spinHours.setToolTipText(Messages.Tour_Editor_Label_Hours_Tooltip); _spinHours.addMouseWheelListener(_mouseWheelListener); _spinHours.addSelectionListener(_tourTimeListener); _tk.adapt(_spinHours, true, true); /* * minute */ _spinMinutes = new Spinner(_pageEditMode, SWT.BORDER); GridDataFactory.fillDefaults()// // .hint(spinnerWidth, SWT.DEFAULT) .align(SWT.BEGINNING, SWT.CENTER).applyTo(_spinMinutes); _spinMinutes.setMinimum(-1); _spinMinutes.setMaximum(60); _spinMinutes.setToolTipText(Messages.Tour_Editor_Label_Minutes_Tooltip); _spinMinutes.addMouseWheelListener(_mouseWheelListener); _spinMinutes.addSelectionListener(_tourTimeListener); _tk.adapt(_spinMinutes, true, true); /* * seconds */ _spinSeconds = new Spinner(_pageEditMode, SWT.BORDER); GridDataFactory.fillDefaults()// // .hint(spinnerWidth, SWT.DEFAULT) .align(SWT.BEGINNING, SWT.CENTER).applyTo(_spinSeconds); _spinSeconds.setMinimum(-1); _spinSeconds.setMaximum(60); _spinSeconds.setToolTipText(Messages.Tour_Editor_Label_Seconds_Tooltip); _spinSeconds.addMouseWheelListener(_mouseWheelListener); _spinSeconds.addSelectionListener(_tourTimeListener); _tk.adapt(_spinSeconds, true, true); } // default is read mode _pageBook.showPage(_pageReadMode); } /** * @return Returns time in seconds */ public int getTime() { return (_spinHours.getSelection() * 3600) // + (_spinMinutes.getSelection() * 60) + _spinSeconds.getSelection(); } public void setEditMode(final boolean isEditMode) { // optimize if (_isTimeEditMode == isEditMode) { // nothing changed return; } _isTimeEditMode = isEditMode; _pageBook.showPage(isEditMode ? _pageEditMode : _pageReadMode); // hide drawing artefact, this do not work 100% correct on winXP _tab1Container.setRedraw(false); { _tab1Container.layout(true, true); } _tab1Container.setRedraw(true); } public void setTime(final int recordingTime) { final int hours = recordingTime / 3600; final int minutes = (recordingTime % 3600) / 60; final int seconds = (recordingTime % 3600) % 60; _txtTime.setText(String.format(timeFormat, hours, minutes, seconds)); final boolean isBackup = _isSetField; _isSetField = true; { _spinHours.setSelection(hours); _spinMinutes.setSelection(minutes); _spinSeconds.setSelection(seconds); } _isSetField = isBackup; } public void setTime(final int hours, final int minutes, final int seconds) { _txtTime.setText(String.format(timeFormat, hours, minutes, seconds)); final boolean isBackup = _isSetField; _isSetField = true; { _spinHours.setSelection(hours); _spinMinutes.setSelection(minutes); _spinSeconds.setSelection(seconds); } _isSetField = isBackup; } } /** * Compute distance values from the geo positions * <p> * Performs the run() method in {@link ActionComputeDistanceValues} */ void actionComputeDistanceValuesFromGeoPosition() { if (TourManager.computeDistanceValuesFromGeoPosition(getSelectedTours()) == false) { return; } updateUI_AfterDistanceModifications(); } /** * Creates a new manually created tour, editor must not be dirty before this action is called */ public void actionCreateTour() { // check if a person is selected final TourPerson activePerson = TourbookPlugin.getActivePerson(); if (activePerson == null) { MessageDialog.openInformation(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_create_tour_title, Messages.tour_editor_dlg_create_tour_message); return; } final TourData manualTourData = new TourData(); // set tour start date/time manualTourData.setTourStartTime(TimeTools.now()); // tour id must be created after the tour date/time is set manualTourData.createTourId(); manualTourData.setDeviceId(TourData.DEVICE_ID_FOR_MANUAL_TOUR); manualTourData.setTourPerson(activePerson); // ensure that the time zone is saved in the tour _isTimeZoneManuallyModified = true; // update UI _tourData = manualTourData; _tourChart = null; updateUI_FromModel(manualTourData, false, true); // set editor into edit mode _isEditMode = true; _actionToggleReadEditMode.setChecked(true); enableActions(); enableControls(); // select tour tab and first field _tabFolder.setSelection(_tabTour); _comboTitle.setFocus(); // set tour dirty even when nothing is entered but the user can see that this tour must be saved or discarded setTourDirty(); } void actionCsvTimeSliceExport() { // get selected time slices final StructuredSelection selection = (StructuredSelection) _sliceViewer.getSelection(); if (selection.size() == 0) { return; } /* * get export filename */ final FileDialog dialog = new FileDialog(Display.getCurrent().getActiveShell(), SWT.SAVE); dialog.setText(Messages.dialog_export_file_dialog_text); dialog.setFilterPath(_state.get(STATE_CSV_EXPORT_PATH)); dialog.setFilterExtensions(new String[] { Util.CSV_FILE_EXTENSION }); dialog.setFileName( net.tourbook.ui.UI.format_yyyymmdd_hhmmss(_tourData) + UI.SYMBOL_DOT + Util.CSV_FILE_EXTENSION); final String selectedFilePath = dialog.open(); if (selectedFilePath == null) { return; } final File exportFilePath = new Path(selectedFilePath).toFile(); // keep export path _state.put(STATE_CSV_EXPORT_PATH, exportFilePath.getPath()); if (exportFilePath.exists()) { if (net.tourbook.ui.UI.confirmOverwrite(exportFilePath) == false) { // don't overwrite file, nothing more to do return; } } /* * write time slices into csv file */ Writer exportWriter = null; try { exportWriter = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(selectedFilePath), UI.UTF_8)); final StringBuilder sb = new StringBuilder(); writeCSVHeader(exportWriter, sb); for (final Object selectedItem : selection.toArray()) { final int serieIndex = ((TimeSlice) selectedItem).serieIndex; // truncate buffer sb.setLength(0); // no. sb.append(Integer.toString(serieIndex + 0)); sb.append(UI.TAB); // time hh:mm:ss if (_serieTime != null) { sb.append(net.tourbook.common.UI.format_hh_mm_ss(_serieTime[serieIndex])); } sb.append(UI.TAB); // time in seconds if (_serieTime != null) { sb.append(Integer.toString(_serieTime[serieIndex])); } sb.append(UI.TAB); // distance if (_serieDistance != null) { sb.append(_nf6.format(_serieDistance[serieIndex] / 1000 / _unitValueDistance)); } sb.append(UI.TAB); // altitude if (_serieAltitude != null) { sb.append(_nf3.format(_serieAltitude[serieIndex] / _unitValueAltitude)); } sb.append(UI.TAB); // gradient if (_serieGradient != null) { sb.append(_nf3.format(_serieGradient[serieIndex])); } sb.append(UI.TAB); // pulse if (_seriePulse != null) { sb.append(_nf3.format(_seriePulse[serieIndex])); } sb.append(UI.TAB); // marker final TourMarker tourMarker = _markerMap.get(serieIndex); if (tourMarker != null) { sb.append(tourMarker.getLabel()); } sb.append(UI.TAB); // temperature if (_serieTemperature != null) { final float temperature = UI.convertTemperatureFromMetric(_serieTemperature[serieIndex]); sb.append(_nf3.format(temperature)); } sb.append(UI.TAB); // cadence if (_serieCadence != null) { sb.append(_nf3.format(_serieCadence[serieIndex])); } sb.append(UI.TAB); // speed if (_serieSpeed != null) { sb.append(_nf3.format(_serieSpeed[serieIndex])); } sb.append(UI.TAB); // pace if (_seriePace != null) { sb.append(net.tourbook.common.UI.format_hhh_mm_ss((long) _seriePace[serieIndex])); } sb.append(UI.TAB); // power if (_seriePower != null) { sb.append(_nf3.format(_seriePower[serieIndex])); } sb.append(UI.TAB); // longitude if (_serieLongitude != null) { sb.append(Double.toString(_serieLongitude[serieIndex])); } sb.append(UI.TAB); // latitude if (_serieLatitude != null) { sb.append(Double.toString(_serieLatitude[serieIndex])); } sb.append(UI.TAB); // break time if (_serieBreakTime != null) { sb.append(_serieBreakTime[serieIndex] ? 1 : 0); } sb.append(UI.TAB); // end of line sb.append(net.tourbook.ui.UI.SYSTEM_NEW_LINE); exportWriter.write(sb.toString()); } } catch (final IOException e) { e.printStackTrace(); } finally { if (exportWriter != null) { try { exportWriter.close(); } catch (final IOException e) { e.printStackTrace(); } } } } void actionDeleteDistanceValues() { if (MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), Messages.TourEditor_Dialog_DeleteDistanceValues_Title, NLS.bind(Messages.TourEditor_Dialog_DeleteDistanceValues_Message, UI.UNIT_LABEL_DISTANCE)) == false) { return; } _tourData.distanceSerie = null; // reset distance in markers final Set<TourMarker> allTourMarker = _tourData.getTourMarkers(); if (allTourMarker != null) { for (final TourMarker tourMarker : allTourMarker) { tourMarker.setDistance(0); } } updateUI_AfterDistanceModifications(); } /** * delete selected time slices * * @param isRemoveTime */ void actionDeleteTimeSlices(final boolean isRemoveTime) { // a tour with reference tours is currently not supported if (_isReferenceTourAvailable) { MessageDialog.openInformation(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_delete_rows_title, Messages.tour_editor_dlg_delete_rows_message); return; } if (isRowSelectionMode() == false) { return; } // get selected time slices final StructuredSelection selection = (StructuredSelection) _sliceViewer.getSelection(); if (selection.size() == 0) { return; } final Object[] selectedTimeSlices = selection.toArray(); /* * check if time slices have a successive selection */ int lastIndex = -1; int firstIndex = -1; for (final Object selectedItem : selectedTimeSlices) { final TimeSlice timeSlice = (TimeSlice) selectedItem; if (lastIndex == -1) { // first slice firstIndex = lastIndex = timeSlice.serieIndex; } else { // 2...n slices if (lastIndex - timeSlice.serieIndex == -1) { // successive selection lastIndex = timeSlice.serieIndex; } else { MessageDialog.openInformation(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_delete_rows_title, Messages.tour_editor_dlg_delete_rows_not_successive); return; } } } // check if markers are within the selection if (canDeleteMarkers(firstIndex, lastIndex) == false) { return; } /* * get first selection index to select a time slice after removal */ final Table table = (Table) _sliceViewer.getControl(); final int[] indices = table.getSelectionIndices(); Arrays.sort(indices); int lastSelectionIndex = indices[0]; TourManager.removeTimeSlices(_tourData, firstIndex, lastIndex, isRemoveTime); getDataSeriesFromTourData(); // update UI updateUI_Tab_1_Tour(); updateUI_Tab_3_Info(); // update slice viewer _sliceViewerItems = getRemainingSliceItems(_sliceViewerItems, firstIndex, lastIndex); _sliceViewer.getControl().setRedraw(false); { // update viewer _sliceViewer.remove(selectedTimeSlices); // update serie index label _sliceViewer.refresh(true); } _sliceViewer.getControl().setRedraw(true); setTourDirty(); // notify other viewers fireModifyNotification(); /* * select next available time slice */ final int itemCount = table.getItemCount(); if (itemCount > 0) { // adjust to array bounds lastSelectionIndex = Math.max(0, Math.min(lastSelectionIndex, itemCount - 1)); table.setSelection(lastSelectionIndex); table.showSelection(); // fire selection position _sliceViewer.setSelection(_sliceViewer.getSelection()); } } void actionSaveTour() { // action is enabled when the tour is modified saveTourIntoDB(); } void actionSetStartDistanceTo_0000() { // it is already checked if a valid data serie is available and first distance is > 0 final float[] distanceSerie = _tourData.distanceSerie; final float distanceOffset = distanceSerie[0]; // adjust distance data serie for (int serieIndex = 0; serieIndex < distanceSerie.length; serieIndex++) { final float sliceDistance = distanceSerie[serieIndex]; distanceSerie[serieIndex] = sliceDistance - distanceOffset; } // adjust distance in markers final Set<TourMarker> allTourMarker = _tourData.getTourMarkers(); if (allTourMarker != null) { for (final TourMarker tourMarker : allTourMarker) { final float markerDistance = tourMarker.getDistance(); if (markerDistance > 0) { tourMarker.setDistance(markerDistance - distanceOffset); } } } updateUI_AfterDistanceModifications(); } private void actionTimeZone_Remove() { _tourData.setTimeZoneId(null); // select default time zone _comboTimeZone.select(TimeTools.getTimeZoneIndex_Default()); _isTimeZoneManuallyModified = false; updateModelFromUI(); setTourDirty(); updateUI_TimeZone(); } private void actionTimeZone_SetDefault() { // select default time zone _comboTimeZone.select(TimeTools.getTimeZoneIndex_Default()); _isTimeZoneManuallyModified = true; updateModelFromUI(); setTourDirty(); updateUI_TimeZone(); } private void actionTimeZone_SetFromGeo() { if (_tourData.latitudeSerie == null || _tourData.latitudeSerie.length == 0) { return; } // select time zone from geo position final double lat0 = _tourData.latitudeSerie[0]; final double lat1 = _tourData.longitudeSerie[0]; final int timeZoneIndex = TimeTools.getTimeZoneIndex(lat0, lat1); _comboTimeZone.select(timeZoneIndex); _isTimeZoneManuallyModified = true; updateModelFromUI(); setTourDirty(); updateUI_TimeZone(); } void actionToggleReadEditMode() { _isEditMode = _actionToggleReadEditMode.isChecked(); enableActions(); enableControls(); } void actionToggleRowSelectMode() { _isRowEditMode = _actionToggleRowSelectMode.isChecked(); recreateViewer(); } void actionUndoChanges() { if (confirmUndoChanges()) { discardModifications(); } } private void addPartListener() { // set the part listener _partListener = new IPartListener2() { @Override public void partActivated(final IWorkbenchPartReference partRef) { if (partRef.getPart(false) == TourDataEditorView.this) { _postSelectionProvider.setSelection(new SelectionTourData(null, _tourData)); } } @Override public void partBroughtToTop(final IWorkbenchPartReference partRef) { } @Override public void partClosed(final IWorkbenchPartReference partRef) { if (partRef.getPart(false) == TourDataEditorView.this) { saveState(); TourManager.setTourDataEditor(null); } } @Override public void partDeactivated(final IWorkbenchPartReference partRef) { } @Override public void partHidden(final IWorkbenchPartReference partRef) { if (partRef.getPart(false) == TourDataEditorView.this) { _isPartVisible = false; } } @Override public void partInputChanged(final IWorkbenchPartReference partRef) { } @Override public void partOpened(final IWorkbenchPartReference partRef) { if (partRef.getPart(false) == TourDataEditorView.this) { // when part is opened it also should be visible _isPartVisible = true; TourManager.setTourDataEditor(TourDataEditorView.this); } } @Override public void partVisible(final IWorkbenchPartReference partRef) { if (partRef.getPart(false) == TourDataEditorView.this) { _isPartVisible = true; Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { updateUI_FromModelRunnable(); } }); } } }; // register the listener in the page getSite().getPage().addPartListener(_partListener); } private void addPrefListener() { _prefChangeListener = new IPropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent event) { if (_tourData == null) { return; } final String property = event.getProperty(); if (property.equals(ITourbookPreferences.MEASUREMENT_SYSTEM) || property.equals(ITourbookPreferences.TOUR_TYPE_LIST_IS_MODIFIED)) { /* * tour data could have been changed but the changes are not reflected in the * data model, the model needs to be updated from the UI */ if (isTourValid()) { updateModelFromUI(); } else { MessageDialog.openInformation(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_discard_tour_title, Messages.tour_editor_dlg_discard_tour_message); discardModifications(); } if (property.equals(ITourbookPreferences.MEASUREMENT_SYSTEM)) { // measurement system has changed net.tourbook.ui.UI.updateUnits(); /* * It is possible that the unit values in the UI class have been updated * before the model was saved, this can happen when another view called the * method UI.updateUnits(). Because of this race condition, only the * internal units are used to calculate values which depend on the * measurement system */ updateInternalUnitValues(); recreateViewer(); updateUI_FromModel(_tourData, false, true); } else if (property.equals(ITourbookPreferences.TOUR_TYPE_LIST_IS_MODIFIED)) { // reload tour data updateUI_FromModel(_tourData, false, true); } } else if (property.equals(ITourbookPreferences.TOUR_PERSON_LIST_IS_MODIFIED)) { // display renamed person // updateUITab4Info(); do NOT work // // tour data must be reloaded } } }; _prefStore.addPropertyChangeListener(_prefChangeListener); /* * Common preferences */ _prefChangeListenerCommon = new IPropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent event) { final String property = event.getProperty(); if (property.equals(ICommonPreferences.TIME_ZONE_LOCAL_ID)) { // reload tour data updateUI_FromModel(_tourData, false, true); } } }; // register the listener _prefStoreCommon.addPropertyChangeListener(_prefChangeListenerCommon); } /** * listen for events when a tour is selected */ private void addSelectionListener() { _postSelectionListener = new ISelectionListener() { @Override public void selectionChanged(final IWorkbenchPart part, final ISelection selection) { if (part == TourDataEditorView.this) { return; } onSelectionChanged(selection); } }; getSite().getPage().addPostSelectionListener(_postSelectionListener); } private void addTourEventListener() { _tourEventListener = new ITourEventListener() { @Override public void tourChanged(final IWorkbenchPart part, final TourEventId eventId, final Object eventData) { if (part == TourDataEditorView.this) { return; } if ((eventId == TourEventId.TOUR_SELECTION) && eventData instanceof ISelection) { onSelectionChanged((ISelection) eventData); } else { if (_tourData == null) { return; } final long tourDataEditorTourId = _tourData.getTourId(); if ((eventId == TourEventId.TOUR_CHANGED) && (eventData instanceof TourEvent)) { final TourEvent tourEvent = (TourEvent) eventData; final ArrayList<TourData> modifiedTours = tourEvent.getModifiedTours(); if (modifiedTours == null) { return; } for (final TourData tourData : modifiedTours) { if (tourData.getTourId() == tourDataEditorTourId) { // update modified tour if (tourEvent.tourDataEditorSavedTour == _tourData) { /* * nothing to do because the tour is already saved (it was not * modified before) and the UI is already updated */ return; } if (tourEvent.isReverted) { setTourClean(); } else { setTourDirty(); } updateUI_FromModel(tourData, true, tourEvent.isReverted); // nothing more to do, the editor contains only one tour return; } } // removed old tour data from the selection provider _postSelectionProvider.clearSelection(); } else if (eventId == TourEventId.TAG_STRUCTURE_CHANGED) { updateUI_FromModel(_tourData, false, true); } else if (eventId == TourEventId.MARKER_SELECTION && eventData instanceof SelectionTourMarker) { // ensure that the tour is displayed onSelectionChanged((ISelection) eventData); final SelectionTourMarker tourMarkerSelection = (SelectionTourMarker) eventData; onSelectionChanged_TourMarker(tourMarkerSelection); } else if (eventId == TourEventId.CLEAR_DISPLAYED_TOUR) { clearEditorContent(); } else if (eventId == TourEventId.SEGMENT_LAYER_CHANGED) { updateUI_FromModel(_tourData, true, true); } else if (eventId == TourEventId.TOUR_CHART_PROPERTY_IS_MODIFIED) { updateUI_FromModel(_tourData, true, true); } else if (eventId == TourEventId.UPDATE_UI) { // check if this tour data editor contains a tour which must be updated // update editor if (net.tourbook.ui.UI.containsTourId(eventData, tourDataEditorTourId) != null) { // reload tour data _tourData = TourManager.getInstance().getTourData(_tourData.getTourId()); updateUI_FromModel(_tourData, false, true); } } else if (eventId == TourEventId.SLIDER_POSITION_CHANGED && eventData instanceof ISelection) { onSelectionChanged((ISelection) eventData); } } } }; TourManager.getInstance().addTourEventListener(_tourEventListener); } private void addTourSaveListener() { _tourSaveListener = new ITourSaveListener() { @Override public boolean saveTour() { boolean isTourSaved; _isSavingInProgress = true; { isTourSaved = saveTourWithValidation(); } _isSavingInProgress = false; return isTourSaved; } }; TourManager.getInstance().addTourSaveListener(_tourSaveListener); } /** * Checks if a marker is within the selected time slices * * @param firstSliceIndex * @param lastSliceIndex * @return Returns <code>true</code> when the marker can be deleted or there is no marker <br> * Returns <code>false</code> when the marker can not be deleted. */ private boolean canDeleteMarkers(final int firstSliceIndex, final int lastSliceIndex) { final Integer[] markerSerieIndex = _markerMap.keySet().toArray(new Integer[_markerMap.size()]); for (final Integer markerIndex : markerSerieIndex) { if ((markerIndex >= firstSliceIndex) && (markerIndex <= lastSliceIndex)) { // there is a marker within the deleted time slices if (MessageDialog.openConfirm(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_delete_marker_title, Messages.tour_editor_dlg_delete_marker_message)) { return true; } else { return false; } } } // marker is not in the selection return true; } private void clearEditorContent() { if ((_tourData != null) && _isTourDirty) { /* * in this case, nothing is done because the method which fires the event * TourEventId.CLEAR_DISPLAYED_TOUR is reponsible to use the correct TourData */ } else { _tourData = null; // set slice viewer dirty _sliceViewerTourId = -1; _postSelectionProvider.clearSelection(); setTourClean(); _pageBook.showPage(_pageNoData); } } private boolean confirmUndoChanges() { // check if confirmation is disabled if (_prefStore.getBoolean(ITourbookPreferences.TOURDATA_EDITOR_CONFIRMATION_REVERT_TOUR)) { return true; } else { final MessageDialogWithToggle dialog = MessageDialogWithToggle.openOkCancelConfirm(// Display.getCurrent().getActiveShell(), // Messages.tour_editor_dlg_revert_tour_title, // title Messages.tour_editor_dlg_revert_tour_message, // message Messages.tour_editor_dlg_revert_tour_toggle_message, // toggle message false, // toggle default state null, null); _prefStore.setValue(ITourbookPreferences.TOURDATA_EDITOR_CONFIRMATION_REVERT_TOUR, dialog.getToggleState()); return dialog.getReturnCode() == Window.OK; } } private void createActions() { _actionSaveTour = new ActionSaveTour(this); _actionCreateTour = new ActionCreateTour(this); _actionUndoChanges = new ActionUndoChanges(this); _actionDeleteDistanceValues = new ActionDeleteDistanceValues(this); _actionComputeDistanceValues = new ActionComputeDistanceValues(this); _actionToggleRowSelectMode = new ActionToggleRowSelectMode(this); _actionToggleReadEditMode = new ActionToggleReadEditMode(this); _actionSetStartDistanceTo_0 = new ActionSetStartDistanceTo0(this); _actionOpenAdjustAltitudeDialog = new ActionOpenAdjustAltitudeDialog(this, true); _actionOpenMarkerDialog = new ActionOpenMarkerDialog(this, false); _actionDeleteTimeSlicesKeepTime = new ActionDeleteTimeSlicesKeepTime(this); _actionDeleteTimeSlicesRemoveTime = new ActionDeleteTimeSlicesRemoveTime(this); _actionCreateTourMarker = new ActionCreateTourMarker(this); _actionExportTour = new ActionExport(this); _actionCsvTimeSliceExport = new ActionCSVTimeSliceExport(this); _actionSplitTour = new ActionSplitTour(this); _actionExtractTour = new ActionExtractTour(this); _actionViewSettings = new ActionViewSettings(); _actionOpenTourTypePrefs = new ActionOpenPrefDialog(Messages.action_tourType_modify_tourTypes, ITourbookPreferences.PREF_PAGE_TOUR_TYPE); _actionModifyColumns = new ActionModifyColumns(this); _tagMenuMgr = new TagMenuManager(this, false); } private void createFieldListener() { _modifyListener = new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { if (_isSetField || _isSavingInProgress) { return; } updateModelFromUI(); setTourDirty(); } }; _mouseWheelListener = new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { if (_isSetField || _isSavingInProgress) { return; } Util.adjustSpinnerValueOnMouseScroll(event); updateModelFromUI(); setTourDirty(); updateUI_Time(event.widget); } }; _selectionListener = new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (_isSetField || _isSavingInProgress) { return; } updateModelFromUI(); setTourDirty(); } }; _keyListener = new KeyAdapter() { @Override public void keyReleased(final KeyEvent e) { onModifyContent(); } }; /* * listener for tour date/time */ _dateTimeListener = new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (_isSetField || _isSavingInProgress) { return; } setTourDirty(); updateUI_Title(); onModifyContent(); } }; /* * listener for recording/driving/paused time */ _tourTimeListener = new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { if (_isSetField || _isSavingInProgress) { return; } updateModelFromUI(); setTourDirty(); updateUI_Time(event.widget); } }; _verifyFloatValue = new ModifyListener() { @Override public void modifyText(final ModifyEvent event) { if (_isSetField || _isSavingInProgress) { return; } final Text widget = (Text) event.widget; final String valueText = widget.getText().trim(); if (valueText.length() > 0) { try { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Float.parseFloat() ignores localized strings therefore the databinding converter is used // which provides also a good error message // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StringToNumberConverter.toFloat(true).convert(valueText); _messageManager.removeMessage(widget.getData(WIDGET_KEY), widget); } catch (final IllegalArgumentException e) { // wrong characters are entered, display an error message _messageManager.addMessage(widget.getData(WIDGET_KEY), e.getLocalizedMessage(), null, IMessageProvider.ERROR, widget); } } /* * tour dirty must be set after validation because an error can occure which enables * actions */ if (_isTourDirty) { /* * when an error occured previously and is now solved, the save action must be * enabled */ enableActions(); } else { setTourDirty(); } } }; _verifyIntValue = new ModifyListener() { @Override public void modifyText(final ModifyEvent event) { if (_isSetField || _isSavingInProgress) { return; } final Text widget = (Text) event.widget; final String valueText = widget.getText().trim(); if (valueText.length() > 0) { try { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // Float.parseFloat() ignores localized strings therefore the databinding converter is used // which provides also a good error message // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StringToNumberConverter.toInteger(true).convert(valueText); _messageManager.removeMessage(widget.getData(WIDGET_KEY), widget); } catch (final IllegalArgumentException e) { // wrong characters are entered, display an error message _messageManager.addMessage(widget.getData(WIDGET_KEY), e.getLocalizedMessage(), null, IMessageProvider.ERROR, widget); } } /* * tour dirty must be set after validation because an error can occure which enables * actions */ if (_isTourDirty) { /* * when an error occured previously and is now solved, the save action must be * enabled */ enableActions(); } else { setTourDirty(); } } }; } /** * create the drop down menus, this must be created after the parent control is created */ private void createMenus() { MenuManager menuMgr; /* * tour type menu */ menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(final IMenuManager menuMgr) { // set menu items ActionSetTourTypeMenu.fillMenu(menuMgr, TourDataEditorView.this, false); menuMgr.add(new Separator()); menuMgr.add(_actionOpenTourTypePrefs); } }); // set menu for the tour type link _linkTourType.setMenu(menuMgr.createContextMenu(_linkTourType)); /* * tag menu */ menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(final IMenuManager menuMgr) { final Set<TourTag> tourTags = _tourData.getTourTags(); final boolean isTagInTour = tourTags.size() > 0; _tagMenuMgr.fillTagMenu(menuMgr); _tagMenuMgr.enableTagActions(true, isTagInTour, tourTags); } }); // set menu for the tag item final Menu tagContextMenu = menuMgr.createContextMenu(_linkTag); tagContextMenu.addMenuListener(new MenuAdapter() { @Override public void menuHidden(final MenuEvent e) { _tagMenuMgr.onHideMenu(); } @Override public void menuShown(final MenuEvent menuEvent) { final Rectangle rect = _linkTag.getBounds(); Point pt = new Point(rect.x, rect.y + rect.height); pt = _linkTag.getParent().toDisplay(pt); _tagMenuMgr.onShowMenu(menuEvent, _linkTag, pt, null); } }); _linkTag.setMenu(tagContextMenu); } @Override public void createPartControl(final Composite parent) { initUI(parent); updateInternalUnitValues(); // define columns for the viewers _sliceColumnManager = new ColumnManager(this, _stateSlice); _sliceColumnManager.setIsCategoryAvailable(true); defineAllColumns(parent); restoreStateBeforeUI(); // must be set before the UI is created createFieldListener(); createUI(parent); createMenus(); createActions(); fillToolbar(); addSelectionListener(); addPartListener(); addPrefListener(); addTourEventListener(); addTourSaveListener(); // this part is a selection provider getSite().setSelectionProvider(_postSelectionProvider = new PostSelectionProvider(ID)); restoreStateWithUI(); _pageBook.showPage(_pageNoData); displaySelectedTour(); } private Section createSection(final Composite parent, final FormToolkit tk, final String title, final boolean isGrabVertical, final boolean isExpandable) { final int style = isExpandable ? // Section.TWISTIE // | Section.TITLE_BAR : Section.TITLE_BAR; final Section section = tk.createSection(parent, style); section.setText(title); GridDataFactory.fillDefaults().grab(true, isGrabVertical).applyTo(section); final Composite sectionContainer = tk.createComposite(section); section.setClient(sectionContainer); section.addExpansionListener(new ExpansionAdapter() { @Override public void expansionStateChanged(final ExpansionEvent e) { onExpandSection(); } }); return section; } private void createUI(final Composite parent) { _pageBook = new PageBook(parent, SWT.NONE); _pageBook.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); _pageNoData = UI.createUI_PageNoData(_pageBook, Messages.UI_Label_no_chart_is_selected); _tk = new FormToolkit(parent.getDisplay()); _pageEditorForm = _tk.createForm(_pageBook); _tk.decorateFormHeading(_pageEditorForm); _messageManager = new MessageManager(_pageEditorForm); final Composite formBody = _pageEditorForm.getBody(); GridLayoutFactory.fillDefaults().applyTo(formBody); formBody.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); _tabFolder = new CTabFolder(formBody, SWT.FLAT | SWT.BOTTOM); GridDataFactory.fillDefaults().grab(true, true).applyTo(_tabFolder); _tabFolder.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { onSelectTab(); } }); { _tabTour = new CTabItem(_tabFolder, SWT.FLAT); _tabTour.setText(Messages.tour_editor_tabLabel_tour); _tabTour.setControl(createUITab_10_Tour(_tabFolder)); _tabSlices = new CTabItem(_tabFolder, SWT.FLAT); _tabSlices.setText(Messages.tour_editor_tabLabel_tour_data); _tabSlices.setControl(createUITab_20_Slices(_tabFolder)); _tabInfo = new CTabItem(_tabFolder, SWT.FLAT); _tabInfo.setText(Messages.tour_editor_tabLabel_info); _tabInfo.setControl(createUITab_30_Info(_tabFolder)); } } /** * @param parent */ private void createUI_10_SliceViewer(final Composite parent) { // table final Table table = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI); table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); table.setHeaderVisible(true); table.setLinesVisible(true); GridDataFactory.fillDefaults().grab(true, true).applyTo(table); createUI_12_SliceViewerContextMenu(table); // table.addTraverseListener(new TraverseListener() { // public void keyTraversed(final TraverseEvent e) { // e.doit = e.keyCode != SWT.CR; // vetoes all CR traversals // } // }); table.addKeyListener(new KeyAdapter() { @Override public void keyPressed(final KeyEvent e) { if ((_isEditMode == false) || (isTourInDb() == false)) { return; } if (e.keyCode == SWT.DEL) { actionDeleteTimeSlices(true); } } }); _sliceViewer = new TableViewer(table); if (_isRowEditMode == false) { UI.setCellEditSupport(_sliceViewer); } /* * create editing support after the viewer is created but before the columns are created. */ final TextCellEditor cellEditor = new TextCellEditorCustomized(_sliceViewer.getTable()); _altitudeEditingSupport = new SliceFloatEditingSupport(cellEditor, _serieAltitude); _pulseEditingSupport = new SliceFloatEditingSupport(cellEditor, _seriePulse); _temperatureEditingSupport = new SliceFloatEditingSupport(cellEditor, _serieTemperature); _cadenceEditingSupport = new SliceFloatEditingSupport(cellEditor, _serieCadence); _latitudeEditingSupport = new SliceDoubleEditingSupport(cellEditor, _serieLatitude); _longitudeEditingSupport = new SliceDoubleEditingSupport(cellEditor, _serieLongitude); _colDefAltitude.setEditingSupport(_altitudeEditingSupport); _colDefPulse.setEditingSupport(_pulseEditingSupport); _colDefTemperature.setEditingSupport(_temperatureEditingSupport); _colDefCadence.setEditingSupport(_cadenceEditingSupport); _colDefLatitude.setEditingSupport(_latitudeEditingSupport); _colDefLongitude.setEditingSupport(_longitudeEditingSupport); _sliceColumnManager.createColumns(_sliceViewer); _sliceViewer.setContentProvider(new SliceViewerContentProvider()); _sliceViewer.setUseHashlookup(true); _sliceViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { final StructuredSelection selection = (StructuredSelection) event.getSelection(); if (selection != null) { fireSliderPosition(selection); } } }); // hide first column, this is a hack to align the "first" visible column to right table.getColumn(0).setWidth(0); } private void createUI_12_SliceViewerContextMenu(final Table table) { final MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(final IMenuManager manager) { fillSliceContextMenu(manager); } }); final Menu tableContextMenu = menuMgr.createContextMenu(table); _sliceColumnManager.createHeaderContextMenu(table, tableContextMenu); } private Text createUI_FieldText(final Composite parent) { final Text txtField = _tk.createText(parent, UI.EMPTY_STRING, SWT.READ_ONLY); GridDataFactory.fillDefaults().grab(true, false).applyTo(txtField); return txtField; } private Label createUI_LabelSeparator(final Composite parent) { return _tk.createLabel(parent, UI.EMPTY_STRING); } private void createUI_SectionSeparator(final Composite parent) { final Composite sep = _tk.createComposite(parent); GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 5).applyTo(sep); } private void createUISection_110_Title(final Composite parent) { Label label; _sectionTitle = createSection(parent, _tk, Messages.tour_editor_section_tour, true, true); final Composite container = (Composite) _sectionTitle.getClient(); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); { /* * title */ label = _tk.createLabel(container, Messages.tour_editor_label_tour_title); _firstColumnControls.add(label); // combo: tour title with history _comboTitle = new Combo(container, SWT.BORDER | SWT.FLAT); _comboTitle.setText(UI.EMPTY_STRING); _tk.adapt(_comboTitle, true, false); GridDataFactory.fillDefaults()// .grab(true, false).hint(_hintTextColumnWidth, SWT.DEFAULT).applyTo(_comboTitle); _comboTitle.addKeyListener(_keyListener); _comboTitle.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { if (_isSetField || _isSavingInProgress) { return; } _isTitleModified = true; setTourDirty(); } }); // fill combobox TreeSet<String> arr = TourDatabase.getAllTourTitles(); for (final String string : arr) { _comboTitle.add(string); } new AutocompleteComboInput(_comboTitle); /* * description */ label = _tk.createLabel(container, Messages.tour_editor_label_description); GridDataFactory.swtDefaults().align(SWT.FILL, SWT.BEGINNING).applyTo(label); _firstColumnControls.add(label); _txtDescription = _tk.createText(container, UI.EMPTY_STRING, SWT.BORDER // | SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL// ); int descLines = _prefStore.getInt(ITourbookPreferences.TOUR_EDITOR_DESCRIPTION_HEIGHT); descLines = descLines == 0 ? 5 : descLines; // description will grab all vertical space in the tour tab GridDataFactory.fillDefaults()// .grab(true, true) // // SWT.DEFAULT causes lot's of problems with the layout therefore the hint is set // .hint(_hintTextColumnWidth, _pc.convertHeightInCharsToPixels(descLines)) .applyTo(_txtDescription); _txtDescription.addModifyListener(_modifyListener); /* * start location */ label = _tk.createLabel(container, Messages.tour_editor_label_start_location); _firstColumnControls.add(label); _comboLocation_Start = new Combo(container, SWT.BORDER | SWT.FLAT); _comboLocation_Start.setText(UI.EMPTY_STRING); _tk.adapt(_comboLocation_Start, true, false); GridDataFactory.fillDefaults().grab(true, false).hint(_hintTextColumnWidth, SWT.DEFAULT) .applyTo(_comboLocation_Start); _comboLocation_Start.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { if (_isSetField || _isSavingInProgress) { return; } _isLocationStartModified = true; setTourDirty(); } }); // fill combobox arr = TourDatabase.getAllTourPlaceStarts(); for (final String string : arr) { if (string != null) { _comboLocation_Start.add(string); } } new AutocompleteComboInput(_comboLocation_Start); /* * end location */ label = _tk.createLabel(container, Messages.tour_editor_label_end_location); _firstColumnControls.add(label); _comboLocation_End = new Combo(container, SWT.BORDER | SWT.FLAT); _comboLocation_End.setText(UI.EMPTY_STRING); _tk.adapt(_comboLocation_End, true, false); GridDataFactory.fillDefaults().grab(true, false).hint(_hintTextColumnWidth, SWT.DEFAULT) .applyTo(_comboLocation_End); _comboLocation_End.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { if (_isSetField || _isSavingInProgress) { return; } _isLocationEndModified = true; setTourDirty(); } }); // fill combobox arr = TourDatabase.getAllTourPlaceEnds(); for (final String string : arr) { if (string != null) { _comboLocation_End.add(string); } } new AutocompleteComboInput(_comboLocation_End); } } private void createUISection_120_DateTime(final Composite parent) { _sectionDateTime = createSection(parent, _tk, Messages.tour_editor_section_date_time, false, true); final Composite container = (Composite) _sectionDateTime.getClient(); GridLayoutFactory.fillDefaults()// .numColumns(2).spacing(COLUMN_SPACING, 5).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); { createUISection_122_DateTime_Col1(container); createUISection_123_DateTime_Col2(container); createUISection_129_DateTime_TimeZone(container); final Label label = createUI_LabelSeparator(container); GridDataFactory.fillDefaults().span(2, 1).applyTo(label); createUISection_127_DateTime_Col1(container); createUISection_128_DateTime_Col2(container); } } /** * 1. column */ private void createUISection_122_DateTime_Col1(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); _firstColumnContainerControls.add(container); { /* * date */ final Label label = _tk.createLabel(container, Messages.tour_editor_label_tour_date); _firstColumnControls.add(label); _dtTourDate = new DateTime(container, SWT.DATE | SWT.MEDIUM | SWT.DROP_DOWN | SWT.BORDER); GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL).applyTo(_dtTourDate); _tk.adapt(_dtTourDate, true, false); _dtTourDate.addSelectionListener(_dateTimeListener); ////////////////////////////////////// createUI_LabelSeparator(container); } } /** * 2. column */ private void createUISection_123_DateTime_Col2(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); { { /* * start time */ _lblStartTime = _tk.createLabel(container, Messages.tour_editor_label_start_time); _secondColumnControls.add(_lblStartTime); _dtStartTime = new DateTime(container, SWT.TIME | SWT.MEDIUM | SWT.BORDER); _tk.adapt(_dtStartTime, true, false); _dtStartTime.addSelectionListener(_dateTimeListener); } } } private void createUISection_127_DateTime_Col1(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); _firstColumnContainerControls.add(container); { { /* * tour distance */ final Label label = _tk.createLabel(container, Messages.tour_editor_label_tour_distance); _firstColumnControls.add(label); _txtDistance = _tk.createText(container, UI.EMPTY_STRING, SWT.TRAIL); _txtDistance.addModifyListener(_verifyFloatValue); _txtDistance.setData(WIDGET_KEY, WIDGET_KEY_TOURDISTANCE); _txtDistance.addKeyListener(new KeyListener() { @Override public void keyPressed(final KeyEvent e) { _isDistManuallyModified = true; } @Override public void keyReleased(final KeyEvent e) { } }); GridDataFactory.fillDefaults().hint(_hintValueFieldWidth, SWT.DEFAULT).applyTo(_txtDistance); _lblDistanceUnit = _tk.createLabel(container, UI.UNIT_LABEL_DISTANCE); } { /* * altitude up */ final Label label = _tk.createLabel(container, Messages.Tour_Editor_Label_AltitudeUp); _firstColumnControls.add(label); _txtAltitudeUp = _tk.createText(container, UI.EMPTY_STRING, SWT.TRAIL); _txtAltitudeUp.addModifyListener(_verifyIntValue); _txtAltitudeUp.setData(WIDGET_KEY, WIDGET_KEY_ALTITUDE_UP); _txtAltitudeUp.addKeyListener(new KeyListener() { @Override public void keyPressed(final KeyEvent e) { _isAltitudeManuallyModified = true; } @Override public void keyReleased(final KeyEvent e) { } }); GridDataFactory.fillDefaults().hint(_hintValueFieldWidth, SWT.DEFAULT).applyTo(_txtAltitudeUp); _lblAltitudeUpUnit = _tk.createLabel(container, UI.UNIT_LABEL_ALTITUDE); } { /* * altitude down */ final Label label = _tk.createLabel(container, Messages.Tour_Editor_Label_AltitudeDown); _firstColumnControls.add(label); _txtAltitudeDown = _tk.createText(container, UI.EMPTY_STRING, SWT.TRAIL); _txtAltitudeDown.addModifyListener(_verifyIntValue); _txtAltitudeDown.setData(WIDGET_KEY, WIDGET_KEY_ALTITUDE_DOWN); _txtAltitudeDown.addKeyListener(new KeyListener() { @Override public void keyPressed(final KeyEvent e) { _isAltitudeManuallyModified = true; } @Override public void keyReleased(final KeyEvent e) { } }); GridDataFactory.fillDefaults().hint(_hintValueFieldWidth, SWT.DEFAULT).applyTo(_txtAltitudeDown); _lblAltitudeDownUnit = _tk.createLabel(container, UI.UNIT_LABEL_ALTITUDE); } } } /** * 2. column */ private void createUISection_128_DateTime_Col2(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); { { /* * recording time */ final Label label = _tk.createLabel(container, Messages.tour_editor_label_recording_time); _secondColumnControls.add(label); _timeRecording = new TimeDuration(container); } { /* * paused time */ final Label label = _tk.createLabel(container, Messages.tour_editor_label_paused_time); _secondColumnControls.add(label); _timePaused = new TimeDuration(container); } { /* * driving time */ final Label label = _tk.createLabel(container, Messages.tour_editor_label_driving_time); _secondColumnControls.add(label); _timeDriving = new TimeDuration(container); } } } private void createUISection_129_DateTime_TimeZone(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults()// .grab(false, false).span(2, 1).applyTo(container); GridLayoutFactory.fillDefaults()// .numColumns(2).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_MAGENTA)); { /* * Time zone */ { // label _lblTimeZone = _tk.createLabel(container, Messages.Tour_Editor_Label_TimeZone); _firstColumnControls.add(_lblTimeZone); } { // combo _comboTimeZone = new Combo(container, SWT.READ_ONLY | SWT.BORDER); _comboTimeZone.setVisibleItemCount(50); _comboTimeZone.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { _isTimeZoneManuallyModified = true; updateModelFromUI(); setTourDirty(); updateUI_TimeZone(); } }); _tk.adapt(_comboTimeZone, true, false); // fill combobox for (final TimeZoneData timeZone : TimeTools.getAllTimeZones()) { _comboTimeZone.add(timeZone.label); } /* * Add decoration */ final Image infoImage = FieldDecorationRegistry.getDefault() .getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION).getImage(); _decoTimeZone = new ControlDecoration(_comboTimeZone, SWT.TOP | SWT.LEFT); _decoTimeZone.hide(); _decoTimeZone.setImage(infoImage); _decoTimeZone.setDescriptionText(Messages.Tour_Editor_Decorator_TimeZone_Tooltip); // indent the combo that the decorator is not truncated GridDataFactory.fillDefaults()// .indent(UI.DECORATOR_HORIZONTAL_INDENT, 0).applyTo(_comboTimeZone); } { // spacer final Label label = createUI_LabelSeparator(container); _firstColumnControls.add(label); } { final Composite actionContainer = new Composite(container, SWT.NONE); GridDataFactory.fillDefaults().grab(false, false).applyTo(actionContainer); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(actionContainer); { { // link: set default _linkDefaultTimeZone = new Link(actionContainer, SWT.NONE); _linkDefaultTimeZone.setText(Messages.Tour_Editor_Link_SetDefautTimeZone); _linkDefaultTimeZone.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { actionTimeZone_SetDefault(); } }); _tk.adapt(_linkDefaultTimeZone, true, true); } { // link: from geo _linkGeoTimeZone = new Link(actionContainer, SWT.NONE); _linkGeoTimeZone.setText(Messages.Tour_Editor_Link_SetGeoTimeZone); _linkGeoTimeZone.setToolTipText(Messages.Tour_Editor_Link_SetGeoTimeZone_Tooltip); _linkGeoTimeZone.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { actionTimeZone_SetFromGeo(); } }); _tk.adapt(_linkGeoTimeZone, true, true); } { // link: remove _linkRemoveTimeZone = new Link(actionContainer, SWT.NONE); _linkRemoveTimeZone.setText(Messages.Tour_Editor_Link_RemoveTimeZone); _linkRemoveTimeZone.setToolTipText(Messages.Tour_Editor_Link_RemoveTimeZone_Tooltip); _linkRemoveTimeZone.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { actionTimeZone_Remove(); } }); _tk.adapt(_linkRemoveTimeZone, true, true); } } } } } private void createUISection_130_Personal(final Composite parent) { _sectionPersonal = createSection(parent, _tk, Messages.tour_editor_section_personal, false, true); final Composite container = (Composite) _sectionPersonal.getClient(); GridLayoutFactory.fillDefaults()// .numColumns(2).spacing(COLUMN_SPACING, 5).applyTo(container); { createUISection_132_PersonalCol1(container); createUISection_134_PersonalCol2(container); } } /** * 1. column */ private void createUISection_132_PersonalCol1(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); _firstColumnContainerControls.add(container); { { /* * calories */ // label final Label label = _tk.createLabel(container, Messages.tour_editor_label_tour_calories); _firstColumnControls.add(label); // spinner _spinCalories = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(_spinCalories); _spinCalories.setMinimum(0); _spinCalories.setMaximum(1000000); _spinCalories.addMouseWheelListener(_mouseWheelListener); _spinCalories.addSelectionListener(_selectionListener); // label: kcal _tk.createLabel(container, VALUE_UNIT_K_CALORIES); } { /* * rest pulse */ // label: Rest pulse final Label label = _tk.createLabel(container, Messages.tour_editor_label_rest_pulse); label.setToolTipText(Messages.tour_editor_label_rest_pulse_Tooltip); _firstColumnControls.add(label); // spinner _spinRestPuls = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .hint(_hintDefaultSpinnerWidth, SWT.DEFAULT).align(SWT.BEGINNING, SWT.CENTER) .applyTo(_spinRestPuls); _spinRestPuls.setMinimum(0); _spinRestPuls.setMaximum(200); _spinRestPuls.setToolTipText(Messages.tour_editor_label_rest_pulse_Tooltip); _spinRestPuls.addMouseWheelListener(_mouseWheelListener); _spinRestPuls.addSelectionListener(_selectionListener); // label: bpm _tk.createLabel(container, GRAPH_LABEL_HEARTBEAT_UNIT); } } } /** * 2. column */ private void createUISection_134_PersonalCol2(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().applyTo(container); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container); { { /* * Body weight */ // label: Weight final Label label = _tk.createLabel(container, Messages.Tour_Editor_Label_BodyWeight); label.setToolTipText(Messages.Tour_Editor_Label_BodyWeight_Tooltip); _secondColumnControls.add(label); // spinner: weight _spinBodyWeight = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .hint(_hintDefaultSpinnerWidth, SWT.DEFAULT).align(SWT.BEGINNING, SWT.CENTER) .applyTo(_spinBodyWeight); _spinBodyWeight.setDigits(1); _spinBodyWeight.setMinimum(0); _spinBodyWeight.setMaximum(3000); // 300.0 kg _spinBodyWeight.addMouseWheelListener(_mouseWheelListener); _spinBodyWeight.addSelectionListener(_selectionListener); // label: unit _tk.createLabel(container, UI.UNIT_WEIGHT_KG); } { /* * FTP - Functional Threshold Power */ // label: FTP final Label label = _tk.createLabel(container, Messages.Tour_Editor_Label_FTP); label.setToolTipText(Messages.Tour_Editor_Label_FTP_Tooltip); _secondColumnControls.add(label); // spinner: FTP _spinFTP = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .hint(_hintDefaultSpinnerWidth, SWT.DEFAULT).align(SWT.BEGINNING, SWT.CENTER) .applyTo(_spinFTP); _spinFTP.setMinimum(0); _spinFTP.setMaximum(10000); _spinFTP.addMouseWheelListener(_mouseWheelListener); // spacer _tk.createLabel(container, UI.EMPTY_STRING); } } } private void createUISection_140_Weather(final Composite parent) { _sectionWeather = createSection(parent, _tk, Messages.tour_editor_section_weather, false, true); final Composite container = (Composite) _sectionWeather.getClient(); GridLayoutFactory.fillDefaults()// .numColumns(2).spacing(COLUMN_SPACING, 5).applyTo(container); { createUISection_141_Weather(container); createUISection_142_Weather(container); createUISection_144_WeatherCol1(container); } } private void createUISection_141_Weather(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container); { /* * weather description */ final Label label = _tk.createLabel(container, Messages.Tour_Editor_Label_Weather); GridDataFactory.swtDefaults().align(SWT.FILL, SWT.BEGINNING).applyTo(label); _firstColumnControls.add(label); _txtWeather = _tk.createText(container, // UI.EMPTY_STRING, SWT.BORDER | SWT.WRAP | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL// ); _txtWeather.addModifyListener(_modifyListener); GridDataFactory.fillDefaults()// .grab(true, true) // // SWT.DEFAULT causes lot's of problems with the layout therefore the hint is set // .hint(_hintTextColumnWidth, _pc.convertHeightInCharsToPixels(2)).applyTo(_txtWeather); } } private void createUISection_142_Weather(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().span(2, 1).applyTo(container); GridLayoutFactory.fillDefaults().numColumns(5).applyTo(container); { /* * wind speed */ // label Label label = _tk.createLabel(container, Messages.tour_editor_label_wind_speed); label.setToolTipText(Messages.tour_editor_label_wind_speed_Tooltip); _firstColumnControls.add(label); // spinner _spinWindSpeedValue = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .hint(_hintDefaultSpinnerWidth, SWT.DEFAULT).align(SWT.BEGINNING, SWT.CENTER) .applyTo(_spinWindSpeedValue); _spinWindSpeedValue.setMinimum(0); _spinWindSpeedValue.setMaximum(120); _spinWindSpeedValue.setToolTipText(Messages.tour_editor_label_wind_speed_Tooltip); _spinWindSpeedValue.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { if (_isSetField || _isSavingInProgress) { return; } onSelectWindSpeedValue(); setTourDirty(); } }); _spinWindSpeedValue.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (_isSetField || _isSavingInProgress) { return; } onSelectWindSpeedValue(); setTourDirty(); } }); _spinWindSpeedValue.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { Util.adjustSpinnerValueOnMouseScroll(event); if (_isSetField || _isSavingInProgress) { return; } onSelectWindSpeedValue(); setTourDirty(); } }); // label: km/h, mi/h _lblSpeedUnit = _tk.createLabel(container, UI.UNIT_LABEL_SPEED); // combo: wind speed with text _comboWindSpeedText = new Combo(container, SWT.READ_ONLY | SWT.BORDER); GridDataFactory.fillDefaults()// .align(SWT.BEGINNING, SWT.FILL).indent(10, 0).span(2, 1).applyTo(_comboWindSpeedText); _tk.adapt(_comboWindSpeedText, true, false); _comboWindSpeedText.setToolTipText(Messages.tour_editor_label_wind_speed_Tooltip); _comboWindSpeedText.setVisibleItemCount(20); _comboWindSpeedText.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (_isSetField || _isSavingInProgress) { return; } onSelectWindSpeedText(); setTourDirty(); } }); /** * this is not working correctly, the combo item is modified but an selection change * event is not fired -> no updates are done :-(( */ // _comboWindSpeedText.addMouseWheelListener(new MouseWheelListener() { // @Override // public void mouseScrolled(final MouseEvent event) { // // final Combo combo = (Combo) event.widget; // final int itemCount = combo.getItemCount(); // // if (itemCount == 0) { // return; // } // // // items are available // // int selectedIndex = combo.getSelectionIndex(); // // // check if items are selected // if (selectedIndex == -1) { // // select first item // combo.select(0); // return; // } // // if (event.count < 0) { // // // select next item // // if (selectedIndex < itemCount - 1) { // combo.select(++selectedIndex); // } // } else { // // // select previous item // // if (selectedIndex > 0) { // combo.select(--selectedIndex); // } // } // } // }); // fill combobox for (final String speedText : IWeather.windSpeedText) { _comboWindSpeedText.add(speedText); } /* * wind direction */ // label label = _tk.createLabel(container, Messages.tour_editor_label_wind_direction); label.setToolTipText(Messages.tour_editor_label_wind_direction_Tooltip); _firstColumnControls.add(label); // combo: wind direction text _comboWindDirectionText = new Combo(container, SWT.READ_ONLY | SWT.BORDER); _tk.adapt(_comboWindDirectionText, true, false); GridDataFactory.fillDefaults()// .align(SWT.BEGINNING, SWT.FILL).hint(_hintDefaultSpinnerWidth, SWT.DEFAULT) .applyTo(_comboWindDirectionText); _comboWindDirectionText.setToolTipText(Messages.tour_editor_label_WindDirectionNESW_Tooltip); _comboWindDirectionText.setVisibleItemCount(10); _comboWindDirectionText.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (_isSetField || _isSavingInProgress) { return; } onSelectWindDirectionText(); setTourDirty(); } }); // spacer new Label(container, SWT.NONE); // spinner: wind direction value _spinWindDirectionValue = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .hint(_hintDefaultSpinnerWidth, SWT.DEFAULT).indent(10, 0).align(SWT.BEGINNING, SWT.CENTER) .applyTo(_spinWindDirectionValue); _spinWindDirectionValue.setMinimum(-1); _spinWindDirectionValue.setMaximum(360); _spinWindDirectionValue.setToolTipText(Messages.tour_editor_label_wind_direction_Tooltip); _spinWindDirectionValue.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { if (_isSetField || _isSavingInProgress) { return; } onSelectWindDirectionValue(); setTourDirty(); } }); _spinWindDirectionValue.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (_isSetField || _isSavingInProgress) { return; } onSelectWindDirectionValue(); setTourDirty(); } }); _spinWindDirectionValue.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { Util.adjustSpinnerValueOnMouseScroll(event); if (_isSetField || _isSavingInProgress) { return; } onSelectWindDirectionValue(); setTourDirty(); } }); // label: direction unit = degree _tk.createLabel(container, Messages.Tour_Editor_Label_WindDirection_Unit); // fill combobox for (final String windDirText : IWeather.windDirectionText) { _comboWindDirectionText.add(windDirText); } } } /** * weather: 1. column */ private void createUISection_144_WeatherCol1(final Composite section) { final Composite container = _tk.createComposite(section); GridDataFactory.fillDefaults().applyTo(container); GridLayoutFactory.fillDefaults()// // .spacing(2, 1) .numColumns(3).applyTo(container); // _firstColumnContainerControls.add(container); { /* * temperature */ // label Label label = _tk.createLabel(container, Messages.tour_editor_label_temperature); label.setToolTipText(Messages.tour_editor_label_temperature_Tooltip); _firstColumnControls.add(label); // spinner _spinTemperature = new Spinner(container, SWT.BORDER); GridDataFactory.fillDefaults()// .align(SWT.BEGINNING, SWT.CENTER).hint(_hintDefaultSpinnerWidth, SWT.DEFAULT) .applyTo(_spinTemperature); _spinTemperature.setToolTipText(Messages.tour_editor_label_temperature_Tooltip); // the min/max temperature has a large range because fahrenheit has bigger values than celcius _spinTemperature.setMinimum(-600); _spinTemperature.setMaximum(1500); _spinTemperature.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { if (_isSetField || _isSavingInProgress) { return; } _isTemperatureManuallyModified = true; setTourDirty(); } }); _spinTemperature.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (_isSetDigits) { _isSetDigits = false; return; } if (_isSetField || _isSavingInProgress) { return; } _isTemperatureManuallyModified = true; setTourDirty(); } }); _spinTemperature.addMouseWheelListener(new MouseWheelListener() { @Override public void mouseScrolled(final MouseEvent event) { Util.adjustSpinnerValueOnMouseScroll(event); if (_isSetField || _isSavingInProgress) { return; } _isTemperatureManuallyModified = true; setTourDirty(); } }); // label: celcius, fahrenheit _lblTemperatureUnit = _tk.createLabel(container, UI.UNIT_LABEL_TEMPERATURE); /* * clouds */ final Composite cloudContainer = new Composite(container, SWT.NONE); GridDataFactory.fillDefaults().applyTo(cloudContainer); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(cloudContainer); { // label: clouds label = _tk.createLabel(cloudContainer, Messages.tour_editor_label_clouds); label.setToolTipText(Messages.tour_editor_label_clouds_Tooltip); // icon: clouds _lblCloudIcon = new CLabel(cloudContainer, SWT.NONE); GridDataFactory.fillDefaults()// .align(SWT.END, SWT.FILL).grab(true, false).applyTo(_lblCloudIcon); } _firstColumnControls.add(cloudContainer); // combo: clouds _comboClouds = new Combo(container, SWT.READ_ONLY | SWT.BORDER); GridDataFactory.fillDefaults().span(2, 1).applyTo(_comboClouds); _tk.adapt(_comboClouds, true, false); _comboClouds.setToolTipText(Messages.tour_editor_label_clouds_Tooltip); _comboClouds.setVisibleItemCount(10); _comboClouds.addModifyListener(_modifyListener); _comboClouds.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { displayCloudIcon(); } }); // fill combobox for (final String cloudText : IWeather.cloudText) { _comboClouds.add(cloudText); } // force the icon to be displayed to ensure the width is correctly set when the size is computed _isSetField = true; { _comboClouds.select(0); displayCloudIcon(); } _isSetField = false; } } private void createUISection_150_Characteristics(final Composite parent) { _sectionCharacteristics = createSection(parent, _tk, Messages.tour_editor_section_characteristics, false, true); final Composite container = (Composite) _sectionCharacteristics.getClient(); GridLayoutFactory.fillDefaults().numColumns(4).applyTo(container); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GREEN)); { { /* * tags */ _linkTag = new Link(container, SWT.NONE); _linkTag.setText(Messages.tour_editor_label_tour_tag); GridDataFactory.fillDefaults()// .align(SWT.BEGINNING, SWT.BEGINNING).applyTo(_linkTag); _linkTag.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { net.tourbook.common.UI.openControlMenu(_linkTag); } }); _tk.adapt(_linkTag, true, true); _firstColumnControls.add(_linkTag); _lblTags = _tk.createLabel(container, UI.EMPTY_STRING, SWT.WRAP); GridDataFactory.fillDefaults()// .grab(true, true) /* * hint is necessary that the width is not expanded when the text is long */ .hint(2 * _hintTextColumnWidth, SWT.DEFAULT).span(3, 1).applyTo(_lblTags); } { /* * tour type */ _linkTourType = new Link(container, SWT.NONE); _linkTourType.setText(Messages.tour_editor_label_tour_type); _linkTourType.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { net.tourbook.common.UI.openControlMenu(_linkTourType); } }); _tk.adapt(_linkTourType, true, true); _firstColumnControls.add(_linkTourType); _lblTourType = new CLabel(container, SWT.NONE); GridDataFactory.swtDefaults()// .grab(true, false).span(3, 1).applyTo(_lblTourType); } { /* * Cadence: rpm/spm */ // label final Label label = _tk.createLabel(container, Messages.Tour_Editor_Label_Cadence); label.setToolTipText(Messages.Tour_Editor_Label_Cadence_Tooltip); _firstColumnControls.add(label); final Composite radioContainer = new Composite(container, SWT.NONE); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(radioContainer); { // ratio: rpm _rdoCadence_Rpm = _tk.createButton(radioContainer, Messages.Tour_Editor_Radio_Cadence_Rpm, SWT.RADIO); _rdoCadence_Rpm.addSelectionListener(_selectionListener); // radio: spm _rdoCadence_Spm = _tk.createButton(radioContainer, Messages.Tour_Editor_Radio_Cadence_Spm, SWT.RADIO); _rdoCadence_Spm.addSelectionListener(_selectionListener); } } } } private void createUISection_300_Info(final Composite parent) { Label label; // keep border style final int defaultBorderStyle = _tk.getBorderStyle(); _tk.setBorderStyle(SWT.NULL); _sectionInfo = createSection(parent, _tk, Messages.tour_editor_section_info, false, false); final Composite container = (Composite) _sectionInfo.getClient(); // LayoutConstants.getSpacing(); // container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); GridLayoutFactory.fillDefaults()// .numColumns(2).spacing(10, 2).applyTo(container); { /* * date/time created */ _tk.createLabel(container, Messages.Tour_Editor_Label_DateTimeCreated); _txtDateTimeCreated = createUI_FieldText(container); /* * date/time modified */ _tk.createLabel(container, Messages.Tour_Editor_Label_DateTimeModified); _txtDateTimeModified = createUI_FieldText(container); /* * reference tours */ label = _tk.createLabel(container, Messages.tour_editor_label_ref_tour); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BEGINNING).applyTo(label); _txtRefTour = _tk.createText(container, UI.EMPTY_STRING, SWT.READ_ONLY | SWT.MULTI); /* * number of time slices */ _tk.createLabel(container, Messages.tour_editor_label_datapoints); _txtTimeSlicesCount = createUI_FieldText(container); /* * device name */ _tk.createLabel(container, Messages.tour_editor_label_device_name); _txtDeviceName = createUI_FieldText(container); /* * device firmware version */ _tk.createLabel(container, Messages.Tour_Editor_Label_DeviceFirmwareVersion); _txtDeviceFirmwareVersion = createUI_FieldText(container); /* * distance sensor */ _tk.createLabel(container, Messages.Tour_Editor_Label_DistanceSensor); _txtDistanceSensor = createUI_FieldText(container); _txtDistanceSensor.setToolTipText(Messages.Tour_Editor_Label_DeviceSensor_Tooltip); /* * stride sensor */ _tk.createLabel(container, Messages.Tour_Editor_Label_StrideSensor); _txtStrideSensor = createUI_FieldText(container); _txtStrideSensor.setToolTipText(Messages.Tour_Editor_Label_DeviceSensor_Tooltip); /* * pulse sensor */ _tk.createLabel(container, Messages.Tour_Editor_Label_PulseSensor); _txtPulseSensor = createUI_FieldText(container); _txtPulseSensor.setToolTipText(Messages.Tour_Editor_Label_DeviceSensor_Tooltip); /* * power sensor */ _tk.createLabel(container, Messages.Tour_Editor_Label_PowerSensor); _txtPowerSensor = createUI_FieldText(container); _txtPowerSensor.setToolTipText(Messages.Tour_Editor_Label_DeviceSensor_Tooltip); /* * import file path */ _tk.createLabel(container, Messages.tour_editor_label_import_file_path); _txtImportFilePath = createUI_FieldText(container); // _txtImportFilePath = new ImageComboLabel(container, SWT.NONE); // _tk.adapt(_txtImportFilePath); // GridDataFactory.fillDefaults()// // .grab(true, false) // // // // adjust to the label controls // .indent(2, 0) // // // .align(SWT.FILL, SWT.FILL) // .applyTo(_txtImportFilePath); /* * person */ _tk.createLabel(container, Messages.tour_editor_label_person); _txtPerson = createUI_FieldText(container); /* * tour id */ label = _tk.createLabel(container, Messages.tour_editor_label_tour_id); label.setToolTipText(Messages.tour_editor_label_tour_id_tooltip); _txtTourId = createUI_FieldText(container); /* * merged from tour id */ label = _tk.createLabel(container, Messages.tour_editor_label_merge_from_tour_id); label.setToolTipText(Messages.tour_editor_label_merge_from_tour_id_tooltip); _txtMergeFromTourId = createUI_FieldText(container); /* * merged into tour id */ label = _tk.createLabel(container, Messages.tour_editor_label_merge_into_tour_id); label.setToolTipText(Messages.tour_editor_label_merge_into_tour_id_tooltip); _txtMergeIntoTourId = createUI_FieldText(container); } /* * reset border style */ _tk.setBorderStyle(defaultBorderStyle); } private Composite createUITab_10_Tour(final Composite parent) { // scrolled container _tab1Container = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL); _tab1Container.setExpandVertical(true); _tab1Container.setExpandHorizontal(true); _tab1Container.addControlListener(new ControlAdapter() { @Override public void controlResized(final ControlEvent e) { onResizeTab1(); } }); { _tourContainer = new Composite(_tab1Container, SWT.NONE); GridDataFactory.fillDefaults().applyTo(_tourContainer); _tk.adapt(_tourContainer); GridLayoutFactory.swtDefaults().applyTo(_tourContainer); // _tourContainer.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_BLUE)); // set content for scrolled composite _tab1Container.setContent(_tourContainer); _tk.setBorderStyle(SWT.BORDER); { createUISection_110_Title(_tourContainer); createUI_SectionSeparator(_tourContainer); createUISection_120_DateTime(_tourContainer); createUI_SectionSeparator(_tourContainer); createUISection_130_Personal(_tourContainer); createUI_SectionSeparator(_tourContainer); createUISection_140_Weather(_tourContainer); createUI_SectionSeparator(_tourContainer); createUISection_150_Characteristics(_tourContainer); } } // compute width for all controls and equalize column width for the different sections _tab1Container.layout(true, true); UI.setEqualizeColumWidths(_firstColumnControls); UI.setEqualizeColumWidths(_secondColumnControls); _tab1Container.layout(true, true); UI.setEqualizeColumWidths(_firstColumnContainerControls); /* * Reduce width that the decorator is not truncated */ final GridData gd = (GridData) _lblTimeZone.getLayoutData(); gd.widthHint -= UI.DECORATOR_HORIZONTAL_INDENT; return _tab1Container; } /** * @param parent * @return returns the controls for the tab */ private Control createUITab_20_Slices(final Composite parent) { _tab3Container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(_tab3Container); GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(_tab3Container); { _sliceViewerContainer = new Composite(_tab3Container, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(_sliceViewerContainer); GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(_sliceViewerContainer); createUI_10_SliceViewer(_sliceViewerContainer); _timeSliceLabel = new Label(_tab3Container, SWT.WRAP); _timeSliceLabel.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_INFO_FOREGROUND)); _timeSliceLabel.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_INFO_BACKGROUND)); _timeSliceLabel.setVisible(false); GridDataFactory.fillDefaults().grab(true, false).applyTo(_timeSliceLabel); } return _tab3Container; } private Composite createUITab_30_Info(final Composite parent) { /* * scrolled container */ _tab4Container = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL); _tab4Container.setExpandVertical(true); _tab4Container.setExpandHorizontal(true); _tab4Container.addControlListener(new ControlAdapter() { @Override public void controlResized(final ControlEvent e) { onResizeTabInfo(); } }); { _infoContainer = new Composite(_tab4Container, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(_infoContainer); _tk.adapt(_infoContainer); GridLayoutFactory.swtDefaults().applyTo(_infoContainer); } // set content for scrolled composite _tab4Container.setContent(_infoContainer); createUISection_300_Info(_infoContainer); return _tab4Container; } private void defineAllColumns(final Composite parent) { defineColumn_Data_1_First(); defineColumn_Data_Sequence(); defineColumn_Time_TimeInHHMMSSRelative(); defineColumn_Time_TimeOfDay(); defineColumn_Time_TimeInSeconds(); defineColumn_Time_TimeDiff(); defineColumn_Time_BreakTime(); defineColumn_Motion_Distance(); defineColumn_Motion_Speed(); defineColumn_Motion_Pace(); defineColumn_Motion_Latitude(); defineColumn_Motion_Longitude(); defineColumn_Motion_DistanceDiff(); defineColumn_Motion_SpeedDiff(); defineColumn_Altitude_Altitude(); defineColumn_Altitude_Gradient(); defineColumn_Body_Pulse(); defineColumn_Tour_Marker(); defineColumn_Weather_Temperature(); defineColumn_Powertrain_Cadence(); defineColumn_Powertrain_GearRatio(); defineColumn_Powertrain_GearTeeth(); defineColumn_Power(); } /** * column: altitude */ private void defineColumn_Altitude_Altitude() { ColumnDefinition colDef; _colDefAltitude = colDef = TableColumnFactory.ALTITUDE_ALTITUDE.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieAltitude != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); cell.setText(_nf1.format(_serieAltitude[timeSlice.serieIndex] / _unitValueAltitude)); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: gradient */ private void defineColumn_Altitude_Gradient() { final ColumnDefinition colDef = TableColumnFactory.ALTITUDE_GRADIENT.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieGradient != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final float value = _serieGradient[timeSlice.serieIndex]; colDef.printDetailValue(cell, value); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: pulse */ private void defineColumn_Body_Pulse() { ColumnDefinition colDef; _colDefPulse = colDef = TableColumnFactory.BODY_PULSE.createColumn(_sliceColumnManager, _pc); colDef.disableValueFormatter(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_seriePulse != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); cell.setText(Integer.toString((int) _seriePulse[timeSlice.serieIndex])); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * 1. column will be hidden because the alignment for the first column is always to the left */ private void defineColumn_Data_1_First() { final ColumnDefinition colDef = TableColumnFactory.DATA_FIRST_COLUMN.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setCanModifyVisibility(false); colDef.setIsColumnMoveable(false); colDef.setHideColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { } }); } /** * column: # */ private void defineColumn_Data_Sequence() { final ColumnDefinition colDef = TableColumnFactory.DATA_SEQUENCE.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setCanModifyVisibility(false); colDef.setIsColumnMoveable(false); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final int serieIndex = ((TimeSlice) cell.getElement()).serieIndex; final int logIndex = ((TimeSlice) cell.getElement()).uniqueCreateIndex; // the UI shows the time slice number starting with 1 and not with 0 cell.setText(Integer.toString(logIndex + 0)); // mark reference tour with a different background color boolean isBgSet = false; if (_refTourRange != null) { for (final int[] oneRange : _refTourRange) { if ((serieIndex >= oneRange[0]) && (serieIndex <= oneRange[1])) { cell.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW)); isBgSet = true; break; } } } if (isBgSet == false) { cell.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); } } }); } /** * column: distance */ private void defineColumn_Motion_Distance() { final ColumnDefinition colDef = TableColumnFactory.MOTION_DISTANCE.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.disableValueFormatter(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieDistance != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final int serieIndex = timeSlice.serieIndex; final float distance = _serieDistance[serieIndex] / 1000 / _unitValueDistance; cell.setText(_nf3.format(distance)); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: distance difference in seconds to previous slice */ private void defineColumn_Motion_DistanceDiff() { final ColumnDefinition colDef = TableColumnFactory.MOTION_DISTANCE_DIFF.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieDistance != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final int serieIndex = timeSlice.serieIndex; float distanceDiff; if (serieIndex == 0) { // first time slice can contain a distance, occured in .fit files distanceDiff = _serieDistance[0] / 1000 / _unitValueDistance; } else { final float distancePrevious = _serieDistance[serieIndex - 1] / 1000 / _unitValueDistance; final float distance = _serieDistance[serieIndex] / 1000 / _unitValueDistance; distanceDiff = distance - distancePrevious; } if (distanceDiff < 0.001) { cell.setText(UI.EMPTY_STRING); } else { cell.setText(_nf3.format(distanceDiff)); } } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: latitude */ private void defineColumn_Motion_Latitude() { ColumnDefinition colDef; _colDefLatitude = colDef = TableColumnFactory.MOTION_LATITUDE.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieLatitude != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final double latitude = _serieLatitude[timeSlice.serieIndex]; final String valueText = _nfLatLon.format(latitude); cell.setText(valueText); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: longitude */ private void defineColumn_Motion_Longitude() { ColumnDefinition colDef; _colDefLongitude = colDef = TableColumnFactory.MOTION_LONGITUDE.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieLongitude != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final double longitude = _serieLongitude[timeSlice.serieIndex]; final String valueText = _nfLatLon.format(longitude); cell.setText(valueText); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: pace */ private void defineColumn_Motion_Pace() { final ColumnDefinition colDef = TableColumnFactory.MOTION_PACE.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_seriePace == null) { cell.setText(UI.EMPTY_STRING); } else { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final long pace = (long) _seriePace[timeSlice.serieIndex]; cell.setText(net.tourbook.common.UI.format_mm_ss(pace)); } } }); } /** * column: speed */ private void defineColumn_Motion_Speed() { final ColumnDefinition colDef = TableColumnFactory.MOTION_SPEED.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieSpeed != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final float speed = _serieSpeed[timeSlice.serieIndex]; cell.setText(_nf1.format(speed)); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: speed diff */ private void defineColumn_Motion_SpeedDiff() { final ColumnDefinition colDef = TableColumnFactory.MOTION_SPEED_DIFF.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieTime != null && _serieDistance != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final int serieIndex = timeSlice.serieIndex; if (serieIndex == 0) { cell.setText(Integer.toString(0)); } else { final float timeDiff = (_serieTime[serieIndex] - _serieTime[serieIndex - 1]); final float distancePrevious = _serieDistance[serieIndex - 1] / 1000 / _unitValueDistance; final float distance = _serieDistance[serieIndex] / 1000 / _unitValueDistance; final float distDiff = distance - distancePrevious; final float speed = timeDiff == 0 ? 0 : distDiff * 3600f / timeDiff; cell.setText(_nf1.format(speed)); } } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: power */ private void defineColumn_Power() { final ColumnDefinition colDef = TableColumnFactory.POWER.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_seriePower != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); cell.setText(Integer.toString((int) _seriePower[timeSlice.serieIndex])); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: cadence */ private void defineColumn_Powertrain_Cadence() { ColumnDefinition colDef; _colDefCadence = colDef = TableColumnFactory.POWERTRAIN_CADENCE.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieCadence != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); cell.setText(_nf1.format(_serieCadence[timeSlice.serieIndex])); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * Column: Gear ratio */ private void defineColumn_Powertrain_GearRatio() { final ColumnDefinition colDef = TableColumnFactory.POWERTRAIN_GEAR_RATIO.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieGears == null) { cell.setText(UI.EMPTY_STRING); } else { final int serieIndex = ((TimeSlice) cell.getElement()).serieIndex; final float gearRatio = _serieGears[0][serieIndex]; cell.setText(_nf2.format(gearRatio)); } } }); } /** * Column: Gear teeth */ private void defineColumn_Powertrain_GearTeeth() { final ColumnDefinition colDef = TableColumnFactory.POWERTRAIN_GEAR_TEETH.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieGears == null) { cell.setText(UI.EMPTY_STRING); } else { final int serieIndex = ((TimeSlice) cell.getElement()).serieIndex; final long frontTeeth = (long) _serieGears[1][serieIndex]; final long rearTeeth = (long) _serieGears[2][serieIndex]; cell.setText(String.format(TourManager.GEAR_TEETH_FORMAT, frontTeeth, rearTeeth)); } } }); } /** * column: cadence */ private void defineColumn_Time_BreakTime() { ColumnDefinition colDef; _colDefCadence = colDef = TableColumnFactory.TIME_BREAK_TIME.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieBreakTime != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); cell.setText(_serieBreakTime[timeSlice.serieIndex] ? net.tourbook.ui.UI.BREAK_TIME_MARKER : UI.EMPTY_STRING); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: time difference in seconds to previous slice */ private void defineColumn_Time_TimeDiff() { final ColumnDefinition colDef = TableColumnFactory.TIME_TOUR_TIME_DIFF.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieTime != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final int serieIndex = timeSlice.serieIndex; if (serieIndex == 0) { cell.setText(Integer.toString(0)); } else { cell.setText(Integer.toString(_serieTime[serieIndex] - _serieTime[serieIndex - 1])); } } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: time hh:mm:ss relative to tour start */ private void defineColumn_Time_TimeInHHMMSSRelative() { final ColumnDefinition colDef = TableColumnFactory.TIME_TOUR_TIME_HH_MM_SS.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final int serieIndex = ((TimeSlice) cell.getElement()).serieIndex; if (_serieTime != null) { cell.setText(net.tourbook.common.UI.format_hh_mm_ss(_serieTime[serieIndex])); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: time in seconds */ private void defineColumn_Time_TimeInSeconds() { final ColumnDefinition colDef = TableColumnFactory.TIME_TOUR_TIME.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieTime != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final int serieIndex = timeSlice.serieIndex; cell.setText(Integer.toString(_serieTime[serieIndex])); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: time of day in hh:mm:ss */ private void defineColumn_Time_TimeOfDay() { final ColumnDefinition colDef = TableColumnFactory.TIME_TOUR_TIME_OF_DAY_HH_MM_SS .createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieTime == null) { cell.setText(UI.EMPTY_STRING); } else { final int serieIndex = ((TimeSlice) cell.getElement()).serieIndex; cell.setText( net.tourbook.common.UI.format_hh_mm_ss(_tourStartDayTime + _serieTime[serieIndex])); } } }); } /** * column: marker */ private void defineColumn_Tour_Marker() { ColumnDefinition colDef; colDef = TableColumnFactory.TOUR_MARKER.createColumn(_sliceColumnManager, _pc); colDef.setIsDefaultColumn(); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final TourMarker tourMarker = _markerMap.get(timeSlice.serieIndex); if (tourMarker != null) { cell.setText(tourMarker.getLabel()); if (tourMarker.getType() == ChartLabel.MARKER_TYPE_DEVICE) { cell.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED)); } } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * column: temperature */ private void defineColumn_Weather_Temperature() { final ColumnDefinition colDef; _colDefTemperature = colDef = TableColumnFactory.WEATHER_TEMPERATURE.createColumn(_sliceColumnManager, _pc); colDef.setLabelProvider(new CellLabelProvider() { @Override public void update(final ViewerCell cell) { if (_serieTemperature != null) { final TimeSlice timeSlice = (TimeSlice) cell.getElement(); final float value = UI.convertTemperatureFromMetric(_serieTemperature[timeSlice.serieIndex]); colDef.printDetailValue(cell, value); } else { cell.setText(UI.EMPTY_STRING); } } }); } /** * Discard modifications and fire revert event */ private void discardModifications() { setTourClean(); _postSelectionProvider.clearSelection(); _messageManager.removeAllMessages(); _tourData = reloadTourData(); updateUI_FromModel(_tourData, true, true); fireRevertNotification(); // a manually created tour can not be reloaded, find a tour in the workbench if (_tourData == null) { displaySelectedTour(); } } private void displayCloudIcon() { final int selectionIndex = _comboClouds.getSelectionIndex(); final String cloudKey = IWeather.cloudIcon[selectionIndex]; final Image cloundIcon = UI.IMAGE_REGISTRY.get(cloudKey); _lblCloudIcon.setImage(cloundIcon); } /** * try to get tourdata from the last selection or from a tour provider */ private void displaySelectedTour() { // show tour from last selection onSelectionChanged(getSite().getWorkbenchWindow().getSelectionService().getSelection()); if (_tourData == null) { Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { if (_pageBook.isDisposed()) { return; } /* * check if tour is set from a selection provider */ if (_tourData != null) { return; } final ArrayList<TourData> selectedTours = TourManager.getSelectedTours(); if ((selectedTours != null) && (selectedTours.size() > 0)) { // get first tour, this view shows only one tour displayTour(selectedTours.get(0)); setTourClean(); } } }); } } private void displayTour(final Long tourId) { if (tourId == null) { return; } // don't reload the same tour if (_tourData != null) { if (_tourData.getTourId().equals(tourId)) { return; } } final TourData tourData = TourManager.getInstance().getTourData(tourId); if (tourData != null) { _tourChart = null; updateUI_FromModel(tourData, false, true); } } private void displayTour(final TourData tourData) { if (tourData == null) { return; } // don't reload the same tour if (_tourData == tourData) { return; } _tourChart = null; updateUI_FromModel(tourData, true, true); } @Override public void dispose() { final IWorkbenchPage page = getSite().getPage(); page.removePostSelectionListener(_postSelectionListener); page.removePartListener(_partListener); _prefStore.removePropertyChangeListener(_prefChangeListener); _prefStoreCommon.removePropertyChangeListener(_prefChangeListenerCommon); TourManager.getInstance().removeTourEventListener(_tourEventListener); TourManager.getInstance().removeTourSaveListener(_tourSaveListener); if (_tk != null) { _tk.dispose(); } _firstColumnControls.clear(); _secondColumnControls.clear(); _firstColumnContainerControls.clear(); super.dispose(); } /** * saving is done in the {@link #promptToSaveOnClose()} method */ @Override public void doSave(final IProgressMonitor monitor) { } @Override public void doSaveAs() { } private void enableActions() { final boolean isTourInDb = isTourInDb(); final boolean isTourValid = isTourValid() && isTourInDb; final boolean isNotManualTour = _isManualTour == false; final boolean canEdit = _isEditMode && isTourInDb; // all actions are disabled when a cell editor is activated final boolean isCellEditorInactive = _isCellEditorActive == false; final CTabItem selectedTab = _tabFolder.getSelection(); final boolean isTableViewerTab = selectedTab == _tabSlices; final boolean isTourData = _tourData != null; final boolean canUseTool = _isEditMode && isTourValid && (_isManualTour == false); // at least 2 positions are necessary to compute the distance final boolean isGeoAvailable = isTourData && _tourData.latitudeSerie != null && _tourData.latitudeSerie.length >= 2; final boolean isDistanceAvailable = isTourData // && _tourData.distanceSerie != null && _tourData.distanceSerie.length > 0; final boolean isDistanceLargerThan0 = isTourData // && isDistanceAvailable && _tourData.distanceSerie[0] > 0; /* * tour can only be saved when it's already saved in the database,except manual tours */ _actionSaveTour.setEnabled(isCellEditorInactive && _isTourDirty && isTourValid); _actionCreateTour.setEnabled(isCellEditorInactive && !_isTourDirty); _actionUndoChanges.setEnabled(isCellEditorInactive && _isTourDirty); _actionOpenAdjustAltitudeDialog.setEnabled(isCellEditorInactive && canUseTool); _actionOpenMarkerDialog.setEnabled(isCellEditorInactive && canUseTool); _actionToggleRowSelectMode .setEnabled(isCellEditorInactive && isTableViewerTab && isTourValid && (_isManualTour == false)); _actionToggleReadEditMode.setEnabled(isCellEditorInactive && isTourInDb); _actionModifyColumns.setEnabled(isCellEditorInactive && isTableViewerTab);// && isTourValid); _actionSetStartDistanceTo_0.setEnabled(// isCellEditorInactive && isNotManualTour && canEdit && isDistanceLargerThan0); _actionDeleteDistanceValues.setEnabled(// isCellEditorInactive && isNotManualTour && canEdit && isDistanceAvailable); _actionComputeDistanceValues.setEnabled(// isCellEditorInactive && isNotManualTour && canEdit && isGeoAvailable); } /** * Delay enable/disable actions. * <p> * When a user traverses the edit fields in a viewer the actions are enabled and disable which * flickers the UI, therefor it is delayed. */ private void enableActionsDelayed() { _enableActionCounter++; final UIJob uiJob = new UIJob(UI.EMPTY_STRING) { final int __runnableCounter = _enableActionCounter; @Override public IStatus runInUIThread(final IProgressMonitor monitor) { // check if view is not disposed if (_pageBook.isDisposed()) { return Status.OK_STATUS; } // check if a newer runnable was created if (__runnableCounter != _enableActionCounter) { return Status.OK_STATUS; } enableActions(); return Status.OK_STATUS; } }; uiJob.setSystem(true); uiJob.schedule(10); } private void enableControls() { final boolean canEdit = _isEditMode && isTourInDb(); final boolean isManualAndEdit = _isManualTour && canEdit; final boolean isDeviceTour = _isManualTour == false; final float[] serieDistance = _tourData == null ? null : _tourData.distanceSerie; final boolean isDistanceSerie = serieDistance != null && serieDistance.length > 0; final boolean isGeoAvailable = _tourData != null && _tourData.latitudeSerie != null && _tourData.latitudeSerie.length > 0; _comboTitle.setEnabled(canEdit); _txtDescription.setEnabled(canEdit); _comboLocation_Start.setEnabled(canEdit); _comboLocation_End.setEnabled(canEdit); _txtWeather.setEnabled(canEdit); _spinTemperature.setEnabled(canEdit && (_tourData != null && _tourData.temperatureSerie == null)); _comboClouds.setEnabled(canEdit); _spinWindDirectionValue.setEnabled(canEdit); _spinWindSpeedValue.setEnabled(canEdit); _comboWindDirectionText.setEnabled(canEdit); _comboWindSpeedText.setEnabled(canEdit); _rdoCadence_Rpm.setEnabled(canEdit); _rdoCadence_Spm.setEnabled(canEdit); _dtTourDate.setEnabled(canEdit); _dtStartTime.setEnabled(canEdit); _comboTimeZone.setEnabled(canEdit); _linkDefaultTimeZone.setEnabled(canEdit); _linkRemoveTimeZone.setEnabled(canEdit); _linkGeoTimeZone.setEnabled(canEdit && isGeoAvailable); _timeRecording.setEditMode(isManualAndEdit); _timePaused.setEditMode(isManualAndEdit); _timeDriving.setEditMode(isManualAndEdit); // distance can be edited when no distance time slices are available _txtDistance.setEnabled(canEdit && isDistanceSerie == false); _txtAltitudeUp.setEnabled(isManualAndEdit); _txtAltitudeDown.setEnabled(isManualAndEdit); // Personal _spinBodyWeight.setEnabled(canEdit); _spinFTP.setEnabled(canEdit); _spinRestPuls.setEnabled(canEdit); _spinCalories.setEnabled(canEdit); _linkTag.setEnabled(canEdit); _linkTourType.setEnabled(canEdit); _sliceViewer.getTable().setEnabled(isDeviceTour); } /** * enable actions */ private void enableSliceActions() { final StructuredSelection sliceSelection = (StructuredSelection) _sliceViewer.getSelection(); final int numberOfSelectedSlices = sliceSelection.size(); final boolean isSliceSelected = numberOfSelectedSlices > 0; final boolean isOneSliceSelected = numberOfSelectedSlices == 1; final boolean isTourInDb = isTourInDb(); // check if a marker can be created boolean canCreateMarker = false; if (isOneSliceSelected) { final TimeSlice oneTimeSlice = (TimeSlice) sliceSelection.getFirstElement(); canCreateMarker = _markerMap.containsKey(oneTimeSlice.serieIndex) == false; } // get selected Marker TourMarker selectedMarker = null; for (final Iterator<?> iterator = sliceSelection.iterator(); iterator.hasNext();) { final TimeSlice timeSlice = (TimeSlice) iterator.next(); if (_markerMap.containsKey(timeSlice.serieIndex)) { selectedMarker = _markerMap.get(timeSlice.serieIndex); break; } } _actionCreateTourMarker.setEnabled(_isEditMode && isTourInDb && isOneSliceSelected && canCreateMarker); _actionOpenMarkerDialog.setEnabled(_isEditMode && isTourInDb); // select marker _actionOpenMarkerDialog.setTourMarker(selectedMarker); _actionDeleteTimeSlicesRemoveTime.setEnabled(_isEditMode && isTourInDb && isSliceSelected); _actionDeleteTimeSlicesKeepTime.setEnabled(_isEditMode && isTourInDb && isSliceSelected); _actionExportTour.setEnabled(true); _actionCsvTimeSliceExport.setEnabled(isSliceSelected); _actionSplitTour.setEnabled(isOneSliceSelected); _actionExtractTour.setEnabled(numberOfSelectedSlices >= 2); // set start/end position into the actions if (isSliceSelected) { final Object[] selectedSliceArray = sliceSelection.toArray(); final int lastSliceIndex = selectedSliceArray.length - 1; final TimeSlice firstSelectedTimeSlice = (TimeSlice) selectedSliceArray[0]; final TimeSlice lastSelectedTimeSlice = (TimeSlice) selectedSliceArray[lastSliceIndex]; final int firstSelectedSerieIndex = firstSelectedTimeSlice.serieIndex; final int lastSelectedSerieIndex = lastSelectedTimeSlice.serieIndex; _actionExportTour.setTourRange(firstSelectedSerieIndex, lastSelectedSerieIndex); _actionSplitTour.setTourRange(firstSelectedSerieIndex); _actionExtractTour.setTourRange(firstSelectedSerieIndex, lastSelectedSerieIndex); /* * prevent that the first and last slice is selected for the split which causes errors */ final int numberOfAllSlices = _sliceViewerItems.length; final boolean isSplitValid = firstSelectedSerieIndex > 0 && firstSelectedSerieIndex < numberOfAllSlices - 1; _actionSplitTour.setEnabled(isOneSliceSelected && isSplitValid); } } private void fillSliceContextMenu(final IMenuManager menuMgr) { menuMgr.add(_actionCreateTourMarker); menuMgr.add(_actionOpenMarkerDialog); menuMgr.add(new Separator()); menuMgr.add(_actionDeleteTimeSlicesRemoveTime); menuMgr.add(_actionDeleteTimeSlicesKeepTime); menuMgr.add(new Separator()); menuMgr.add(_actionSetStartDistanceTo_0); menuMgr.add(_actionDeleteDistanceValues); menuMgr.add(_actionComputeDistanceValues); menuMgr.add(new Separator()); menuMgr.add(_actionSplitTour); menuMgr.add(_actionExtractTour); menuMgr.add(_actionExportTour); menuMgr.add(_actionCsvTimeSliceExport); enableSliceActions(); } private void fillToolbar() { /* * fill view toolbar */ final IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager(); tbm.add(_actionSaveTour); tbm.add(new Separator()); tbm.add(_actionOpenMarkerDialog); tbm.add(_actionOpenAdjustAltitudeDialog); tbm.add(new Separator()); tbm.add(_actionToggleReadEditMode); tbm.add(_actionToggleRowSelectMode); tbm.add(new Separator()); tbm.add(_actionCreateTour); tbm.add(_actionViewSettings); tbm.update(true); /* * fill toolbar view menu */ final IMenuManager menuMgr = getViewSite().getActionBars().getMenuManager(); menuMgr.add(_actionUndoChanges); menuMgr.add(new Separator()); menuMgr.add(_actionModifyColumns); } /** * fire notification for changed tour data */ private void fireModifyNotification() { final ArrayList<TourData> modifiedTour = new ArrayList<TourData>(); modifiedTour.add(_tourData); final TourEvent tourEvent = new TourEvent(modifiedTour); tourEvent.isTourModified = true; TourManager.fireEvent(TourEventId.TOUR_CHANGED, tourEvent, TourDataEditorView.this); } /** * fire notification for the reverted tour data */ private void fireRevertNotification() { final TourEvent tourEvent = new TourEvent(_tourData); tourEvent.isReverted = true; TourManager.fireEvent(TourEventId.TOUR_CHANGED, tourEvent, TourDataEditorView.this); } /** * select the chart slider(s) according to the selected marker(s) * * @return */ private ISelection fireSliderPosition(final StructuredSelection selection) { final Object[] selectedData = selection.toArray(); if ((selectedData == null) || (selectedData.length == 0)) { return null; } if (_tourChart == null) { final TourChart tourChart = TourManager.getInstance().getActiveTourChart(); if ((tourChart != null) && (tourChart.isDisposed() == false)) { _tourChart = tourChart; } } final Object firstItem = selectedData[0]; int serieIndex0 = -1; int serieIndex1 = -1; int serieIndex2 = -1; if (selectedData.length > 1) { // two or more data are selected, set the 2 sliders to the first and last selected data if (firstItem instanceof TimeSlice) { final int serieIndexFirst = ((TimeSlice) firstItem).serieIndex; /* * position slider at the beginning of the first slice */ serieIndex1 = serieIndexFirst > 0 ? serieIndexFirst - 1 : 0; serieIndex2 = ((TimeSlice) selectedData[selectedData.length - 1]).serieIndex; } else if (firstItem instanceof TourMarker) { serieIndex1 = ((TourMarker) firstItem).getSerieIndex(); serieIndex2 = ((TourMarker) selectedData[selectedData.length - 1]).getSerieIndex(); } } else if (selectedData.length > 0) { // one data is selected if (firstItem instanceof TimeSlice) { final int serieIndexFirst = ((TimeSlice) firstItem).serieIndex; /* * position slider at the beginning of the slice so that each slice borders has an * slider */ serieIndex0 = serieIndexFirst > 0 ? serieIndexFirst - 1 : SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION; serieIndex1 = serieIndexFirst; serieIndex2 = SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION; } else if (firstItem instanceof TourMarker) { serieIndex1 = ((TourMarker) firstItem).getSerieIndex(); serieIndex2 = SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION; } } ISelection sliderSelection = null; if (serieIndex1 != -1) { if (_tourChart == null) { // chart is not available, fire a map position if ((_serieLatitude != null) && (_serieLatitude.length > 0)) { // map position is available sliderSelection = new SelectionMapPosition(_tourData, serieIndex1, serieIndex2, true); } } else { final SelectionChartXSliderPosition xSliderSelection = new SelectionChartXSliderPosition(_tourChart, serieIndex0, serieIndex1, serieIndex2); xSliderSelection.setCenterSliderPosition(true); sliderSelection = xSliderSelection; } _postSelectionProvider.setSelection(sliderSelection); } return sliderSelection; } @Override public ColumnManager getColumnManager() { final CTabItem selectedTab = _tabFolder.getSelection(); if (selectedTab == _tabSlices) { return _sliceColumnManager; } return null; } // @Override // public Object getAdapter(final Class adapter) { // // if (adapter == ColumnViewer.class) { // return _sliceViewer; // } // // return Platform.getAdapterManager().getAdapter(this, adapter); // } private void getDataSeriesFromTourData() { _serieTime = _tourData.timeSerie; _serieDistance = _tourData.distanceSerie; _serieAltitude = _tourData.altitudeSerie; _serieCadence = _tourData.cadenceSerie; _serieGears = _tourData.getGears(); _seriePulse = _tourData.pulseSerie; _serieLatitude = _tourData.latitudeSerie; _serieLongitude = _tourData.longitudeSerie; _serieBreakTime = _tourData.getBreakTimeSerie(); _serieGradient = _tourData.getGradientSerie(); _serieSpeed = _tourData.getSpeedSerie(); _seriePace = _tourData.getPaceSerieSeconds(); _seriePower = _tourData.getPowerSerie(); _serieTemperature = _tourData.temperatureSerie; _altitudeEditingSupport.setDataSerie(_serieAltitude); _temperatureEditingSupport.setDataSerie(_serieTemperature); _pulseEditingSupport.setDataSerie(_seriePulse); _cadenceEditingSupport.setDataSerie(_serieCadence); _latitudeEditingSupport.setDataSerie(_serieLatitude); _longitudeEditingSupport.setDataSerie(_serieLongitude); final ZonedDateTime tourStartTime = _tourData.getTourStartTime(); // _tourStartDayTime = _tourData.getStartTimeOfDay(); _tourStartDayTime = tourStartTime.get(ChronoField.SECOND_OF_DAY); if (_isManualTour == false) { // tour is imported if ((_serieTime == null) || (_serieTime.length == 0)) { _tourData.setTourRecordingTime(0); } else { _tourData.setTourRecordingTime(_serieTime[_serieTime.length - 1]); } _tourData.computeTourDrivingTime(); if ((_serieDistance == null) || (_serieDistance.length == 0)) { // disabled because distance can be edited when distance serie is not available // _tourData.setTourDistance(0); } else { // have no idea why tour distance is set because it should be already set during the tour import _tourData.setTourDistance(_serieDistance[_serieDistance.length - 1]); } _tourData.computeComputedValues(); } } /** * Converts a string into a float value * * @param valueText * @return Returns the float value for the parameter valueText, return <code>0</code> * @throws IllegalArgumentException */ private float getFloatValue(String valueText) throws IllegalArgumentException { valueText = valueText.trim(); if (valueText.length() == 0) { return 0; } else { final Object convertedValue = StringToNumberConverter.toFloat(true).convert(valueText); if (convertedValue instanceof Float) { return ((Float) convertedValue).floatValue(); } } return 0; } private TimeSlice[] getRemainingSliceItems(final Object[] dataViewerItems, final int firstIndex, final int lastIndex) { final int oldSerieLength = dataViewerItems.length; final int newSerieLength = oldSerieLength - (lastIndex - firstIndex + 1); final TimeSlice[] newViewerItems = new TimeSlice[newSerieLength]; if (firstIndex == 0) { // delete from start, copy data by skipping removed slices System.arraycopy(dataViewerItems, lastIndex + 1, newViewerItems, 0, newSerieLength); } else if (lastIndex == oldSerieLength - 1) { // get items from start, delete until the end System.arraycopy(dataViewerItems, 0, newViewerItems, 0, newSerieLength); } else { // delete somewhere in the middle // copy start segment System.arraycopy(dataViewerItems, 0, newViewerItems, 0, firstIndex); // copy end segment final int copyLength = oldSerieLength - (lastIndex + 1); System.arraycopy(dataViewerItems, lastIndex + 1, newViewerItems, firstIndex, copyLength); } // update serie index int serieIndex = 0; for (final TimeSlice timeSlice : newViewerItems) { timeSlice.serieIndex = serieIndex++; } return newViewerItems; } @Override public ArrayList<TourData> getSelectedTours() { if (_tourData == null) { return null; } final ArrayList<TourData> tourDataList = new ArrayList<TourData>(); tourDataList.add(_tourData); return tourDataList; } // /** // * Converts a string into a int value // * // * @param valueText // * @return Returns the float value for the parameter valueText, return <code>0</code> // * @throws IllegalArgumentException // */ // private int getIntValue(String valueText) throws IllegalArgumentException { // // valueText = valueText.trim(); // if (valueText.length() == 0) { // // return 0; // // } else { // // final Object convertedValue = StringToNumberConverter.toInteger(true).convert(valueText); // if (convertedValue instanceof Integer) { // return ((Integer) convertedValue).intValue(); // } // } // // return 0; // } TableViewer getSliceViewer() { return _sliceViewer; } private Object[] getSliceViewerItems() { if ((_tourData == null) || (_tourData.timeSerie == null) || (_tourData.timeSerie.length == 0)) { return new Object[0]; } getDataSeriesFromTourData(); /* * create viewer elements (time slices), each viewer item contains the index into the data * series */ final TimeSlice[] viewerItems = new TimeSlice[_tourData.timeSerie.length]; for (int serieIndex = 0; serieIndex < viewerItems.length; serieIndex++) { viewerItems[serieIndex] = new TimeSlice(serieIndex); } return viewerItems; } /** * @return Returns {@link TourData} for the tour in the tour data editor or <code>null</code> * when a tour is not in the tour data editor */ public TourData getTourData() { return _tourData; } private TourData getTourData(final Long tourId) { TourData tourData = TourManager.getInstance().getTourData(tourId); if (tourData == null) { // tour is not in the database, try to get it from the raw data manager final HashMap<Long, TourData> rawData = RawDataManager.getInstance().getImportedTours(); tourData = rawData.get(tourId); } return tourData; } /** * @return Returns the title of the active tour */ private String getTourTitle() { if (_tourData.isMultipleTours()) { return TourManager.getTourTitleMultiple(_tourData); } else { final ZonedDateTime tourDate = _tourData.getTourStartTime(); return TourManager.getTourTitle(tourDate); } } @Override public ColumnViewer getViewer() { final CTabItem selectedTab = _tabFolder.getSelection(); if (selectedTab == _tabSlices) { return _sliceViewer; } return null; } private int getWindDirectionTextIndex(final int degreeDirection) { final float degree = (degreeDirection + 22.5f) / 45.0f; final int directionIndex = ((int) degree) % 8; return directionIndex; } private int getWindSpeedTextIndex(final int speed) { // set speed to max index value int speedValueIndex = _unitValueWindSpeed.length - 1; for (int speedIndex = 0; speedIndex < _unitValueWindSpeed.length; speedIndex++) { final int speedMaxValue = _unitValueWindSpeed[speedIndex]; if (speed <= speedMaxValue) { speedValueIndex = speedIndex; break; } } return speedValueIndex; } private void initUI(final Composite parent) { _pc = new PixelConverter(parent); _hintDefaultSpinnerWidth = _isLinux // ? SWT.DEFAULT : _pc.convertWidthInCharsToPixels(_isOSX ? 14 : 7); _hintValueFieldWidth = _pc.convertWidthInCharsToPixels(10); } @Override public boolean isColumn0Visible(final ColumnViewer columnViewer) { if (columnViewer == _sliceViewer) { // first column is hidden, this is a super hack that the second column can be right aligned return false; } return true; } /** * @return Returns <code>true</code> when the data have been modified and not saved, returns * <code>false</code> when tour is not modified or {@link TourData} is <code>null</code> */ @Override public boolean isDirty() { if (_tourData == null) { return false; } return _isTourDirty; } /** * @return Returns <code>true</code> when the tour should be discarded<br> * returns <code>false</code> when the tour is invalid but should be saved<br> */ private boolean isDiscardTour() { final MessageDialog dialog = new MessageDialog(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_save_tour_title, null, NLS.bind(Messages.tour_editor_dlg_save_invalid_tour, TourManager.getTourDateFull(_tourData)), MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, 1); final int result = dialog.open(); if (result == 0) { // discard modifications return true; } else { // save modifications return false; } } /** * check row/cell mode, row mode must be set, it works with the cell mode but can be confusing * because multiple rows can be selected but they are not visible * * @return */ private boolean isRowSelectionMode() { if (_isRowEditMode == false) { final MessageDialogWithToggle dialog = MessageDialogWithToggle.openInformation( Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_delete_rows_title, Messages.tour_editor_dlg_delete_rows_mode_message, Messages.tour_editor_dlg_delete_rows_mode_toggle_message, true, null, null); if (dialog.getToggleState()) { _actionToggleRowSelectMode.setChecked(true); actionToggleRowSelectMode(); } return false; } return true; } @Override public boolean isSaveAsAllowed() { return false; } @Override public boolean isSaveOnCloseNeeded() { return isDirty(); } /** * @return Returns <code>true</code> when the tour is saved in the database or when a manual * tour is created which also contains a person. */ private boolean isTourInDb() { if (_tourData != null && _tourData.getTourPerson() != null) { return true; } return false; } /** * Checks if tour has no errors * * @return Returns <code>true</code> when all data for the tour are valid, <code>false</code> * otherwise */ private boolean isTourValid() { if (_tourData == null) { return false; } if (_isTourDirty) { if (_tourData.isValidForSave() == false) { return false; } if (_tourData.getTourPerson() == null) { // tour is modified but not yet saved in the database _messageManager.addMessage(WIDGET_KEY_PERSON, Messages.tour_editor_message_person_is_required, null, IMessageProvider.ERROR, _txtPerson); } else { _messageManager.removeMessage(WIDGET_KEY_PERSON, _txtPerson); } // tour is valid when there are no error messages return _messageManager.getErrorMessageCount() == 0; } else { // tour is not dirty return true; } } private void onExpandSection() { onResizeTab1(); // form.reflow(false); } private void onModifyContent() { if (_tourData == null) { return; } // update modified data updateModelFromUI(); enableActions(); fireModifyNotification(); } private void onResizeTab1() { _tab1Container.setMinSize(_tourContainer.computeSize(SWT.DEFAULT, SWT.DEFAULT)); } private void onResizeTabInfo() { // horizontal scroll bar ishidden, only the vertical scrollbar can be displayed int infoContainerWidth = _tab4Container.getBounds().width; final ScrollBar vertBar = _tab4Container.getVerticalBar(); if (vertBar != null) { // vertical bar is displayed infoContainerWidth -= vertBar.getSize().x; } final Point minSize = _infoContainer.computeSize(infoContainerWidth, SWT.DEFAULT); _tab4Container.setMinSize(minSize); } private void onSelectionChanged(final ISelection selection) { if (_isSavingInProgress) { return; } if (selection instanceof SelectionDeletedTours) { clearEditorContent(); return; } // ensure that the tour manager contains the same tour data if ((_tourData != null) && _isTourDirty) { try { TourManager.checkTourData(_tourData, getTourData(_tourData.getTourId())); } catch (final MyTourbookException e) { e.printStackTrace(); } } if (onSelectionChanged_isTourInSelection(selection)) { /* * tour in the selection is already displayed or a tour is not in the selection */ if (_isInfoInTitle) { showDefaultTitle(); } return; } else { // another tour is selected, show info if (_isTourDirty) { if (_isInfoInTitle == false) { /* * show info only when it is not yet displayed, this is an optimization because * setting the message causes an layout and this is EXTREMLY SLOW because of the * bad date time controls */ // hide title _pageEditorForm.setText(UI.EMPTY_STRING); // show info _messageManager.addMessage(MESSAGE_KEY_ANOTHER_SELECTION, NLS.bind(Messages.tour_editor_message_show_another_tour, getTourTitle()), null, IMessageProvider.ERROR); _isInfoInTitle = true; } return; } } if (_isInfoInTitle) { showDefaultTitle(); } if (selection instanceof SelectionTourData) { final SelectionTourData selectionTourData = (SelectionTourData) selection; final TourData tourData = selectionTourData.getTourData(); if (tourData == null) { _tourChart = null; } else { final TourChart tourChart = selectionTourData.getTourChart(); _tourChart = tourChart; updateUI_FromModel(tourData, false, true); } } else if (selection instanceof SelectionTourId) { displayTour(((SelectionTourId) selection).getTourId()); } else if (selection instanceof SelectionTourIds) { final ArrayList<Long> tourIds = ((SelectionTourIds) selection).getTourIds(); if ((tourIds != null) && (tourIds.size() > 0)) { displayTour(tourIds.get(0)); } } else if (selection instanceof SelectionTourMarker) { displayTour(((SelectionTourMarker) selection).getTourData()); } else if (selection instanceof SelectionTourCatalogView) { final SelectionTourCatalogView tourCatalogSelection = (SelectionTourCatalogView) selection; final TVICatalogRefTourItem refItem = tourCatalogSelection.getRefItem(); if (refItem != null) { displayTour(refItem.getTourId()); } } else if (selection instanceof SelectionChartInfo) { final ChartDataModel chartDataModel = ((SelectionChartInfo) selection).chartDataModel; if (chartDataModel != null) { final Object tourId = chartDataModel.getCustomData(Chart.CUSTOM_DATA_TOUR_ID); if (tourId instanceof Long) { final TourData tourData = getTourData((Long) tourId); if (tourData != null) { if (_tourData == null) { _tourData = tourData; _tourChart = null; updateUI_FromModel(tourData, false, true); } else { if (_tourData.getTourId().equals(tourData.getTourId())) { // a new tour id is in the selection _tourData = tourData; _tourChart = null; updateUI_FromModel(tourData, false, true); } } } } } } else if (selection instanceof StructuredSelection) { final Object firstElement = ((StructuredSelection) selection).getFirstElement(); if (firstElement instanceof TVICatalogComparedTour) { displayTour(((TVICatalogComparedTour) firstElement).getTourId()); } else if (firstElement instanceof TVICompareResultComparedTour) { displayTour(((TVICompareResultComparedTour) firstElement).getComparedTourData().getTourId()); } } } /** * Checks the selection if it contains the current tour, {@link #_selectionTourId} contains the * tour id which is within the selection * * @param selection * @return Returns <code>true</code> when the current tour is within the selection */ private boolean onSelectionChanged_isTourInSelection(final ISelection selection) { boolean isCurrentTourSelected = false; if (_tourData == null) { return false; } TourData selectedTourData = null; final long currentTourId = _tourData.getTourId(); if (selection instanceof SelectionTourData) { final TourData tourData = ((SelectionTourData) selection).getTourData(); if (tourData == null) { return false; } _selectionTourId = tourData.getTourId(); if ((tourData != null) && (currentTourId == _selectionTourId)) { isCurrentTourSelected = true; selectedTourData = tourData; } } else if (selection instanceof SelectionTourId) { _selectionTourId = ((SelectionTourId) selection).getTourId(); if (currentTourId == _selectionTourId) { isCurrentTourSelected = true; } } else if (selection instanceof SelectionTourIds) { final ArrayList<Long> tourIds = ((SelectionTourIds) selection).getTourIds(); if ((tourIds != null) && (tourIds.size() > 0)) { _selectionTourId = tourIds.get(0); if (currentTourId == _selectionTourId) { isCurrentTourSelected = true; } } } else if (selection instanceof SelectionTourMarker) { final TourData tourData = ((SelectionTourMarker) selection).getTourData(); if (tourData == null) { return false; } _selectionTourId = tourData.getTourId(); if ((tourData != null) && (currentTourId == _selectionTourId)) { isCurrentTourSelected = true; selectedTourData = tourData; } } else if (selection instanceof SelectionChartInfo) { final SelectionChartInfo chartInfo = (SelectionChartInfo) selection; final ChartDataModel chartDataModel = chartInfo.chartDataModel; if (chartDataModel != null) { final Object tourId = chartDataModel.getCustomData(Chart.CUSTOM_DATA_TOUR_ID); if (tourId instanceof Long) { final TourData tourData = getTourData((Long) tourId); if (tourData != null) { _selectionTourId = tourData.getTourId(); if (currentTourId == _selectionTourId) { isCurrentTourSelected = true; selectedTourData = tourData; // select time slices selectTimeSlice(chartInfo); } } } } } else if (selection instanceof SelectionChartXSliderPosition) { final SelectionChartXSliderPosition xSliderPosition = (SelectionChartXSliderPosition) selection; final Chart chart = xSliderPosition.getChart(); if (chart != null) { final ChartDataModel chartDataModel = chart.getChartDataModel(); if (chartDataModel != null) { final Object tourId = chartDataModel.getCustomData(Chart.CUSTOM_DATA_TOUR_ID); if (tourId instanceof Long) { final TourData tourData = getTourData((Long) tourId); if (tourData != null) { _selectionTourId = tourData.getTourId(); if (currentTourId == _selectionTourId) { isCurrentTourSelected = true; selectedTourData = tourData; // select time slices selectTimeSlice(xSliderPosition); } } } } } } else if (selection instanceof SelectionTourMarker) { final SelectionTourMarker markerSelection = (SelectionTourMarker) selection; final ArrayList<TourMarker> tourMarker = markerSelection.getSelectedTourMarker(); final int numberOfTourMarkers = tourMarker.size(); int leftSliderValueIndex = 0; int rightSliderValueIndex = 0; if (numberOfTourMarkers == 1) { leftSliderValueIndex = tourMarker.get(0).getSerieIndex(); rightSliderValueIndex = leftSliderValueIndex; } else if (numberOfTourMarkers > 1) { leftSliderValueIndex = tourMarker.get(0).getSerieIndex(); rightSliderValueIndex = tourMarker.get(numberOfTourMarkers - 1).getSerieIndex(); } selectTimeSlice_InViewer(leftSliderValueIndex, rightSliderValueIndex); } else if (selection instanceof StructuredSelection) { final Object firstElement = ((StructuredSelection) selection).getFirstElement(); if (firstElement instanceof TVICatalogComparedTour) { _selectionTourId = ((TVICatalogComparedTour) firstElement).getTourId(); if (currentTourId == _selectionTourId) { isCurrentTourSelected = true; } } else if (firstElement instanceof TVICompareResultComparedTour) { final long comparedTourTourId = ((TVICompareResultComparedTour) firstElement).getComparedTourData() .getTourId(); _selectionTourId = comparedTourTourId; if (currentTourId == _selectionTourId) { isCurrentTourSelected = true; } } } if (selectedTourData != null) { try { TourManager.checkTourData(selectedTourData, _tourData); } catch (final MyTourbookException e) { StatusUtil.log("Selection:" + selection, e);//$NON-NLS-1$ } } return isCurrentTourSelected; } private void onSelectionChanged_TourMarker(final SelectionTourMarker markerSelection) { final ArrayList<TourMarker> tourMarker = markerSelection.getSelectedTourMarker(); final int numberOfTourMarkers = tourMarker.size(); int leftSliderValueIndex = 0; int rightSliderValueIndex = 0; if (numberOfTourMarkers == 1) { leftSliderValueIndex = tourMarker.get(0).getSerieIndex(); rightSliderValueIndex = leftSliderValueIndex; } else if (numberOfTourMarkers > 1) { leftSliderValueIndex = tourMarker.get(0).getSerieIndex(); rightSliderValueIndex = tourMarker.get(numberOfTourMarkers - 1).getSerieIndex(); } selectTimeSlice_InViewer(leftSliderValueIndex, rightSliderValueIndex); } private void onSelectTab() { if (_tabFolder.getSelection() == _tabSlices) { if (_sliceViewerTourId == -1L) { // load viewer when this is was not yet done _sliceViewerTourId = _tourData.getTourId(); reloadViewer(); updateStatusLine(); // run asynch because relaodViewer is also running asynch Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { if (_sliceViewer.getTable().isDisposed()) { return; } selectTimeSlice(_sliceViewerXSliderPosition); _sliceViewer.getTable().setFocus(); } }); } } enableActions(); } private void onSelectWindDirectionText() { // N=0=0 NE=1=45 E=2=90 SE=3=135 S=4=180 SW=5=225 W=6=270 NW=7=315 final int selectedIndex = _comboWindDirectionText.getSelectionIndex(); // get degree from selected direction final int degree = selectedIndex * 45; _spinWindDirectionValue.setSelection(degree); } private void onSelectWindDirectionValue() { int degree = _spinWindDirectionValue.getSelection(); if (degree == -1) { degree = 359; _spinWindDirectionValue.setSelection(degree); } if (degree == 360) { degree = 0; _spinWindDirectionValue.setSelection(degree); } _comboWindDirectionText.select(getWindDirectionTextIndex(degree)); } private void onSelectWindSpeedText() { _isWindSpeedManuallyModified = true; final int selectedIndex = _comboWindSpeedText.getSelectionIndex(); final int speed = _unitValueWindSpeed[selectedIndex]; final boolean isBackup = _isSetField; _isSetField = true; { _spinWindSpeedValue.setSelection(speed); } _isSetField = isBackup; } private void onSelectWindSpeedValue() { _isWindSpeedManuallyModified = true; final int windSpeed = _spinWindSpeedValue.getSelection(); final boolean isBackup = _isSetField; _isSetField = true; { _comboWindSpeedText.select(getWindSpeedTextIndex(windSpeed)); } _isSetField = isBackup; } /* * this method is called when the application is shut down to save dirty tours or to cancel the * shutdown * @see org.eclipse.ui.ISaveablePart2#promptToSaveOnClose() */ @Override public int promptToSaveOnClose() { int returnCode; if (_isTourDirty == false) { returnCode = ISaveablePart2.NO; } _isSavingInProgress = true; { if (saveTourWithValidation()) { returnCode = ISaveablePart2.NO; } else { returnCode = ISaveablePart2.CANCEL; } } _isSavingInProgress = false; return returnCode; } private void recreateViewer() { // recreate slice viewer _sliceColumnManager.saveState(_stateSlice); _sliceColumnManager.clearColumns(); defineAllColumns(_sliceViewerContainer); _sliceViewer = (TableViewer) recreateViewer(_sliceViewer); } @Override public ColumnViewer recreateViewer(final ColumnViewer columnViewer) { final ColumnViewer[] newColumnViewer = new ColumnViewer[1]; BusyIndicator.showWhile(Display.getCurrent(), new Runnable() { private void recreateSliceViewer() { // preserve column width, selection and focus final ISelection selection = _sliceViewer.getSelection(); final Table table = _sliceViewer.getTable(); final boolean isFocus = table.isFocusControl(); _sliceViewerContainer.setRedraw(false); { table.dispose(); createUI_10_SliceViewer(_sliceViewerContainer); _sliceViewerContainer.layout(); // update the viewer _sliceViewerItems = getSliceViewerItems(); _sliceViewer.setInput(_sliceViewerItems); } _sliceViewerContainer.setRedraw(true); _sliceViewer.setSelection(selection, true); if (isFocus) { _sliceViewer.getTable().setFocus(); } newColumnViewer[0] = _sliceViewer; } @Override public void run() { if (columnViewer == _sliceViewer) { recreateSliceViewer(); } } }); return newColumnViewer[0]; } private TourData reloadTourData() { if (_tourData.getTourPerson() == null) { // tour is not saved, reloading tour data is not possible MessageDialog.openInformation(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_reload_data_title, Messages.tour_editor_dlg_reload_data_message); return _tourData; } final Long tourId = _tourData.getTourId(); final TourManager tourManager = TourManager.getInstance(); tourManager.removeTourFromCache(tourId); return tourManager.getTourDataFromDb(tourId); } /** * reload the content of the viewer */ @Override public void reloadViewer() { Display.getCurrent().asyncExec(new Runnable() { private void reloadSliceViewer() { final ISelection previousSelection = _sliceViewer.getSelection(); final Table table = _sliceViewer.getTable(); if (table.isDisposed()) { return; } table.setRedraw(false); { /* * update the viewer, show busy indicator when it's a large tour or the previous * tour was large because it takes time to remove the old items */ if (((_tourData != null) && (_tourData.timeSerie != null) && (_tourData.timeSerie.length > BUSY_INDICATOR_ITEMS)) || (table.getItemCount() > BUSY_INDICATOR_ITEMS)) { BusyIndicator.showWhile(Display.getCurrent(), new Runnable() { @Override public void run() { _sliceViewerItems = getSliceViewerItems(); _sliceViewer.setInput(_sliceViewerItems); } }); } else { _sliceViewerItems = getSliceViewerItems(); _sliceViewer.setInput(_sliceViewerItems); } _sliceViewer.setSelection(previousSelection, true); } table.setRedraw(true); } @Override public void run() { final CTabItem selectedTab = _tabFolder.getSelection(); if (selectedTab == _tabSlices) { reloadSliceViewer(); } } }); } private void restoreStateBeforeUI() { _isRowEditMode = _state.getBoolean(STATE_ROW_EDIT_MODE); _isEditMode = _state.getBoolean(STATE_IS_EDIT_MODE); _latLonDigits = Util.getStateInt(_state, STATE_LAT_LON_DIGITS, DEFAULT_LAT_LON_DIGITS); setup_LatLonDigits(); } private void restoreStateWithUI() { // select tab try { _tabFolder.setSelection(_state.getInt(STATE_SELECTED_TAB)); } catch (final NumberFormatException e) { _tabFolder.setSelection(_tabTour); } _actionToggleRowSelectMode.setChecked(_isRowEditMode); _actionToggleReadEditMode.setChecked(_isEditMode); _actionSetStartDistanceTo_0 .setText(NLS.bind(Messages.TourEditor_Action_SetStartDistanceTo0, UI.UNIT_LABEL_DISTANCE)); // _advMenuAddTag.setAutoOpen( // _prefStore.getBoolean(ITourbookPreferences.APPEARANCE_IS_TAGGING_AUTO_OPEN), // _prefStore.getInt(ITourbookPreferences.APPEARANCE_TAGGING_AUTO_OPEN_DELAY)); // expand/collapse sections _sectionCharacteristics.setExpanded(Util.getStateBoolean(_state, STATE_SECTION_CHARACTERISTICS, true)); _sectionDateTime.setExpanded(Util.getStateBoolean(_state, STATE_SECTION_DATE_TIME, true)); _sectionInfo.setExpanded(Util.getStateBoolean(_state, STATE_SECTION_INFO, true)); _sectionPersonal.setExpanded(Util.getStateBoolean(_state, STATE_SECTION_PERSONAL, true)); _sectionTitle.setExpanded(Util.getStateBoolean(_state, STATE_SECTION_TITLE, true)); _sectionWeather.setExpanded(Util.getStateBoolean(_state, STATE_SECTION_WEATHER, true)); } private void saveState() { // selected tab _state.put(STATE_SELECTED_TAB, _tabFolder.getSelectionIndex()); // row/column edit mode _state.put(STATE_IS_EDIT_MODE, _actionToggleReadEditMode.isChecked()); _state.put(STATE_ROW_EDIT_MODE, _actionToggleRowSelectMode.isChecked()); // viewer state _sliceColumnManager.saveState(_stateSlice); // editor state _state.put(STATE_SECTION_CHARACTERISTICS, _sectionCharacteristics.isExpanded()); _state.put(STATE_SECTION_DATE_TIME, _sectionDateTime.isExpanded()); _state.put(STATE_SECTION_INFO, _sectionInfo.isExpanded()); _state.put(STATE_SECTION_PERSONAL, _sectionPersonal.isExpanded()); _state.put(STATE_SECTION_TITLE, _sectionTitle.isExpanded()); _state.put(STATE_SECTION_WEATHER, _sectionWeather.isExpanded()); } /** * @param isConfirmSave * @return Returns <code>true</code> when the tour was saved, <code>false</code> when the tour * is not saved but canceled */ private boolean saveTourConfirmation() { if (_isTourDirty == false) { return true; } // show the tour data editor try { getSite().getPage().showView(ID, null, IWorkbenchPage.VIEW_VISIBLE); } catch (final PartInitException e) { e.printStackTrace(); } // confirm save/discard/cancel final int returnCode = new MessageDialog(Display.getCurrent().getActiveShell(), Messages.tour_editor_dlg_save_tour_title, null, NLS.bind(Messages.tour_editor_dlg_save_tour_message, TourManager.getTourDateFull(_tourData)), MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }, 0)// .open(); if (returnCode == 0) { // button YES: save tour saveTourIntoDB(); return true; } else if (returnCode == 1) { // button NO: discard modifications discardModifications(); return true; } else { // button CANCEL / dialog is canceled: tour is not saved and not discarded return false; } } /** * saves the tour when it is dirty, valid and confirmation is done */ private boolean saveTourIntoDB() { _isSavingInProgress = true; updateModelFromUI(); _tourData.computeAltitudeUpDown(); _tourData.computeTourDrivingTime(); _tourData.computeComputedValues(); /* * saveTour will check the tour editor dirty state, but when the tour is saved the dirty * flag can be set before to prevent an out of synch error */ _isTourDirty = false; _tourData = TourDatabase.saveTour(_tourData, true); updateMarkerMap(); // refresh combos if (_isTitleModified) { _comboTitle.clearSelection(); _comboTitle.removeAll(); // fill combobox final TreeSet<String> arr = TourDatabase.getAllTourTitles(); for (final String string : arr) { _comboTitle.add(string); } _comboTitle.update(); _isTitleModified = false; } if (_isLocationStartModified) { _comboLocation_Start.clearSelection(); _comboLocation_Start.removeAll(); // fill combobox final TreeSet<String> arr = TourDatabase.getAllTourPlaceStarts(); for (final String string : arr) { _comboLocation_Start.add(string); } _comboLocation_Start.update(); _isLocationStartModified = false; } if (_isLocationEndModified) { _comboLocation_End.clearSelection(); _comboLocation_End.removeAll(); // fill combobox final TreeSet<String> arr = TourDatabase.getAllTourPlaceEnds(); for (final String string : arr) { _comboLocation_End.add(string); } _comboLocation_End.update(); _isLocationEndModified = false; } setTourClean(); // notify all views which display the tour type TourManager.fireEvent(TourEventId.TOUR_CHANGED, new TourEvent(_tourData), TourDataEditorView.this); _isSavingInProgress = false; return true; } /** * saves the tour in the {@link TourDataEditorView} * * @return Returns <code>true</code> when the tour is saved or <code>false</code> when the tour * could not saved because the user canceled saving */ private boolean saveTourWithValidation() { if (_tourData == null) { return true; } if (isTourValid()) { return saveTourConfirmation(); } else { // tour is invalid if (isDiscardTour()) { // discard modifications discardModifications(); return true; } else { /* * tour is not saved because the tour is invalid and should not be discarded */ return false; } } } private void selectTimeSlice(final SelectionChartInfo chartInfo) { final Table table = (Table) _sliceViewer.getControl(); final int itemCount = table.getItemCount(); // adjust to array bounds int valueIndex = chartInfo.selectedSliderValuesIndex; valueIndex = Math.max(0, Math.min(valueIndex, itemCount - 1)); table.setSelection(valueIndex); table.showSelection(); // fire slider position // fDataViewer.setSelection(fDataViewer.getSelection()); } private void selectTimeSlice(final SelectionChartXSliderPosition sliderPosition) { if (sliderPosition == null) { return; } int valueIndexStart = sliderPosition.getLeftSliderValueIndex(); final int valueIndexEnd = sliderPosition.getRightSliderValueIndex(); final boolean isAdjustStartIndex = sliderPosition.isAdjustStartIndex(); if (isAdjustStartIndex && (valueIndexStart < valueIndexEnd && valueIndexStart != 0)) { valueIndexStart++; } selectTimeSlice_InViewer(valueIndexStart, valueIndexEnd); } /** * @param valueIndexStart * Can be {@link SelectionChartXSliderPosition#IGNORE_SLIDER_POSITION} when this * position should not be set. * @param valueIndexEnd * Can be {@link SelectionChartXSliderPosition#IGNORE_SLIDER_POSITION} when this * position should not be set. */ private void selectTimeSlice_InViewer(final int valueIndexStart, final int valueIndexEnd) { final Table table = (Table) _sliceViewer.getControl(); final int itemCount = table.getItemCount(); // adjust to array bounds final int checkedValueIndex1 = Math.max(0, Math.min(valueIndexStart, itemCount - 1)); final int checkedValueIndex2 = Math.max(0, Math.min(valueIndexEnd, itemCount - 1)); if ((valueIndexStart == SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION) && (valueIndexStart == SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION)) { return; } if (valueIndexStart == SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION) { table.setSelection(checkedValueIndex2); } else if (valueIndexEnd == SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION) { table.setSelection(checkedValueIndex1); } else { table.setSelection(checkedValueIndex1, checkedValueIndex2); } table.showSelection(); } @Override public void setFocus() { // !!! disabled because the first field gets the focus !!! // fTabFolder.setFocus(); _pageEditorForm.setFocus(); } /** * removes the dirty state from the tour editor, updates the save/undo actions and updates the * part name */ private void setTourClean() { _isTourDirty = false; _isAltitudeManuallyModified = false; _isDistManuallyModified = false; _isTemperatureManuallyModified = false; _isTimeZoneManuallyModified = false; _isWindSpeedManuallyModified = false; enableActions(); enableControls(); _messageManager.removeAllMessages(); showDefaultTitle(); /* * this is not an eclipse editor part but the property change must be fired to hide the "*" * marker in the part name */ firePropertyChange(PROP_DIRTY); } /** * Set {@link TourData} for the editor, when the editor is dirty, nothing is done, the calling * method must check if the tour editor is dirty * * @param tourDataForEditor */ public void setTourData(final TourData tourDataForEditor) { if (_isTourDirty) { return; } _tourChart = null; _postSelectionProvider.clearSelection(); updateUI_FromModel(tourDataForEditor, false, true); } /** * sets the tour editor dirty, updates the save/undo actions and updates the part name */ private void setTourDirty() { if (_isTourDirty) { return; } _isTourDirty = true; enableActions(); /* * this is not an eclipse editor part but the property change must be fired to show the * start "*" marker in the part name */ firePropertyChange(PROP_DIRTY); } /** * Setup the geo position formatter. */ private void setup_LatLonDigits() { _nfLatLon.setMinimumFractionDigits(_latLonDigits); _nfLatLon.setMaximumFractionDigits(_latLonDigits); } /** * show the default title in the editor */ private void showDefaultTitle() { _messageManager.removeMessage(MESSAGE_KEY_ANOTHER_SELECTION); updateUI_Title(); _isInfoInTitle = false; } @Override public void toursAreModified(final ArrayList<TourData> modifiedTours) { if ((modifiedTours != null) && (modifiedTours.size() > 0)) { // check if it's the correct tour if (_tourData == modifiedTours.get(0)) { // tour type or tags can have been changed within this dialog updateUI_TourTypeTags(); setTourDirty(); } } } @Override public void updateColumnHeader(final ColumnDefinition colDef) { } private void updateInternalUnitValues() { _unitValueDistance = net.tourbook.ui.UI.UNIT_VALUE_DISTANCE; _unitValueAltitude = net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE; _unitValueWindSpeed = net.tourbook.ui.UI.UNIT_VALUE_DISTANCE == 1 ? IWeather.windSpeedKmh : IWeather.windSpeedMph; } /** * converts {@link TourMarker} from {@link #_tourData} into the map {@link #_markerMap} */ void updateMarkerMap() { _markerMap.clear(); final Set<TourMarker> tourMarkers = _tourData.getTourMarkers(); for (final TourMarker tourMarker : tourMarkers) { _markerMap.put(tourMarker.getSerieIndex(), tourMarker); } } /** * Update {@link TourData} from the UI fields. */ private void updateModelFromUI() { if (_tourData == null) { return; } try { _tourData.setTourTitle(_comboTitle.getText()); _tourData.setTourDescription(_txtDescription.getText()); _tourData.setTourStartPlace(_comboLocation_Start.getText()); _tourData.setTourEndPlace(_comboLocation_End.getText()); _tourData.setBodyWeight((float) (_spinBodyWeight.getSelection() / 10.0)); _tourData.setPower_FTP(_spinFTP.getSelection()); _tourData.setCalories(_spinCalories.getSelection()); _tourData.setRestPulse(_spinRestPuls.getSelection()); _tourData.setCadenceMultiplier(_rdoCadence_Rpm.getSelection() ? 1.0f : 2.0f); _tourData.setWeather(_txtWeather.getText().trim()); _tourData.setWeatherWindDir(_spinWindDirectionValue.getSelection()); if (_isWindSpeedManuallyModified) { /* * update the speed only when it was modified because when the measurement is * changed when the tour is being modified then the computation of the speed value * can cause rounding errors */ _tourData.setWeatherWindSpeed((int) (_spinWindSpeedValue.getSelection() * _unitValueDistance)); } final int cloudIndex = _comboClouds.getSelectionIndex(); String cloudValue = IWeather.cloudIcon[cloudIndex]; if (cloudValue.equals(UI.IMAGE_EMPTY_16)) { // replace invalid cloud key cloudValue = UI.EMPTY_STRING; } _tourData.setWeatherClouds(cloudValue); if (_isTemperatureManuallyModified) { final float temperature = (float) _spinTemperature.getSelection() / 10; _tourData.setAvgTemperature(UI.convertTemperatureToMetric(temperature)); } // set time zone BEFORE the time is set if (_isTimeZoneManuallyModified) { // set time zone ONLY when manually modified final int selectedTimeZoneIndex = _comboTimeZone.getSelectionIndex(); final TimeZoneData timeZoneData = TimeTools.getTimeZone_ByIndex(selectedTimeZoneIndex); final String timeZoneId = timeZoneData.zoneId; _tourData.setTimeZoneId(timeZoneId); } final ZonedDateTime tourStartTime = ZonedDateTime.of(_dtTourDate.getYear(), _dtTourDate.getMonth() + 1, _dtTourDate.getDay(), _dtStartTime.getHours(), _dtStartTime.getMinutes(), _dtStartTime.getSeconds(), 0, _tourData.getTimeZoneIdWithDefault()); _tourData.setTourStartTime(tourStartTime); // distance if (_isDistManuallyModified) { /* * update the distance only when it was modified because when the measurement is * changed when the tour is being modified then the computation of the distance * value can cause rounding errors */ final float distanceValue = getFloatValue(_txtDistance.getText()) * _unitValueDistance * 1000; _tourData.setTourDistance(distanceValue); } // altitude if (_isAltitudeManuallyModified) { /* * update the altitude only when it was modified because when the measurement is * changed and the tour is modified, the computation of the distance value can cause * rounding errors */ float altitudeUpValue = getFloatValue(_txtAltitudeUp.getText()); float altitudeDownValue = getFloatValue(_txtAltitudeDown.getText()); if (_unitValueAltitude != 1) { // none metric measurement system // ensure float is used float noneMetricValue = altitudeUpValue; altitudeUpValue = Math.round(noneMetricValue * _unitValueAltitude); noneMetricValue = altitudeDownValue; altitudeDownValue = Math.round(noneMetricValue * _unitValueAltitude); } _tourData.setTourAltUp((int) altitudeUpValue); _tourData.setTourAltDown((int) altitudeDownValue); } // manual tour if (_isManualTour) { _tourData.setTourRecordingTime(_timeRecording.getTime()); _tourData.setTourDrivingTime(_timeDriving.getTime()); } } catch (final IllegalArgumentException e) { // this should not happen (but it happend when developing the tour data editor :-) // // wrong characters are entered, display an error message MessageDialog.openError(Display.getCurrent().getActiveShell(), "Error", e.getLocalizedMessage());//$NON-NLS-1$ StatusUtil.log(e); } } private void updateStatusLine() { final boolean isVisible = _timeSliceLabel.isVisible(); boolean setVisible = false; if (_isReferenceTourAvailable) { // tour contains reference tours _timeSliceLabel.setText(Messages.TourDataEditorView_tour_editor_status_tour_contains_ref_tour); setVisible = true; } else { _timeSliceLabel.setText(UI.EMPTY_STRING); } if (isVisible != setVisible) { // changes visibility _timeSliceLabel.setVisible(setVisible); _tab3Container.layout(true, true); } } /** * Updates the UI from {@link TourData}, dirty flag is not set * * @param tourData */ public void updateUI(final TourData tourData) { updateUI_FromModel(tourData, true, true); } /** * @param tourData * @param isDirty * When <code>true</code>, the tour is set to be dirty */ public void updateUI(final TourData tourData, final boolean isDirty) { updateUI(tourData); if (isDirty) { setTourDirty(); } } private void updateUI_AfterDistanceModifications() { updateUI_AfterSliceEdit(); // update distance in the UI, this must be done after updateUIAfterSliceEdit() updateUI_Tab_1_Tour(); /* * set slice viewer dirty when the time slice tab is not selected -> slice viewer was not * updated in updateUIAfterSliceEdit() */ if (_tabFolder.getSelection() != _tabSlices) { _sliceViewerTourId = -1; } } private void updateUI_AfterSliceEdit() { setTourDirty(); _tourData.clearComputedSeries(); getDataSeriesFromTourData(); // refresh the whole viewer because the computed data series could have been changed final ColumnViewer viewer = getViewer(); if (viewer != null) { viewer.refresh(); } // display modified time slices in other views fireModifyNotification(); } /** * updates the fields in the tour data editor and enables actions and controls * * @param tourData * @param forceTimeSliceReload * <code>true</code> will reload time slices * @param isDirtyDisabled */ private void updateUI_FromModel(final TourData tourData, final boolean forceTimeSliceReload, final boolean isDirtyDisabled) { if (tourData == null) { _pageBook.showPage(_pageNoData); return; } _uiUpdateCounter++; /* * set tour data because the TOUR_PROPERTIES_CHANGED event can occure before the runnable is * executed, this ensures that tour data is already set even if the ui is not yet updated */ _tourData = tourData; // get manual/device mode _isManualTour = tourData.isManualTour(); updateMarkerMap(); Display.getDefault().asyncExec(new Runnable() { final int __runnableCounter = _uiUpdateCounter; @Override public void run() { /* * update the UI */ // check if this is the last runnable if (__runnableCounter != _uiUpdateCounter) { // a new runnable was created return; } _uiRunnableTourData = tourData; _uiRunnableForceTimeSliceReload = forceTimeSliceReload; _uiRunnableIsDirtyDisabled = isDirtyDisabled; // force reload _uiRunnableCounter = _uiUpdateCounter - 1; if (_isPartVisible) { updateUI_FromModelRunnable(); } } }); } private void updateUI_FromModelRunnable() { if (_uiRunnableCounter == _uiUpdateCounter) { // UI is updated return; } _uiRunnableCounter = _uiUpdateCounter; if (_pageEditorForm.isDisposed() || (_uiRunnableTourData == null)) { // widget is disposed or data is not set return; } _isSetField = _uiRunnableIsDirtyDisabled; // keep tour data _tourData = _uiRunnableTourData; updateMarkerMap(); // a tour which is not saved has no tour references _isReferenceTourAvailable = _tourData.isContainReferenceTour(); // show tour type image when tour type is set final TourType tourType = _uiRunnableTourData.getTourType(); if (tourType == null) { _pageEditorForm.setImage(null); } else { _pageEditorForm.setImage(net.tourbook.ui.UI.getInstance().getTourTypeImage(tourType.getTypeId())); } updateUI_TitleAsynch(getTourTitle()); updateUI_Tab_1_Tour(); updateUI_Tab_2_Slices(); updateUI_Tab_3_Info(); enableActions(); enableControls(); // this action displays selected unit label _actionSetStartDistanceTo_0 .setText(NLS.bind(Messages.TourEditor_Action_SetStartDistanceTo0, UI.UNIT_LABEL_DISTANCE)); // show editor page _pageBook.showPage(_pageEditorForm); _isSetField = false; } void updateUI_LatLonDigits(final int selectedDigits) { if (selectedDigits == _latLonDigits) { // nothing has changed return; } _latLonDigits = selectedDigits; setup_LatLonDigits(); _sliceViewer.getControl().setRedraw(false); { _sliceViewer.refresh(true); } _sliceViewer.getControl().setRedraw(true); } private void updateUI_RefTourInfo(final Collection<TourReference> refTours) { final ArrayList<TourReference> refTourList = new ArrayList<TourReference>(refTours); // sort reference tours by start index Collections.sort(refTourList, new Comparator<TourReference>() { @Override public int compare(final TourReference refTour1, final TourReference refTour2) { return refTour1.getStartValueIndex() - refTour2.getStartValueIndex(); } }); final StringBuilder sb = new StringBuilder(); int refCounter = 0; _refTourRange = new int[refTourList.size()][2]; for (final TourReference refTour : refTourList) { if (refCounter > 0) { sb.append(UI.NEW_LINE); } sb.append(refTour.getLabel()); sb.append(" ("); //$NON-NLS-1$ sb.append(refTour.getStartValueIndex()); sb.append(UI.DASH_WITH_SPACE); sb.append(refTour.getEndValueIndex()); sb.append(")"); //$NON-NLS-1$ final int[] oneRange = _refTourRange[refCounter]; oneRange[0] = refTour.getStartValueIndex(); oneRange[1] = refTour.getEndValueIndex(); refCounter++; } _txtRefTour.setText(sb.toString()); // _txtRefTour.pack(true); _txtRefTour.getParent().layout(true, true); } private void updateUI_Tab_1_Tour() { /* * tour/event */ // title/description _comboTitle.setText(_tourData.getTourTitle()); _txtDescription.setText(_tourData.getTourDescription()); // start/end location _comboLocation_Start.setText(_tourData.getTourStartPlace()); _comboLocation_End.setText(_tourData.getTourEndPlace()); /* * personal details */ _spinBodyWeight.setSelection(Math.round(_tourData.getBodyWeight() * 10)); _spinFTP.setSelection(_tourData.getPower_FTP()); _spinRestPuls.setSelection(_tourData.getRestPulse()); _spinCalories.setSelection(_tourData.getCalories()); /* * wind properties */ _txtWeather.setText(_tourData.getWeather()); // wind direction final int weatherWindDirDegree = _tourData.getWeatherWindDir(); _spinWindDirectionValue.setSelection(weatherWindDirDegree); _comboWindDirectionText.select(getWindDirectionTextIndex(weatherWindDirDegree)); // wind speed final int windSpeed = _tourData.getWeatherWindSpeed(); final int speed = (int) (windSpeed / _unitValueDistance); _spinWindSpeedValue.setSelection(speed); _comboWindSpeedText.select(getWindSpeedTextIndex(speed)); // weather clouds _comboClouds.select(_tourData.getWeatherIndex()); // icon must be displayed after the combobox entry is selected displayCloudIcon(); /* * avg temperature */ final float avgTemperature = UI.convertTemperatureFromMetric(_tourData.getAvgTemperature()); /* * on Linux .setDigit() fires an asynch selection event that the flag _isSetField is not * recognized */ _isSetDigits = true; _spinTemperature.setDigits(1); _spinTemperature.setSelection((int) ((avgTemperature * 10) + 0.5)); // set start date/time without time zone final ZonedDateTime tourStartTime = _tourData.getTourStartTime(); _dtTourDate.setDate(tourStartTime.getYear(), tourStartTime.getMonthValue() - 1, tourStartTime.getDayOfMonth()); _dtStartTime.setTime(tourStartTime.getHour(), tourStartTime.getMinute(), tourStartTime.getSecond()); // tour distance final float tourDistance = _tourData.getTourDistance(); if (tourDistance == 0) { _txtDistance.setText(Integer.toString(0)); } else { final float distance = tourDistance / 1000 / _unitValueDistance; _txtDistance.setText(_nf3NoGroup.format(distance)); } // altitude up/down final int altitudeUp = _tourData.getTourAltUp(); final int altitudeDown = _tourData.getTourAltDown(); _txtAltitudeUp.setText(Integer.toString((int) (altitudeUp / _unitValueAltitude))); _txtAltitudeDown.setText(Integer.toString((int) (altitudeDown / _unitValueAltitude))); // tour time's final int recordingTime = (int) _tourData.getTourRecordingTime(); final int drivingTime = (int) _tourData.getTourDrivingTime(); final int pausedTime = recordingTime - drivingTime; _timeRecording.setTime(recordingTime); _timeDriving.setTime(drivingTime); _timePaused.setTime(pausedTime); /* * Time zone */ int timeZoneIndex; final String timeZoneId = _tourData.getTimeZoneId(); if (timeZoneId == null) { // time zone is not set, try to get it from the geo position double lat0 = Double.MIN_VALUE; double lat1 = Double.MIN_VALUE; if (_tourData.latitudeSerie != null && _tourData.latitudeSerie.length > 0) { lat0 = _tourData.latitudeSerie[0]; lat1 = _tourData.longitudeSerie[0]; } timeZoneIndex = TimeTools.getTimeZoneIndex(lat0, lat1); } else { timeZoneIndex = TimeTools.getTimeZoneIndex_WithDefault(timeZoneId); } _comboTimeZone.select(timeZoneIndex); _linkDefaultTimeZone.setToolTipText( NLS.bind(Messages.Tour_Editor_Link_SetDefautTimeZone_Tooltip, TimeTools.getDefaultTimeZoneId())); updateUI_TimeZone(); // tour type/tags net.tourbook.ui.UI.updateUI_TourType(_tourData, _lblTourType, true); net.tourbook.ui.UI.updateUI_Tags(_tourData, _lblTags); // measurement system _lblDistanceUnit.setText(UI.UNIT_LABEL_DISTANCE); _lblAltitudeUpUnit.setText(UI.UNIT_LABEL_ALTITUDE); _lblAltitudeDownUnit.setText(UI.UNIT_LABEL_ALTITUDE); _lblTemperatureUnit.setText(UI.UNIT_LABEL_TEMPERATURE); _lblSpeedUnit.setText(UI.UNIT_LABEL_SPEED); // cadence rpm/spm final float cadence = _tourData.getCadenceMultiplier(); final boolean isSpm = cadence == 2.0f; _rdoCadence_Rpm.setSelection(!isSpm); _rdoCadence_Spm.setSelection(isSpm); /* * layout container to resize labels */ _tourContainer.layout(true); } private void updateUI_Tab_2_Slices() { if (_uiRunnableForceTimeSliceReload) { _sliceViewerTourId = -1L; } if ((_tabFolder.getSelection() == _tabSlices) && (_sliceViewerTourId != _tourData.getTourId())) { /* * time slice tab is selected and the viewer is not yeat loaded */ reloadViewer(); _sliceViewerTourId = _tourData.getTourId(); updateStatusLine(); } else { if (_sliceViewerTourId != _tourData.getTourId()) { // force reload when it's not yet loaded _sliceViewerTourId = -1L; } } } private void updateUI_Tab_3_Info() { // data points final int[] timeSerie = _tourData.timeSerie; if (timeSerie == null) { _txtTimeSlicesCount.setText(UI.EMPTY_STRING); } else { final int dataPoints = timeSerie.length; _txtTimeSlicesCount.setText(Integer.toString(dataPoints)); } _txtTimeSlicesCount.pack(true); // device name/version _txtDeviceName.setText(_tourData.getDeviceName()); _txtDeviceName.pack(true); _txtDeviceFirmwareVersion.setText(_tourData.getDeviceFirmwareVersion()); _txtDeviceFirmwareVersion.pack(true); // distance sensor _txtDistanceSensor.setText(_tourData.isDistanceSensorPresent() ? Messages.Tour_Editor_Label_Sensor_Yes : Messages.Tour_Editor_Label_Sensor_No); // stride sensor _txtStrideSensor.setText(_tourData.isStrideSensorPresent() ? Messages.Tour_Editor_Label_Sensor_Yes : Messages.Tour_Editor_Label_Sensor_No); // pulse sensor _txtPulseSensor.setText(_tourData.isPulseSensorPresent() ? Messages.Tour_Editor_Label_Sensor_Yes : Messages.Tour_Editor_Label_Sensor_No); // power sensor _txtPowerSensor.setText(_tourData.isPowerSensorPresent() ? Messages.Tour_Editor_Label_Sensor_Yes : Messages.Tour_Editor_Label_Sensor_No); // import file path final String importFilePath = _tourData.getImportFilePathNameText(); _txtImportFilePath.setText(importFilePath); _txtImportFilePath.setToolTipText(importFilePath); /* * reference tours */ final Collection<TourReference> refTours = _tourData.getTourReferences(); if (refTours.size() > 0) { updateUI_RefTourInfo(refTours); } else { _txtRefTour.setText(Messages.tour_editor_label_ref_tour_none); _refTourRange = null; } /* * person */ final TourPerson tourPerson = _tourData.getTourPerson(); if (tourPerson == null) { _txtPerson.setText(UI.EMPTY_STRING); } else { _txtPerson.setText(tourPerson.getName()); } /* * tour ID */ final Long tourId = _tourData.getTourId(); if (tourId == null) { _txtTourId.setText(UI.EMPTY_STRING); } else { _txtTourId.setText(Long.toString(tourId)); } /* * date/time created */ final ZonedDateTime dtCreated = _tourData.getDateTimeCreated(); _txtDateTimeCreated.setText(dtCreated == null ? // UI.EMPTY_STRING : dtCreated.format(TimeTools.Formatter_DateTime_M)); /* * date/time modified */ final ZonedDateTime dtModified = _tourData.getDateTimeModified(); _txtDateTimeModified.setText(dtModified == null ? // UI.EMPTY_STRING : dtModified.format(TimeTools.Formatter_DateTime_M)); /* * merge from tour ID */ final Long mergeFromTourId = _tourData.getMergeSourceTourId(); if (mergeFromTourId == null) { _txtMergeFromTourId.setText(UI.EMPTY_STRING); } else { _txtMergeFromTourId.setText(Long.toString(mergeFromTourId)); } /* * merge into tour ID */ final Long mergeIntoTourId = _tourData.getMergeTargetTourId(); if (mergeIntoTourId == null) { _txtMergeIntoTourId.setText(UI.EMPTY_STRING); } else { _txtMergeIntoTourId.setText(Long.toString(mergeIntoTourId)); } /* * layout container to resize the labels */ onResizeTabInfo(); } /** * Validate tour recording/driving/paused time */ private void updateUI_Time(final Widget widget) { /* * check if a time control is currently used */ if ((widget == _timeRecording._spinHours || widget == _timeRecording._spinMinutes || widget == _timeRecording._spinSeconds || widget == _timeDriving._spinHours || widget == _timeDriving._spinMinutes || widget == _timeDriving._spinSeconds || widget == _timePaused._spinHours || widget == _timePaused._spinMinutes || widget == _timePaused._spinSeconds) == false) { return; } int recTime = _timeRecording.getTime(); int pausedTime = _timePaused.getTime(); int driveTime = _timeDriving.getTime(); if (recTime < 0) { recTime = -recTime - 1; } if (pausedTime < 0) { pausedTime = 0; } if (widget == _timeRecording._spinHours || widget == _timeRecording._spinMinutes || widget == _timeRecording._spinSeconds) { // recording time is modified if (pausedTime > recTime) { pausedTime = recTime; } driveTime = recTime - pausedTime; } else if (widget == _timePaused._spinHours || widget == _timePaused._spinMinutes || widget == _timePaused._spinSeconds) { // paused time is modified if (pausedTime > recTime) { recTime = pausedTime; } driveTime = recTime - pausedTime; } else if (widget == _timeDriving._spinHours || widget == _timeDriving._spinMinutes || widget == _timeDriving._spinSeconds) { // driving time is modified if (driveTime > recTime) { recTime = driveTime; } pausedTime = recTime - driveTime; } _timeRecording.setTime(recTime / 3600, ((recTime % 3600) / 60), ((recTime % 3600) % 60)); _timeDriving.setTime(driveTime / 3600, ((driveTime % 3600) / 60), ((driveTime % 3600) % 60)); _timePaused.setTime(pausedTime / 3600, ((pausedTime % 3600) / 60), ((pausedTime % 3600) % 60)); } private void updateUI_TimeZone() { /* * Update tooltip */ final ZonedDateTime tourStartTime = _tourData.getTourStartTime(); final ZonedDateTime tourStartTimeUTC = tourStartTime.withZoneSameInstant(ZoneOffset.UTC); final String tourStartTooltip = NLS.bind(// Messages.Tour_Editor_Label_TourStartTime_Tooltip, tourStartTimeUTC.format(TimeTools.Formatter_DateTime_SM)); _lblStartTime.setToolTipText(tourStartTooltip); _lblTimeZone.setToolTipText(tourStartTooltip); _comboTimeZone.setToolTipText(tourStartTooltip); /* * Show/hide time zone decorator */ if (_tourData.hasATimeZone()) { // hide info _decoTimeZone.hide(); } else { // show info that a time zone is not set _decoTimeZone.show(); } } /** * Update title of the view with the modified date/time */ private void updateUI_Title() { final ZoneId zoneId = _tourData == null // ? TimeTools.getDefaultTimeZone() : _tourData.getTimeZoneIdWithDefault(); final ZonedDateTime tourStartTime = ZonedDateTime.of(_dtTourDate.getYear(), _dtTourDate.getMonth() + 1, _dtTourDate.getDay(), _dtStartTime.getHours(), _dtStartTime.getMinutes(), _dtStartTime.getSeconds(), 0, zoneId); final String tourTitle = TourManager.getTourTitle(tourStartTime); updateUI_TitleAsynch(tourTitle); } /** * update the title is a really performance hog because of the date/time controls when they are * layouted */ private void updateUI_TitleAsynch(final String title) { _uiUpdateTitleCounter++; Display.getCurrent().asyncExec(new Runnable() { final int runnableCounter = _uiUpdateTitleCounter; @Override public void run() { if (_pageEditorForm.isDisposed()) { return; } // check if this is the last runnable if (runnableCounter != _uiUpdateTitleCounter) { // a new runnable was created return; } _pageEditorForm.setText(title); } }); } private void updateUI_TourTypeTags() { // tour type/tags net.tourbook.ui.UI.updateUI_TourType(_tourData, _lblTourType, true); net.tourbook.ui.UI.updateUI_Tags(_tourData, _lblTags); // reflow layout that the tags are aligned correctly _tourContainer.layout(true); } private void writeCSVHeader(final Writer exportWriter, final StringBuilder sb) throws IOException { // no. sb.append("#"); //$NON-NLS-1$ sb.append(UI.TAB); // time hh:mm:ss sb.append("hh:mm:ss"); //$NON-NLS-1$ sb.append(UI.TAB); // time in seconds sb.append("sec"); //$NON-NLS-1$ sb.append(UI.TAB); // distance sb.append(UI.UNIT_LABEL_DISTANCE); sb.append(UI.TAB); // altitude sb.append(UI.UNIT_LABEL_ALTITUDE); sb.append(UI.TAB); // gradient sb.append("%"); //$NON-NLS-1$ sb.append(UI.TAB); // pulse sb.append("bpm"); //$NON-NLS-1$ sb.append(UI.TAB); // marker sb.append("marker"); //$NON-NLS-1$ sb.append(UI.TAB); // temperature sb.append(UI.UNIT_LABEL_TEMPERATURE); sb.append(UI.TAB); // cadence sb.append("rpm"); //$NON-NLS-1$ sb.append(UI.TAB); // speed sb.append(UI.UNIT_LABEL_SPEED); sb.append(UI.TAB); // pace sb.append(UI.UNIT_LABEL_PACE); sb.append(UI.TAB); // power sb.append("W"); //$NON-NLS-1$ sb.append(UI.TAB); // longitude sb.append("longitude"); //$NON-NLS-1$ sb.append(UI.TAB); // latitude sb.append("latitude"); //$NON-NLS-1$ sb.append(UI.TAB); // break time sb.append("break"); //$NON-NLS-1$ sb.append(UI.TAB); // end of line sb.append(net.tourbook.ui.UI.SYSTEM_NEW_LINE); exportWriter.write(sb.toString()); } }