net.tourbook.ui.views.TourSegmenterView.java Source code

Java tutorial

Introduction

Here is the source code for net.tourbook.ui.views.TourSegmenterView.java

Source

/*******************************************************************************
 * Copyright (C) 2005, 2011  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;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Set;

import net.tourbook.Messages;
import net.tourbook.algorithm.DPPoint;
import net.tourbook.algorithm.DouglasPeuckerSimplifier;
import net.tourbook.application.TourbookPlugin;
import net.tourbook.chart.SelectionChartXSliderPosition;
import net.tourbook.data.AltitudeUpDownSegment;
import net.tourbook.data.TourData;
import net.tourbook.data.TourMarker;
import net.tourbook.data.TourSegment;
import net.tourbook.database.MyTourbookException;
import net.tourbook.preferences.ITourbookPreferences;
import net.tourbook.preferences.PrefPageComputedValues;
import net.tourbook.tour.BreakTimeResult;
import net.tourbook.tour.BreakTimeTool;
import net.tourbook.tour.ITourEventListener;
import net.tourbook.tour.SelectionDeletedTours;
import net.tourbook.tour.SelectionTourData;
import net.tourbook.tour.SelectionTourId;
import net.tourbook.tour.SelectionTourIds;
import net.tourbook.tour.TourEditor;
import net.tourbook.tour.TourEvent;
import net.tourbook.tour.TourEventId;
import net.tourbook.tour.TourManager;
import net.tourbook.ui.ImageComboLabel;
import net.tourbook.ui.TableColumnFactory;
import net.tourbook.ui.UI;
import net.tourbook.ui.action.ActionModifyColumns;
import net.tourbook.ui.tourChart.TourChart;
import net.tourbook.ui.tourChart.TourChartView;
import net.tourbook.util.ArrayListToArray;
import net.tourbook.util.ColumnDefinition;
import net.tourbook.util.ColumnManager;
import net.tourbook.util.ITourViewer;
import net.tourbook.util.PostSelectionProvider;
import net.tourbook.util.Util;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.part.ViewPart;

/**
 *
 */
public class TourSegmenterView extends ViewPart implements ITourViewer {

    public static final String ID = "net.tourbook.views.TourSegmenter"; //$NON-NLS-1$

    private static final int SEGMENTER_REQUIRES_ALTITUDE = 0x01;
    private static final int SEGMENTER_REQUIRES_DISTANCE = 0x02;
    private static final int SEGMENTER_REQUIRES_PULSE = 0x04;
    private static final int SEGMENTER_REQUIRES_MARKER = 0x08;

    private static final int MAX_DISTANCE_SCALE_MILE = 80;
    private static final int MAX_DISTANCE_SCALE_METRIC = 100;

    private static final String STATE_SELECTED_SEGMENTER_INDEX = "selectedSegmenterIndex"; //$NON-NLS-1$
    private static final String STATE_SELECTED_MIN_ALTITUDE_INDEX = "selectedMinAltitude"; //$NON-NLS-1$
    private static final String STATE_SELECTED_DISTANCE = "selectedDistance"; //$NON-NLS-1$

    private static final String STATE_SELECTED_BREAK_METHOD = "selectedBreakMethod"; //$NON-NLS-1$
    private static final String STATE_BREAK_TIME_MIN_DISTANCE_VALUE = "selectedBreakTimeMinDistance"; //$NON-NLS-1$
    private static final String STATE_BREAK_TIME_MIN_TIME_VALUE = "selectedBreakTimeMinTime"; //$NON-NLS-1$
    private static final String STATE_BREAK_TIME_MIN_SLICE_SPEED = "selectedBreakTimeMinSliceSpeed"; //$NON-NLS-1$
    private static final String STATE_BREAK_TIME_MIN_AVG_SPEED = "selectedBreakTimeMinAvgSpeed"; //$NON-NLS-1$

    private static final int COLUMN_DEFAULT = 0; // sort by time
    private static final int COLUMN_SPEED = 10;
    private static final int COLUMN_PACE = 20;
    private static final int COLUMN_GRADIENT = 30;
    private static final int COLUMN_PULSE = 40;

    private static final float SPEED_DIGIT_VALUE = 10.0f;

    private final IPreferenceStore _prefStore = TourbookPlugin.getDefault().getPreferenceStore();
    private final IDialogSettings _state = TourbookPlugin.getDefault().getDialogSettingsSection(ID);

    private final boolean _isOSX = net.tourbook.util.UI.IS_OSX;

    private ColumnManager _columnManager;

    private TourData _tourData;

    private int _dpTolerance;
    private int _savedDpTolerance = -1;

    private PostSelectionProvider _postSelectionProvider;

    private ISelectionListener _postSelectionListener;
    private IPartListener2 _partListener;
    private IPropertyChangeListener _prefChangeListener;
    private ITourEventListener _tourEventListener;

    private final NumberFormat _nf_0_0 = NumberFormat.getNumberInstance();
    private final NumberFormat _nf_1_0 = NumberFormat.getNumberInstance();
    private final NumberFormat _nf_1_1 = NumberFormat.getNumberInstance();
    private final NumberFormat _nf_3_3 = NumberFormat.getNumberInstance();
    {
        _nf_0_0.setMinimumFractionDigits(0);
        _nf_0_0.setMaximumFractionDigits(0);

        _nf_1_0.setMinimumFractionDigits(1);
        _nf_1_0.setMaximumFractionDigits(0);

        _nf_1_1.setMinimumFractionDigits(1);
        _nf_1_1.setMaximumFractionDigits(1);

        _nf_3_3.setMinimumFractionDigits(3);
        _nf_3_3.setMaximumFractionDigits(3);
    }

    private int _maxDistanceScale;
    private int _scaleDistancePage;

    private boolean _isShowSegmentsInChart;
    private boolean _isTourDirty = false;
    private boolean _isSaving;

    /**
     * when <code>true</code>, the tour dirty flag is disabled to load data into the fields
     */
    private boolean _isDirtyDisabled = false;

    private boolean _isClearView;
    private int _altitudeUp;
    private int _altitudeDown;

    /**
     * contains all segmenters
     */
    private static HashMap<SegmenterType, TourSegmenter> _allTourSegmenter;

    private ArrayList<TourSegmenter> _availableSegmenter = new ArrayList<TourSegmenter>();

    /**
     * segmenter type which the user has selected
     */
    private SegmenterType _userSelectedSegmenterType;

    private long _tourBreakTime;
    private int _breakUIShortestBreakTime;
    private float _breakUIMaxDistance;
    private float _breakUIMinSliceSpeed;
    private float _breakUIMinAvgSpeed;

    private PixelConverter _pc;
    private int _spinnerWidth;

    /**
     * contains the controls which are displayed in the first column, these controls are used to get
     * the maximum width and set the first column within the differenct section to the same width
     */
    private final ArrayList<Control> _firstColBreakTime = new ArrayList<Control>();

    /*
     * UI controls
     */

    private PageBook _pageBookUI;
    private Composite _pageSegmenter;
    private Label _pageNoData;

    private PageBook _pageBookSegmenter;
    private Composite _pageSegTypeDP;
    private Composite _pageSegTypeByMarker;
    private Composite _pageSegTypeByDistance;
    private Composite _pageSegTypeByAltiUpDown;
    private Composite _pageSegTypeByBreakTime;

    private TableViewer _segmentViewer;

    private Scale _scaleDistance;
    private Label _lblDistanceValue;

    private Composite _containerViewer;
    private Composite _containerSaveAltitude;

    private Scale _scaleTolerance;
    private Label _lblToleranceValue;
    private ImageComboLabel _lblTitle;
    private Combo _cboSegmenterType;
    private Label _lblAltitudeUp;

    private Button _btnSaveTour;
    private Combo _cboMinAltitude;
    private Label _lblMinAltitude;

    private ActionShowSegments _actionShowSegments;

    /**
     * {@link TourChart} contains the chart for the tour, this is necessary to move the slider in
     * the chart to a selected segment
     */
    private TourChart _tourChart;

    /*
     * break time
     */
    private Composite _containerBreakTime;
    private PageBook _pagebookBreakTime;
    private Composite _pageBreakByTimeDistance;
    private Composite _pageBreakBySliceSpeed;
    private Composite _pageBreakByAvgSpeed;

    private Combo _comboBreakMethod;
    private Spinner _spinnerBreakMinSliceSpeed;
    private Spinner _spinnerBreakMinAvgSpeed;
    private Spinner _spinnerBreakShortestTime;
    private Spinner _spinnerBreakMaxDistance;
    private Label _lblTourBreakTime;
    private Label _lblBreakDistanceUnit;

    private class ActionShowSegments extends Action {

        public ActionShowSegments() {

            super(Messages.App_Action_open_tour_segmenter, SWT.TOGGLE);
            setToolTipText(Messages.App_Action_open_tour_segmenter_tooltip);

            setImageDescriptor(TourbookPlugin.getImageDescriptor(Messages.Image__tour_segmenter));
        }

        @Override
        public void run() {
            _isShowSegmentsInChart = !_isShowSegmentsInChart;
            fireSegmentLayerChanged();
        }
    }

    public static enum SegmenterType {
        ByAltitudeWithDP, //
        ByPulseWithDP, //
        ByMarker, //
        ByDistance, //
        ByComputedAltiUpDown, //
        ByBreakTime,
    }

    /**
     * The content provider class is responsible for providing objects to the view. It can wrap
     * existing objects in adapters or simply return objects as-is. These objects may be sensitive
     * to the current input of the view, or ignore it and always show the same content (like Task
     * List, for example).
     */
    class ViewContentProvider implements IStructuredContentProvider {

        public ViewContentProvider() {
        }

        public void dispose() {
        }

        public Object[] getElements(final Object parent) {
            if (_tourData == null) {
                return new Object[0];
            } else {

                final Object[] tourSegments = createSegmenterContent();

                updateUIAltitude();
                updateUIBreakTime();

                return tourSegments;
            }
        }

        public void inputChanged(final Viewer v, final Object oldInput, final Object newInput) {
        }
    }

    private static class ViewSorter extends ViewerSorter {

        // private static final int ASCENDING = 0;

        private static final int DESCENDING = 1;

        private int column;

        private int direction;

        /**
         * Compares the object for sorting
         */
        @Override
        public int compare(final Viewer viewer, final Object obj1, final Object obj2) {

            final TourSegment segment1 = ((TourSegment) obj1);
            final TourSegment segment2 = ((TourSegment) obj2);

            int rc = 0;

            // Determine which column and do the appropriate sort
            switch (column) {
            case COLUMN_DEFAULT:
                rc = segment1.serieIndexStart - segment2.serieIndexStart;
                if (direction == DESCENDING) {
                    rc = -rc;
                }
                break;

            case COLUMN_SPEED:
                rc = (int) ((segment1.speed - segment2.speed) * 100);
                break;

            case COLUMN_PACE:
                rc = ((segment1.pace - segment2.pace) * 100);
                break;

            case COLUMN_PULSE:
                rc = segment1.pulse - segment2.pulse;
                break;

            case COLUMN_GRADIENT:
                rc = (int) ((segment1.gradient - segment2.gradient) * 100);
                break;
            }

            // If descending order, flip the direction
            if (direction == DESCENDING) {
                rc = -rc;
            }

            return rc;
        }

        /**
         * Does the sort. If it's a different column from the previous sort, do an ascending sort.
         * If it's the same column as the last sort, toggle the sort direction.
         * 
         * @param column
         */
        public void setSortColumn(final int column) {

            if (column == this.column) {
                // Same column as last sort; toggle the direction
                direction = 1 - direction;
            } else {
                // New column; do an descending sort
                this.column = column;
                direction = DESCENDING;
            }
        }
    }

    /**
     * Constructor
     */
    public TourSegmenterView() {
        super();
    }

    private void addPartListener() {

        // set the part listener
        _partListener = new IPartListener2() {
            public void partActivated(final IWorkbenchPartReference partRef) {
            }

            public void partBroughtToTop(final IWorkbenchPartReference partRef) {
            }

            public void partClosed(final IWorkbenchPartReference partRef) {
                if (partRef.getPart(false) == TourSegmenterView.this) {
                    saveTour();
                    saveState();
                    hideTourSegmentsInChart();
                }
            }

            public void partDeactivated(final IWorkbenchPartReference partRef) {
            }

            public void partHidden(final IWorkbenchPartReference partRef) {
            }

            public void partInputChanged(final IWorkbenchPartReference partRef) {
            }

            public void partOpened(final IWorkbenchPartReference partRef) {
            }

            public void partVisible(final IWorkbenchPartReference partRef) {
            }
        };

        getSite().getPage().addPartListener(_partListener);
    }

    private void addPrefListener() {

        _prefChangeListener = new IPropertyChangeListener() {
            public void propertyChange(final PropertyChangeEvent event) {

                final String property = event.getProperty();

                if (property.equals(ITourbookPreferences.MEASUREMENT_SYSTEM)) {

                    // measurement system has changed

                    UI.updateUnits();

                    /*
                     * update viewer
                     */
                    _columnManager.saveState(_state);
                    _columnManager.clearColumns();
                    defineAllColumns(_containerViewer);

                    recreateViewer(null);

                    /*
                     * update distance scale
                     */
                    setMaxDistanceScale();
                    _scaleDistance.setMaximum(_maxDistanceScale);
                    _scaleDistance.setPageIncrement(_scaleDistancePage);
                    updateUIDistance();

                    /*
                     * update min altitude
                     */
                    _lblMinAltitude.setText(UI.UNIT_LABEL_DISTANCE);
                    _lblMinAltitude.pack(true);
                    updateUIMinAltitude();

                    createSegments();

                } else if (property.equals(ITourbookPreferences.VIEW_LAYOUT_CHANGED)) {

                    _segmentViewer.getTable()
                            .setLinesVisible(_prefStore.getBoolean(ITourbookPreferences.VIEW_LAYOUT_DISPLAY_LINES));

                    _segmentViewer.refresh();

                    /*
                     * the tree must be redrawn because the styled text does not show with the new
                     * color
                     */
                    _segmentViewer.getTable().redraw();
                }
            }
        };

        _prefStore.addPropertyChangeListener(_prefChangeListener);
    }

    private void addSelectionListener() {

        _postSelectionListener = new ISelectionListener() {
            public void selectionChanged(final IWorkbenchPart part, final ISelection selection) {

                if (part == TourSegmenterView.this) {
                    return;
                }

                onSelectionChanged(selection);
            }
        };

        getSite().getPage().addPostSelectionListener(_postSelectionListener);
    }

    private void addTourEventListener() {

        _tourEventListener = new ITourEventListener() {
            public void tourChanged(final IWorkbenchPart part, final TourEventId eventId, final Object eventData) {

                if (_tourData == null || part == TourSegmenterView.this) {
                    return;
                }

                if (eventId == TourEventId.TOUR_CHANGED && eventData instanceof TourEvent) {

                    final TourEvent tourEvent = (TourEvent) eventData;
                    final ArrayList<TourData> modifiedTours = tourEvent.getModifiedTours();

                    if (modifiedTours == null || modifiedTours.size() == 0) {
                        return;
                    }

                    final TourData modifiedTourData = modifiedTours.get(0);
                    final long viewTourId = _tourData.getTourId();

                    if (modifiedTourData.getTourId() == viewTourId) {

                        // update existing tour

                        if (checkDataValidation(modifiedTourData)) {

                            if (tourEvent.isReverted) {

                                /*
                                 * tour is reverted, saving existing tour is not necessary, just
                                 * update the tour
                                 */
                                setTour(modifiedTourData, true);

                            } else {

                                createSegments();
                                reloadViewer();
                            }
                        }

                    } else {

                        // display new tour

                        onSelectionChanged(new SelectionTourData(null, modifiedTourData));
                    }

                    // removed old tour data from the selection provider
                    _postSelectionProvider.clearSelection();

                } else if (eventId == TourEventId.CLEAR_DISPLAYED_TOUR) {

                    clearView();

                } else if (eventId == TourEventId.UPDATE_UI) {

                    // check if a tour must be updated

                    if (_tourData == null) {
                        return;
                    }

                    final Long tourId = _tourData.getTourId();

                    // update ui
                    if (UI.containsTourId(eventData, tourId) != null) {

                        setTour(TourManager.getInstance().getTourData(tourId), true);
                    }
                }
            }
        };

        TourManager.getInstance().addTourEventListener(_tourEventListener);
    }

    /**
     * check if data for the segmenter is valid
     */
    private boolean checkDataValidation(final TourData tourData) {

        /*
         * tourdata and time serie are necessary to create any segment
         */
        if (tourData == null || tourData.timeSerie == null || tourData.timeSerie.length < 2) {

            clearView();

            return false;
        }

        if (checkSegmenterData(tourData) == 0) {

            clearView();

            return false;
        }

        _pageBookUI.showPage(_pageSegmenter);

        return true;
    }

    private int checkSegmenterData(final TourData tourData) {

        final int[] altitudeSerie = tourData.altitudeSerie;
        final int[] metricDistanceSerie = tourData.getMetricDistanceSerie();
        final int[] pulseSerie = tourData.pulseSerie;
        final Set<TourMarker> markerSerie = tourData.getTourMarkers();

        int checkedSegmenterData = 0;

        checkedSegmenterData |= altitudeSerie != null && altitudeSerie.length > 1 ? //
                SEGMENTER_REQUIRES_ALTITUDE : 0;

        checkedSegmenterData |= metricDistanceSerie != null && metricDistanceSerie.length > 1
                ? SEGMENTER_REQUIRES_DISTANCE
                : 0;

        checkedSegmenterData |= pulseSerie != null && pulseSerie.length > 1 ? //
                SEGMENTER_REQUIRES_PULSE : 0;

        checkedSegmenterData |= markerSerie != null && markerSerie.size() > 0 ? //
                SEGMENTER_REQUIRES_MARKER : 0;

        return checkedSegmenterData;
    }

    private void clearView() {

        _pageBookUI.showPage(_pageNoData);

        _tourData = null;
        _tourChart = null;

        _isClearView = true;
    }

    private void createActions() {

        _actionShowSegments = new ActionShowSegments();
        final ActionModifyColumns actionModifyColumns = new ActionModifyColumns(this);

        /*
         * fill view menu
         */
        final IMenuManager menuMgr = getViewSite().getActionBars().getMenuManager();
        menuMgr.add(actionModifyColumns);

        /*
         * fill view toolbar
         */
        final IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager();
        tbm.add(_actionShowSegments);
    }

    private void createAllTourSegmenter() {

        if (_allTourSegmenter != null) {
            // segmenter are already created
            return;
        }

        _allTourSegmenter = new HashMap<SegmenterType, TourSegmenter>();

        _allTourSegmenter.put(SegmenterType.ByAltitudeWithDP, //
                new TourSegmenter(SegmenterType.ByAltitudeWithDP, Messages.tour_segmenter_type_byAltitude,
                        SEGMENTER_REQUIRES_ALTITUDE | SEGMENTER_REQUIRES_DISTANCE));

        _allTourSegmenter.put(SegmenterType.ByDistance, //
                new TourSegmenter(SegmenterType.ByDistance, //
                        Messages.tour_segmenter_type_byDistance, //
                        SEGMENTER_REQUIRES_DISTANCE));

        _allTourSegmenter.put(SegmenterType.ByComputedAltiUpDown, //
                new TourSegmenter(SegmenterType.ByComputedAltiUpDown,
                        Messages.tour_segmenter_type_byComputedAltiUpDown, SEGMENTER_REQUIRES_ALTITUDE));

        _allTourSegmenter.put(SegmenterType.ByPulseWithDP, //
                new TourSegmenter(SegmenterType.ByPulseWithDP, Messages.tour_segmenter_type_byPulse,
                        SEGMENTER_REQUIRES_PULSE));

        _allTourSegmenter.put(SegmenterType.ByMarker, //
                new TourSegmenter(SegmenterType.ByMarker, Messages.tour_segmenter_type_byMarker,
                        SEGMENTER_REQUIRES_MARKER));

        _allTourSegmenter.put(SegmenterType.ByBreakTime, //
                new TourSegmenter(SegmenterType.ByBreakTime, Messages.Tour_Segmenter_Type_ByBreakTime,
                        SEGMENTER_REQUIRES_DISTANCE));
    }

    @Override
    public void createPartControl(final Composite parent) {

        _pc = new PixelConverter(parent);
        _spinnerWidth = _pc.convertWidthInCharsToPixels(_isOSX ? 10 : 5);

        createAllTourSegmenter();
        setMaxDistanceScale();

        // define all columns
        _columnManager = new ColumnManager(this, _state);
        defineAllColumns(parent);

        createUI(parent);
        createActions();

        addSelectionListener();
        addPartListener();
        addPrefListener();
        addTourEventListener();

        // tell the site that this view is a selection provider
        getSite().setSelectionProvider(_postSelectionProvider = new PostSelectionProvider());

        // set default value, show segments in opened charts
        _isShowSegmentsInChart = true;
        _actionShowSegments.setChecked(_isShowSegmentsInChart);

        _pageBookUI.showPage(_pageNoData);

        restoreState();

        // update viewer with current selection
        onSelectionChanged(getSite().getWorkbenchWindow().getSelectionService().getSelection());

        // when previous onSelectionChanged did not display a tour, get tour from tour manager
        if (_tourData == null) {
            Display.getCurrent().asyncExec(new Runnable() {
                public void run() {

                    final ArrayList<TourData> selectedTours = TourManager.getSelectedTours();

                    if (selectedTours != null && selectedTours.size() > 0) {
                        onSelectionChanged(new SelectionTourData(null, selectedTours.get(0)));
                    }
                }
            });
        }

    }

    private Object[] createSegmenterContent() {

        final TourSegmenter selectedSegmenter = getSelectedSegmenter();
        if (selectedSegmenter == null) {
            return new Object[0];
        }

        /*
         * get break time values: time/distance & speed
         */
        final BreakTimeTool btConfig;

        if (selectedSegmenter.segmenterType == SegmenterType.ByBreakTime) {

            // use segmenter values

            btConfig = new BreakTimeTool(_comboBreakMethod.getSelectionIndex(), _breakUIShortestBreakTime,
                    _breakUIMaxDistance, _breakUIMinSliceSpeed, _breakUIMinAvgSpeed);

        } else {

            // use pref values for time/distance & speed

            btConfig = BreakTimeTool.getPrefValues();
        }

        return _tourData.createTourSegments(btConfig);
    }

    /**
     * create points for the simplifier from distance and altitude
     */
    private void createSegments() {

        if (_tourData == null) {
            _pageBookUI.showPage(_pageNoData);
            return;
        }

        // disable computed altitude
        _tourData.segmentSerieComputedAltitudeDiff = null;

        final TourSegmenter selectedSegmenter = getSelectedSegmenter();
        if (selectedSegmenter == null) {
            clearView();
            return;
        }

        final SegmenterType selectedSegmenterType = selectedSegmenter.segmenterType;

        if (selectedSegmenterType == SegmenterType.ByAltitudeWithDP) {

            createSegmentsByAltitudeWithDP();

        } else if (selectedSegmenterType == SegmenterType.ByPulseWithDP) {

            createSegmentsByPulseWithDP();

        } else if (selectedSegmenterType == SegmenterType.ByDistance) {

            createSegmentsByDistance();

        } else if (selectedSegmenterType == SegmenterType.ByMarker) {

            createSegmentsByMarker();

        } else if (selectedSegmenterType == SegmenterType.ByComputedAltiUpDown) {

            createSegmentsByAltiUpDown();

        } else if (selectedSegmenterType == SegmenterType.ByBreakTime) {

            createSegmentsByBreakTime();
        }

        // update table and create the tour segments in tour data
        reloadViewer();

        fireSegmentLayerChanged();
    }

    /**
     * create Douglas-Peucker segments from distance and altitude
     */
    private void createSegmentsByAltitudeWithDP() {

        final int[] distanceSerie = _tourData.getMetricDistanceSerie();
        final int[] altitudeSerie = _tourData.altitudeSerie;

        // convert data series into points
        final DPPoint graphPoints[] = new DPPoint[distanceSerie.length];
        for (int serieIndex = 0; serieIndex < graphPoints.length; serieIndex++) {
            graphPoints[serieIndex] = new DPPoint(distanceSerie[serieIndex], altitudeSerie[serieIndex], serieIndex);
        }

        final Object[] simplePoints = new DouglasPeuckerSimplifier(_dpTolerance, graphPoints).simplify();

        /*
         * copie the data index for the simplified points into the tour data
         */

        final int[] segmentSerieIndex = _tourData.segmentSerieIndex = new int[simplePoints.length];

        for (int iPoint = 0; iPoint < simplePoints.length; iPoint++) {
            final DPPoint point = (DPPoint) simplePoints[iPoint];
            segmentSerieIndex[iPoint] = point.serieIndex;
        }
    }

    private void createSegmentsByAltiUpDown() {

        final int selectedMinAltiDiff = PrefPageComputedValues.ALTITUDE_MINIMUM[_cboMinAltitude
                .getSelectionIndex()];
        final ArrayList<AltitudeUpDownSegment> tourSegements = new ArrayList<AltitudeUpDownSegment>();

        // create segment when the altitude up/down is changing
        _tourData.computeAltitudeUpDown(tourSegements, selectedMinAltiDiff);

        // convert segment list into array
        int serieIndex = 0;
        final int segmentLength = tourSegements.size();
        final int[] segmentSerieIndex = _tourData.segmentSerieIndex = new int[segmentLength];
        final int[] altitudeDiff = _tourData.segmentSerieComputedAltitudeDiff = new int[segmentLength];

        for (final AltitudeUpDownSegment altitudeUpDownSegment : tourSegements) {

            segmentSerieIndex[serieIndex] = altitudeUpDownSegment.serieIndex;
            altitudeDiff[serieIndex] = altitudeUpDownSegment.computedAltitudeDiff;

            serieIndex++;
        }
    }

    private void createSegmentsByBreakTime() {

        boolean[] breakTimeSerie = null;
        BreakTimeResult breakTimeResult;

        final int breakMethod = _comboBreakMethod.getSelectionIndex();

        if (breakMethod == BreakTimeTool.BREAK_TIME_METHOD_BY_TIME_DISTANCE) {

            _breakUIShortestBreakTime = _spinnerBreakShortestTime.getSelection();
            _breakUIMaxDistance = _spinnerBreakMaxDistance.getSelection() * UI.UNIT_VALUE_DISTANCE_SMALL;

            breakTimeResult = BreakTimeTool.computeBreakTimeByTimeDistance(_tourData, _breakUIShortestBreakTime,
                    _breakUIMaxDistance);
            breakTimeSerie = breakTimeResult.breakTimeSerie;
            _tourBreakTime = breakTimeResult.tourBreakTime;

        } else if (breakMethod == BreakTimeTool.BREAK_TIME_METHOD_BY_SLICE_SPEED) {

            _breakUIMinSliceSpeed = _spinnerBreakMinSliceSpeed.getSelection() / SPEED_DIGIT_VALUE
                    / UI.UNIT_VALUE_DISTANCE;

            breakTimeResult = BreakTimeTool.computeBreakTimeBySpeed(_tourData, breakMethod, _breakUIMinSliceSpeed);
            breakTimeSerie = breakTimeResult.breakTimeSerie;
            _tourBreakTime = breakTimeResult.tourBreakTime;

        } else if (breakMethod == BreakTimeTool.BREAK_TIME_METHOD_BY_AVG_SPEED) {

            _breakUIMinAvgSpeed = _spinnerBreakMinAvgSpeed.getSelection()//
                    / SPEED_DIGIT_VALUE / UI.UNIT_VALUE_DISTANCE;

            breakTimeResult = BreakTimeTool.computeBreakTimeBySpeed(_tourData, breakMethod, _breakUIMinAvgSpeed);
            breakTimeSerie = breakTimeResult.breakTimeSerie;
            _tourBreakTime = breakTimeResult.tourBreakTime;
        }

        /*
         * convert recognized breaks into segments
         */
        final ArrayList<Integer> segmentSerieIndex = new ArrayList<Integer>();

        // set start for first segment
        segmentSerieIndex.add(0);

        boolean prevIsBreak = false;
        boolean isBreak = breakTimeSerie[0];

        for (int serieIndex = 1; serieIndex < breakTimeSerie.length; serieIndex++) {

            isBreak = breakTimeSerie[serieIndex];

            if (isBreak != prevIsBreak) {

                // break has toggled, set end index

                segmentSerieIndex.add(serieIndex - 1);
            }

            prevIsBreak = isBreak;
        }

        // ensure the last segment ends at the end of the tour
        final int lastDistanceSerieIndex = _tourData.timeSerie.length - 1;
        final int serieSize = segmentSerieIndex.size();
        if (serieSize == 1 || //

        // ensure the last index is not duplicated
                segmentSerieIndex.get(serieSize - 1) != lastDistanceSerieIndex) {

            segmentSerieIndex.add(lastDistanceSerieIndex);
        }

        _tourData.segmentSerieIndex = ArrayListToArray.toInt(segmentSerieIndex);
        _tourData.breakTimeSerie = breakTimeSerie;

    }

    private void createSegmentsByDistance() {

        final int[] distanceSerie = _tourData.getMetricDistanceSerie();
        final int lastDistanceSerieIndex = distanceSerie.length - 1;

        final float segmentDistance = getDistance();
        final ArrayList<Integer> segmentSerieIndex = new ArrayList<Integer>();

        // set first segment start
        segmentSerieIndex.add(0);

        float nextSegmentDistance = segmentDistance;

        for (int distanceIndex = 0; distanceIndex < distanceSerie.length; distanceIndex++) {
            final int distance = distanceSerie[distanceIndex];

            if (distance >= nextSegmentDistance) {

                segmentSerieIndex.add(distanceIndex);

                // set minimum distance for the next segment
                nextSegmentDistance += segmentDistance;
            }
        }

        // ensure the last segment ends at the end of the tour
        final int serieSize = segmentSerieIndex.size();
        if (serieSize == 1 || //

        // ensure the last index is not duplicated
                segmentSerieIndex.get(serieSize - 1) != lastDistanceSerieIndex) {

            segmentSerieIndex.add(lastDistanceSerieIndex);
        }

        _tourData.segmentSerieIndex = ArrayListToArray.toInt(segmentSerieIndex);
    }

    private void createSegmentsByMarker() {

        final int[] timeSerie = _tourData.timeSerie;
        final Set<TourMarker> tourMarkers = _tourData.getTourMarkers();

        // sort markers by time - they are unsorted
        final ArrayList<TourMarker> markerList = new ArrayList<TourMarker>(tourMarkers);
        Collections.sort(markerList, new Comparator<TourMarker>() {
            public int compare(final TourMarker tm1, final TourMarker tm2) {
                return tm1.getSerieIndex() - tm2.getSerieIndex();
            }
        });

        final ArrayList<Integer> segmentSerieIndex = new ArrayList<Integer>();

        // set first segment at tour start
        segmentSerieIndex.add(0);

        // create segment for each marker
        for (final TourMarker tourMarker : markerList) {
            segmentSerieIndex.add(tourMarker.getSerieIndex());
        }

        // add segment end at the tour end
        segmentSerieIndex.add(timeSerie.length - 1);

        _tourData.segmentSerieIndex = ArrayListToArray.toInt(segmentSerieIndex);
    }

    /**
     * create Douglas-Peucker segments from time and pulse
     */
    private void createSegmentsByPulseWithDP() {

        final int[] timeSerie = _tourData.timeSerie;
        final int[] pulseSerie = _tourData.pulseSerie;

        if (pulseSerie == null || pulseSerie.length < 2) {
            _tourData.segmentSerieIndex = null;
            return;
        }

        // convert data series into points
        final DPPoint graphPoints[] = new DPPoint[timeSerie.length];
        for (int serieIndex = 0; serieIndex < graphPoints.length; serieIndex++) {
            graphPoints[serieIndex] = new DPPoint(timeSerie[serieIndex], pulseSerie[serieIndex], serieIndex);
        }

        final DouglasPeuckerSimplifier dpSimplifier = new DouglasPeuckerSimplifier(_dpTolerance, graphPoints);
        final Object[] simplePoints = dpSimplifier.simplify();

        /*
         * copie the data index for the simplified points into the tour data
         */
        final int[] segmentSerieIndex = _tourData.segmentSerieIndex = new int[simplePoints.length];

        for (int iPoint = 0; iPoint < simplePoints.length; iPoint++) {
            final DPPoint point = (DPPoint) simplePoints[iPoint];
            segmentSerieIndex[iPoint] = point.serieIndex;
        }
    }

    private void createUI(final Composite parent) {

        _pageBookUI = new PageBook(parent, SWT.NONE);

        _pageNoData = new Label(_pageBookUI, SWT.WRAP);
        _pageNoData.setText(Messages.Tour_Segmenter_Label_no_chart);

        _pageSegmenter = new Composite(_pageBookUI, SWT.NONE);
        GridLayoutFactory.fillDefaults().spacing(0, 0).applyTo(_pageSegmenter);
        {
            createUI10Header(_pageSegmenter);
            createUI70Viewer(_pageSegmenter);
        }
    }

    private void createUI10Header(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(3).extendedMargins(3, 3, 3, 2).applyTo(container);
        //      container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE));
        {
            // tour title
            _lblTitle = new ImageComboLabel(container, SWT.NONE);
            GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(_lblTitle);

            // label: create segments with
            final Label label = new Label(container, SWT.NONE);
            GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).applyTo(label);
            label.setText(Messages.tour_segmenter_label_createSegmentsWith);

            // combo: segmenter type
            _cboSegmenterType = new Combo(container, SWT.READ_ONLY);
            _cboSegmenterType.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onSelectSegmenterType(true);
                }
            });
            /*
             * fill the segmenter list that the next layout shows the combo that all segmenter names
             * are visible
             */
            for (final TourSegmenter segmenter : _allTourSegmenter.values()) {
                _cboSegmenterType.add(segmenter.name);
            }
            final Point size = _cboSegmenterType.computeSize(SWT.DEFAULT, SWT.DEFAULT);
            GridDataFactory.fillDefaults().hint(size).applyTo(_cboSegmenterType);

            /*
             * tour/computed altitude
             */
            _containerSaveAltitude = new Composite(container, SWT.NONE);
            GridDataFactory.fillDefaults().grab(true, false).applyTo(_containerSaveAltitude);
            GridLayoutFactory.fillDefaults().numColumns(2).applyTo(_containerSaveAltitude);
            {
                // label: computed altitude up
                _lblAltitudeUp = new Label(_containerSaveAltitude, SWT.TRAIL);
                GridDataFactory.fillDefaults()//
                        .align(SWT.END, SWT.CENTER).grab(true, false)
                        .hint(_pc.convertWidthInCharsToPixels(18), SWT.DEFAULT).applyTo(_lblAltitudeUp);
                _lblAltitudeUp.setToolTipText(Messages.tour_segmenter_label_tourAltitude_tooltip);

                // button: save tour altitude
                _btnSaveTour = new Button(_containerSaveAltitude, SWT.NONE);
                GridDataFactory.fillDefaults().indent(5, 0).applyTo(_btnSaveTour);
                _btnSaveTour.setText(Messages.tour_segmenter_button_updateAltitude);
                _btnSaveTour.setToolTipText(Messages.tour_segmenter_button_updateAltitude_tooltip);
                _btnSaveTour.addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        onSaveTourAltitude();
                    }
                });
            }

            // pagebook: segmenter type
            _pageBookSegmenter = new PageBook(container, SWT.NONE);
            GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(_pageBookSegmenter);
            {
                _pageSegTypeDP = createUI11SegmenterDP(_pageBookSegmenter);
                _pageSegTypeByMarker = createUI12SegmenterByMarker(_pageBookSegmenter);
                _pageSegTypeByDistance = createUI13SegmenterByDistance(_pageBookSegmenter);
                _pageSegTypeByAltiUpDown = createUI14SegmenterByAltiUpDown(_pageBookSegmenter);
                _pageSegTypeByBreakTime = createUI50SegmenterByBreakTime(_pageBookSegmenter);
            }
        }

    }

    private Composite createUI11SegmenterDP(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container);
        {

            // label: tolerance
            final Label label = new Label(container, SWT.NONE);
            label.setText(Messages.Tour_Segmenter_Label_tolerance);

            // scale: tolerance
            _scaleTolerance = new Scale(container, SWT.HORIZONTAL);
            GridDataFactory.fillDefaults().grab(true, false).applyTo(_scaleTolerance);
            _scaleTolerance.setMaximum(100);
            _scaleTolerance.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onChangedTolerance(getDPTolerance());
                    setTourDirty();
                }
            });

            // text: tolerance value
            _lblToleranceValue = new Label(container, SWT.TRAIL);
            _lblToleranceValue.setText(Messages.Tour_Segmenter_Label_default_tolerance);
            GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
                    .hint(_pc.convertWidthInCharsToPixels(4), SWT.DEFAULT).applyTo(_lblToleranceValue);
        }

        return container;
    }

    private Composite createUI12SegmenterByMarker(final Composite parent) {

        /*
         * display NONE, this is not easy to do - or I didn't find an easier way
         */
        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().applyTo(container);
        {
            final Canvas canvas = new Canvas(container, SWT.NONE);
            GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.BEGINNING).hint(1, 1).applyTo(canvas);
        }

        return container;
    }

    private Composite createUI13SegmenterByDistance(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container);
        {

            // label: distance
            final Label label = new Label(container, SWT.NONE);
            label.setText(Messages.tour_segmenter_segType_byDistance_label);

            // scale: distance
            _scaleDistance = new Scale(container, SWT.HORIZONTAL);
            GridDataFactory.fillDefaults().grab(true, false).applyTo(_scaleDistance);
            _scaleDistance.setMaximum(_maxDistanceScale);
            _scaleDistance.setPageIncrement(_scaleDistancePage);
            _scaleDistance.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onChangedDistance();
                }
            });

            // text: distance value
            _lblDistanceValue = new Label(container, SWT.TRAIL);
            _lblDistanceValue.setText(Messages.tour_segmenter_segType_byDistance_defaultDistance);
            GridDataFactory.fillDefaults()//
                    .align(SWT.FILL, SWT.CENTER).hint(_pc.convertWidthInCharsToPixels(8), SWT.DEFAULT)
                    .applyTo(_lblDistanceValue);
        }

        return container;
    }

    private Composite createUI14SegmenterByAltiUpDown(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container);
        {
            // label: min alti diff
            final Label label = new Label(container, SWT.NONE);
            label.setText(Messages.tour_segmenter_segType_byUpDownAlti_label);

            // combo: min altitude
            _cboMinAltitude = new Combo(container, SWT.READ_ONLY);
            _cboMinAltitude.setVisibleItemCount(20);
            _cboMinAltitude.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    createSegments();
                }
            });

            // label: unit
            _lblMinAltitude = new Label(container, SWT.NONE);
            _lblMinAltitude.setText(UI.UNIT_LABEL_ALTITUDE);
        }

        return container;
    }

    private Composite createUI50SegmenterByBreakTime(final Composite parent) {

        _containerBreakTime = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(_containerBreakTime);
        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(_containerBreakTime);
        //      _containerBreakTime.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE));
        {
            final Composite container = new Composite(_containerBreakTime, SWT.NONE);
            GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
            GridLayoutFactory.fillDefaults().numColumns(1).applyTo(container);
            {
                createUI58TourBreakTime(container);
                createUI51BreakTime(container);
            }

            createUI56BreakActions(_containerBreakTime);
        }

        _containerBreakTime.layout(true, true);
        UI.setEqualizeColumWidths(_firstColBreakTime);

        return _containerBreakTime;
    }

    private void createUI51BreakTime(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container);
        //      container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_MAGENTA));
        {
            /*
             * label: compute break time by
             */
            final Label label = new Label(container, SWT.NONE);
            GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).applyTo(label);
            label.setText(Messages.Compute_BreakTime_Label_ComputeBreakTimeBy);
            _firstColBreakTime.add(label);

            _comboBreakMethod = new Combo(container, SWT.READ_ONLY | SWT.BORDER);
            _comboBreakMethod.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onSelectBreakTimeMethod();
                }
            });

            // fill combo
            for (final String breakMethod : BreakTimeTool.BREAK_TIME_METHODS) {
                _comboBreakMethod.add(breakMethod);
            }

            /*
             * pagebook: break algorithm
             */
            _pagebookBreakTime = new PageBook(container, SWT.NONE);
            GridDataFactory.fillDefaults().span(2, 1).grab(true, false).applyTo(_pagebookBreakTime);
            {
                _pageBreakByTimeDistance = createUI52BreakByTimeDistance(_pagebookBreakTime);
                _pageBreakBySliceSpeed = createUI53BreakBySliceSpeed(_pagebookBreakTime);
                _pageBreakByAvgSpeed = createUI54BreakByAvgSpeed(_pagebookBreakTime);
            }

            /*
             * force pages to be displayed otherwise they are hidden or the hint is not computed for
             * the first column until a resize is done
             */
            //         _pagebookBreakTime.showPage(_pageBreakBySliceSpeed);
            //         _pagebookBreakTime.showPage(_pageBreakByAvgSpeed);
            //         _pageBreakBySliceSpeed.layout(true, true);
            //         _pageBreakByAvgSpeed.layout(true, true);
            //         _pagebookBreakTime.layout(true, true);
        }
    }

    private Composite createUI52BreakByTimeDistance(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container);
        {
            /*
             * shortest break time
             */
            {
                // label: break min time
                Label label = new Label(container, SWT.NONE);
                label.setText(Messages.Compute_BreakTime_Label_MinimumTime);
                _firstColBreakTime.add(label);

                // spinner: break minimum time
                _spinnerBreakShortestTime = new Spinner(container, SWT.BORDER);
                GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreakShortestTime);
                _spinnerBreakShortestTime.setMinimum(1);
                _spinnerBreakShortestTime.setMaximum(120); // 120 seconds
                _spinnerBreakShortestTime.addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        onChangeBreakTime();
                    }
                });
                _spinnerBreakShortestTime.addMouseWheelListener(new MouseWheelListener() {
                    public void mouseScrolled(final MouseEvent event) {
                        UI.adjustSpinnerValueOnMouseScroll(event);
                        onChangeBreakTime();
                    }
                });

                // label: unit
                label = new Label(container, SWT.NONE);
                label.setText(Messages.App_Unit_Seconds_Small);
            }

            /*
             * recording distance
             */
            {
                // label: break min distance
                final Label label = new Label(container, SWT.NONE);
                label.setText(Messages.Compute_BreakTime_Label_MinimumDistance);
                _firstColBreakTime.add(label);

                // spinner: break minimum time
                _spinnerBreakMaxDistance = new Spinner(container, SWT.BORDER);
                GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreakMaxDistance);
                _spinnerBreakMaxDistance.setMinimum(1);
                _spinnerBreakMaxDistance.setMaximum(1000); // 1000 m/yards
                _spinnerBreakMaxDistance.addSelectionListener(new SelectionAdapter() {
                    @Override
                    public void widgetSelected(final SelectionEvent e) {
                        onChangeBreakTime();
                    }
                });
                _spinnerBreakMaxDistance.addMouseWheelListener(new MouseWheelListener() {
                    public void mouseScrolled(final MouseEvent event) {
                        UI.adjustSpinnerValueOnMouseScroll(event);
                        onChangeBreakTime();
                    }
                });

                // label: unit
                _lblBreakDistanceUnit = new Label(container, SWT.NONE);
                _lblBreakDistanceUnit.setText(UI.UNIT_LABEL_DISTANCE_SMALL);
                GridDataFactory.fillDefaults()//
                        //                  .span(2, 1)
                        .align(SWT.FILL, SWT.CENTER).applyTo(_lblBreakDistanceUnit);
            }
        }

        return container;
    }

    private Composite createUI53BreakBySliceSpeed(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container);
        //      container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE));
        {
            /*
             * minimum speed
             */

            // label: minimum speed
            Label label = new Label(container, SWT.NONE);
            label.setText(Messages.Compute_BreakTime_Label_MinimumSliceSpeed);
            _firstColBreakTime.add(label);

            // spinner: minimum speed
            _spinnerBreakMinSliceSpeed = new Spinner(container, SWT.BORDER);
            GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreakMinSliceSpeed);
            _spinnerBreakMinSliceSpeed.setMinimum(0); // 0.0 km/h
            _spinnerBreakMinSliceSpeed.setMaximum(100); // 10.0 km/h
            _spinnerBreakMinSliceSpeed.setDigits(1);
            _spinnerBreakMinSliceSpeed.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onChangeBreakTime();
                }
            });
            _spinnerBreakMinSliceSpeed.addMouseWheelListener(new MouseWheelListener() {
                public void mouseScrolled(final MouseEvent event) {
                    UI.adjustSpinnerValueOnMouseScroll(event);
                    onChangeBreakTime();
                }
            });

            // label: km/h
            label = new Label(container, SWT.NONE);
            label.setText(UI.UNIT_LABEL_SPEED);
        }

        return container;
    }

    private Composite createUI54BreakByAvgSpeed(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(3).applyTo(container);
        //      container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
        {
            /*
             * minimum average speed
             */

            // label: minimum speed
            Label label = new Label(container, SWT.NONE);
            label.setText(Messages.Compute_BreakTime_Label_MinimumAvgSpeed);
            _firstColBreakTime.add(label);

            // spinner: minimum speed
            _spinnerBreakMinAvgSpeed = new Spinner(container, SWT.BORDER);
            GridDataFactory.fillDefaults().hint(_spinnerWidth, SWT.DEFAULT).applyTo(_spinnerBreakMinAvgSpeed);
            _spinnerBreakMinAvgSpeed.setMinimum(0); // 0.0 km/h
            _spinnerBreakMinAvgSpeed.setMaximum(100); // 10.0 km/h
            _spinnerBreakMinAvgSpeed.setDigits(1);
            _spinnerBreakMinAvgSpeed.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onChangeBreakTime();
                }
            });
            _spinnerBreakMinAvgSpeed.addMouseWheelListener(new MouseWheelListener() {
                public void mouseScrolled(final MouseEvent event) {
                    UI.adjustSpinnerValueOnMouseScroll(event);
                    onChangeBreakTime();
                }
            });

            // label: km/h
            label = new Label(container, SWT.NONE);
            label.setText(UI.UNIT_LABEL_SPEED);
        }

        return container;
    }

    private void createUI56BreakActions(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(false, true).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(container);
        //      container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE));
        {
            /*
             * button: restore from defaults
             */
            final Button btnRestore = new Button(container, SWT.NONE);
            GridDataFactory.fillDefaults()//
                    .applyTo(btnRestore);
            btnRestore.setText(Messages.Compute_BreakTime_Button_RestoreDefaultValues);
            btnRestore.setToolTipText(Messages.Compute_BreakTime_Button_RestoreDefaultValues_Tooltip);
            btnRestore.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onGetBreakTimePrefValues();
                }
            });

            /*
             * button: set as default values
             */
            final Button btnSetDefault = new Button(container, SWT.NONE);
            GridDataFactory.fillDefaults()//
                    .applyTo(btnSetDefault);
            btnSetDefault.setText(Messages.Compute_BreakTime_Button_SetDefaultValues);
            btnSetDefault.setToolTipText(Messages.Compute_BreakTime_Button_SetDefaultValues_Tooltip);
            btnSetDefault.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(final SelectionEvent e) {
                    onSetDefaults(parent);
                }
            });
        }
    }

    private void createUI58TourBreakTime(final Composite parent) {

        final Composite container = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults()//
                //            .span(2, 1)
                .grab(true, false).applyTo(container);
        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(container);
        //      container.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GREEN));
        {
            /*
             * tour break time
             */
            // label: break time
            final Label label = new Label(container, SWT.NONE);
            label.setText(Messages.Compute_BreakTime_Label_TourBreakTime);
            GridDataFactory.fillDefaults()//
                    .align(SWT.FILL, SWT.CENTER).applyTo(label);
            _firstColBreakTime.add(label);

            // label: value + unit
            _lblTourBreakTime = new Label(container, SWT.NONE);
            GridDataFactory.fillDefaults()//
                    .align(SWT.FILL, SWT.CENTER).grab(true, false)
                    //               .span(2, 1)
                    .applyTo(_lblTourBreakTime);
        }
    }

    private void createUI70Viewer(final Composite parent) {

        _containerViewer = new Composite(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(_containerViewer);
        GridLayoutFactory.fillDefaults().applyTo(_containerViewer);
        {
            createUI80SegmentViewer(_containerViewer);
        }
    }

    private void createUI80SegmentViewer(final Composite parent) {

        final Table table = new Table(parent, //
                SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI /* | SWT.BORDER */);

        table.setHeaderVisible(true);
        table.setLinesVisible(true);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(table);

        _segmentViewer = new TableViewer(table);
        _columnManager.createColumns(_segmentViewer);

        _segmentViewer.setContentProvider(new ViewContentProvider());
        _segmentViewer.setSorter(new ViewSorter());

        _segmentViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(final SelectionChangedEvent event) {

                final StructuredSelection selection = (StructuredSelection) event.getSelection();
                if (selection != null) {

                    /*
                     * select the chart sliders according to the selected segment(s)
                     */

                    final Object[] segments = selection.toArray();

                    if (segments.length > 0) {

                        if (_tourChart == null) {
                            _tourChart = getActiveTourChart(_tourData);
                        }

                        final int startIndex = ((TourSegment) (segments[0])).serieIndexStart;
                        final int endIndex = ((TourSegment) (segments[segments.length - 1])).serieIndexEnd;

                        final SelectionChartXSliderPosition selectionSliderPosition = new SelectionChartXSliderPosition(
                                _tourChart, startIndex, endIndex, true);

                        _postSelectionProvider.setSelection(selectionSliderPosition);
                    }
                }
            }
        });

        createUI90ContextMenu();
    }

    /**
     * create the views context menu
     */
    private void createUI90ContextMenu() {

        final Table table = (Table) _segmentViewer.getControl();

        _columnManager.createHeaderContextMenu(table, null);
    }

    private void defineAllColumns(final Composite parent) {

        final SelectionAdapter defaultListener = new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent event) {
                ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_DEFAULT);
                _segmentViewer.refresh();
            }
        };

        defineColumnRecordingTimeTotal(defaultListener);

        defineColumnDistanceTotal(defaultListener);
        defineColumnDistance(defaultListener);

        defineColumnRecordingTime(defaultListener);
        defineColumnDrivingTime(defaultListener);
        defineColumnPausedTime(defaultListener);

        defineColumnAltitudeDiffSegmentBorder(defaultListener);
        defineColumnAltitudeDiffSegmentComputed(defaultListener);
        defineColumnAltitudeUpSummarizedComputed(defaultListener);
        defineColumnAltitudeDownSummarizedComputed(defaultListener);

        defineColumnGradient();

        defineColumnAltitudeUpHour(defaultListener);
        defineColumnAltitudeDownHour(defaultListener);

        defineColumnAvgSpeed();
        defineColumnAvgPace();
        defineColumnAvgPaceDifference();
        defineColumnAvgPulse();
        defineColumnAvgPulseDifference();

        defineColumnAltitudeUpSummarizedBorder(defaultListener);
        defineColumnAltitudeDownSummarizedBorder(defaultListener);

        defineColumnSerieStartEndIndex();
    }

    /**
     * column: altitude diff segment border (m/ft)
     */
    private void defineColumnAltitudeDiffSegmentBorder(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_DIFF_SEGMENT_BORDER.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                final int altitudeDiff = segment.altitudeDiffSegmentBorder;

                cell.setText(_nf_0_0.format(altitudeDiff / UI.UNIT_VALUE_ALTITUDE));
                setCellColor(cell, altitudeDiff);
            }
        });
    }

    /**
     * column: computed altitude diff (m/ft)
     */
    private void defineColumnAltitudeDiffSegmentComputed(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_DIFF_SEGMENT_COMPUTED.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                final int altitudeDiff = segment.altitudeDiffSegmentComputed;

                cell.setText(_nf_0_0.format(altitudeDiff / UI.UNIT_VALUE_ALTITUDE));
                setCellColor(cell, altitudeDiff);
            }
        });
    }

    /**
     * column: altitude down m/h
     */
    private void defineColumnAltitudeDownHour(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_DOWN_H.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();
                if (segment.drivingTime == 0) {
                    cell.setText(UI.EMPTY_STRING);
                } else {
                    final float result = (segment.altitudeDownHour / UI.UNIT_VALUE_ALTITUDE) / segment.drivingTime
                            * 3600;
                    if (result == 0) {
                        cell.setText(UI.EMPTY_STRING);
                    } else {
                        cell.setText(_nf_1_0.format(result));
                    }
                }
            }
        });
    }

    /**
     * column: total altitude down (m/ft)
     */
    private void defineColumnAltitudeDownSummarizedBorder(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_DOWN_SUMMARIZED_BORDER.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                cell.setText(_nf_0_0.format(segment.altitudeDownSummarizedBorder / UI.UNIT_VALUE_ALTITUDE));
            }
        });
    }

    /**
     * column: total altitude down (m/ft)
     */
    private void defineColumnAltitudeDownSummarizedComputed(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_DOWN_SUMMARIZED_COMPUTED.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                cell.setText(_nf_0_0.format(segment.altitudeDownSummarizedComputed / UI.UNIT_VALUE_ALTITUDE));
            }
        });
    }

    /**
     * column: altitude up m/h
     */
    private void defineColumnAltitudeUpHour(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_UP_H.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();
                if (segment.drivingTime == 0) {
                    cell.setText(UI.EMPTY_STRING);
                } else {
                    final float result = (segment.altitudeUpHour / UI.UNIT_VALUE_ALTITUDE) / segment.drivingTime
                            * 3600;
                    if (result == 0) {
                        cell.setText(UI.EMPTY_STRING);
                    } else {
                        cell.setText(_nf_1_0.format(result));
                    }
                }
            }
        });
    }

    /**
     * column: total altitude up (m/ft)
     */
    private void defineColumnAltitudeUpSummarizedBorder(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_UP_SUMMARIZED_BORDER.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                cell.setText(_nf_0_0.format(segment.altitudeUpSummarizedBorder / UI.UNIT_VALUE_ALTITUDE));
            }
        });
    }

    /**
     * column: total altitude up (m/ft)
     */
    private void defineColumnAltitudeUpSummarizedComputed(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.ALTITUDE_UP_SUMMARIZED_COMPUTED.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                cell.setText(_nf_0_0.format(segment.altitudeUpSummarizedComputed / UI.UNIT_VALUE_ALTITUDE));
            }
        });
    }

    /**
     * column: average pace
     */
    private void defineColumnAvgPace() {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.AVG_PACE.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent event) {
                ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_SPEED);
                _segmentViewer.refresh();
            }
        });
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();
                cell.setText(UI.format_mm_ss(segment.pace));
            }
        });
    }

    /**
     * column: pace difference
     */
    private void defineColumnAvgPaceDifference() {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.AVG_PACE_DIFFERENCE.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();
                cell.setText(UI.format_mm_ss(segment.paceDiff));
            }
        });
    }

    /**
     * column: average pulse
     */
    private void defineColumnAvgPulse() {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.AVG_PULSE.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent event) {
                ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_PULSE);
                _segmentViewer.refresh();
            }
        });
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final int pulse = ((TourSegment) cell.getElement()).pulse;

                if (pulse == 0) {
                    cell.setText(UI.EMPTY_STRING);
                } else {
                    cell.setText(Integer.toString(pulse));
                }
            }
        });
    }

    /**
     * column: pulse difference
     */
    private void defineColumnAvgPulseDifference() {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.AVG_PULSE_DIFFERENCE.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final int pulseDiff = ((TourSegment) cell.getElement()).pulseDiff;

                if (pulseDiff == Integer.MIN_VALUE) {
                    cell.setText(UI.EMPTY_STRING);
                } else if (pulseDiff == 0) {
                    cell.setText(UI.DASH);
                } else {
                    cell.setText(Integer.toString(pulseDiff));
                }
            }
        });
    }

    /**
     * column: average speed
     */
    private void defineColumnAvgSpeed() {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.AVG_SPEED.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent event) {
                ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_SPEED);
                _segmentViewer.refresh();
            }
        });
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();
                cell.setText(_nf_1_1.format(segment.speed));
            }
        });
    }

    /**
     * column: distance (km/mile)
     */
    private void defineColumnDistance(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.DISTANCE.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                cell.setText(_nf_3_3.format((segment.distanceDiff) / (1000 * UI.UNIT_VALUE_DISTANCE)));
            }
        });
    }

    /**
     * column: TOTAL distance (km/mile)
     */
    private void defineColumnDistanceTotal(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.DISTANCE_TOTAL.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                cell.setText(_nf_3_3.format((segment.distanceTotal) / (1000 * UI.UNIT_VALUE_DISTANCE)));
            }
        });
    }

    /**
     * column: driving time
     */
    private void defineColumnDrivingTime(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.DRIVING_TIME.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                final TourSegment segment = (TourSegment) cell.getElement();
                final int drivingTime = segment.drivingTime;
                if (drivingTime == 0) {
                    cell.setText(UI.EMPTY_STRING);
                } else {
                    cell.setText(UI.format_hh_mm_ss(drivingTime));
                }
            }
        });
    }

    /**
     * column: gradient
     */
    private void defineColumnGradient() {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.GRADIENT.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent event) {
                ((ViewSorter) _segmentViewer.getSorter()).setSortColumn(COLUMN_GRADIENT);
                _segmentViewer.refresh();
            }
        });
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                cell.setText(_nf_1_1.format(segment.gradient));
            }
        });
    }

    /**
     * column: break time
     */
    private void defineColumnPausedTime(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.PAUSED_TIME.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                final TourSegment segment = (TourSegment) cell.getElement();
                final int breakTime = segment.breakTime;
                if (breakTime == 0) {
                    cell.setText(UI.EMPTY_STRING);
                } else {
                    cell.setText(UI.format_hh_mm_ss(breakTime));
                }
            }
        });
    }

    /**
     * column: recording time
     */
    private void defineColumnRecordingTime(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.RECORDING_TIME.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                final TourSegment segment = (TourSegment) cell.getElement();
                cell.setText(UI.format_hh_mm_ss(segment.recordingTime));
            }
        });
    }

    /**
     * column: TOTAL recording time
     */
    private void defineColumnRecordingTimeTotal(final SelectionAdapter defaultColumnSelectionListener) {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.RECORDING_TIME_TOTAL.createColumn(_columnManager, _pc);
        colDef.setIsDefaultColumn();
        colDef.addSelectionListener(defaultColumnSelectionListener);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                final TourSegment segment = (TourSegment) cell.getElement();
                cell.setText(UI.format_hh_mm_ss(segment.timeTotal));
            }
        });
    }

    /**
     * column: data serie start/end index
     */
    private void defineColumnSerieStartEndIndex() {

        final ColumnDefinition colDef;

        colDef = TableColumnFactory.SERIE_START_END_INDEX.createColumn(_columnManager, _pc);
        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourSegment segment = (TourSegment) cell.getElement();

                int startIndex = segment.serieIndexStart;
                final int endIndex = segment.serieIndexEnd;

                if (startIndex > 0) {
                    startIndex++;
                }

                cell.setText(startIndex == endIndex ? //
                Integer.toString(startIndex) : startIndex + UI.DASH_WITH_SPACE + endIndex);
            }
        });
    }

    @Override
    public void dispose() {

        final IWorkbenchPage wbPage = getSite().getPage();
        wbPage.removePostSelectionListener(_postSelectionListener);
        wbPage.removePartListener(_partListener);

        _prefStore.removePropertyChangeListener(_prefChangeListener);
        TourManager.getInstance().removeTourEventListener(_tourEventListener);

        super.dispose();
    }

    /**
     * notify listeners to show/hide the segments
     */
    private void fireSegmentLayerChanged() {

        // show/hide the segments in the chart
        TourManager.fireEvent(TourEventId.SEGMENT_LAYER_CHANGED, _isShowSegmentsInChart, TourSegmenterView.this);
    }

    /**
     * try to get the tour chart and/or editor from the active part
     * 
     * @param tourData
     * @return Returns the {@link TourChart} for the requested {@link TourData}
     */
    private TourChart getActiveTourChart(final TourData tourData) {

        // get tour chart from the active editor part
        for (final IWorkbenchWindow wbWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) {
            for (final IWorkbenchPage wbPage : wbWindow.getPages()) {

                final IEditorPart activeEditor = wbPage.getActiveEditor();
                if (activeEditor instanceof TourEditor) {

                    /*
                     * check if the tour data in the editor is the same
                     */
                    final TourChart tourChart = ((TourEditor) activeEditor).getTourChart();
                    final TourData tourChartTourData = tourChart.getTourData();
                    if (tourChartTourData == tourData) {

                        try {
                            TourManager.checkTourData(tourData, tourChartTourData);
                        } catch (final MyTourbookException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        // get tour chart from the tour chart view
        for (final IWorkbenchWindow wbWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) {
            for (final IWorkbenchPage wbPage : wbWindow.getPages()) {

                final IViewReference viewRef = wbPage.findViewReference(TourChartView.ID);
                if (viewRef != null) {

                    final IViewPart view = viewRef.getView(false);
                    if (view instanceof TourChartView) {

                        final TourChartView tourChartView = ((TourChartView) view);

                        /*
                         * check if the tour data in the tour chart is the same
                         */
                        final TourChart tourChart = tourChartView.getTourChart();
                        final TourData tourChartTourData = tourChart.getTourData();
                        if (tourChartTourData == tourData) {
                            try {
                                TourManager.checkTourData(tourData, tourChartTourData);
                            } catch (final MyTourbookException e) {
                                e.printStackTrace();
                            }

                            return tourChart;
                        }
                    }
                }
            }
        }

        return null;
    }

    public ColumnManager getColumnManager() {
        return _columnManager;
    }

    /**
     * @return Returns distance in meters from the scale control
     */
    private float getDistance() {

        final float selectedDistance = _scaleDistance.getSelection();
        float scaleDistance;

        if (UI.UNIT_VALUE_DISTANCE == UI.UNIT_MILE) {

            // miles are displayed

            scaleDistance = (selectedDistance) * 1000 / 8;

            if (scaleDistance == 0) {
                scaleDistance = 1000 / 8;
            }

            // convert mile -> meters
            scaleDistance *= UI.UNIT_MILE;

        } else {

            // meters are displayed

            scaleDistance = selectedDistance * MAX_DISTANCE_SCALE_METRIC;

            // ensure the distance in not below 100m
            if (scaleDistance < 100) {
                scaleDistance = 100;
            }
        }

        return scaleDistance;
    }

    //   @SuppressWarnings("unchecked")
    //   @Override
    //   public Object getAdapter(final Class adapter) {
    //
    //      if (adapter == ColumnViewer.class) {
    //         return fSegmentViewer;
    //      }
    //
    //      return Platform.getAdapterManager().getAdapter(this, adapter);
    //   }

    private int getDPTolerance() {
        return (int) ((Math.pow(_scaleTolerance.getSelection(), 2.05)) / 50.0);
    }

    private TourSegmenter getSelectedSegmenter() {

        if (_availableSegmenter.size() == 0) {
            return null;
        }

        final int selectedIndex = _cboSegmenterType.getSelectionIndex();

        if (selectedIndex != -1) {
            return _availableSegmenter.get(selectedIndex);
        }

        // should not happen
        return null;
    }

    public ColumnViewer getViewer() {
        return _segmentViewer;
    }

    /**
     * hides the tour segments
     */
    private void hideTourSegmentsInChart() {

        _isShowSegmentsInChart = false;
        _actionShowSegments.setChecked(_isShowSegmentsInChart);

        fireSegmentLayerChanged();
    }

    private void onChangeBreakTime() {

        createSegments();
    }

    private void onChangedDistance() {

        updateUIDistance();

        createSegments();
    }

    private void onChangedTolerance(final int dpTolerance/* , final boolean forceRecalc */) {

        // update label in the ui
        _lblToleranceValue.setText(Integer.toString(dpTolerance));

        if (_tourData == null || (_dpTolerance == dpTolerance /* && forceRecalc == false */)) {
            return;
        }

        _dpTolerance = dpTolerance;

        // update tolerance into the tour data
        _tourData.setDpTolerance((short) dpTolerance);

        createSegments();
    }

    /**
     * defaults are the values which are stored in the pref store not the default-default which can
     * be set in the pref page
     */
    private void onGetBreakTimePrefValues() {

        final BreakTimeTool btConfig = BreakTimeTool.getPrefValues();

        // method
        _comboBreakMethod.select(btConfig.breakTimeMethod);

        // break time by time/distance
        _spinnerBreakShortestTime.setSelection(btConfig.breakShortestTime);

        final float prefBreakDistance = btConfig.breakMaxDistance / UI.UNIT_VALUE_DISTANCE_SMALL;
        _spinnerBreakMaxDistance.setSelection((int) (prefBreakDistance + 0.5));

        // break time by speed
        _spinnerBreakMinSliceSpeed.setSelection(//
                (int) (btConfig.breakMinSliceSpeed * SPEED_DIGIT_VALUE * UI.UNIT_VALUE_DISTANCE));
        _spinnerBreakMinAvgSpeed.setSelection(//
                (int) (btConfig.breakMinAvgSpeed * SPEED_DIGIT_VALUE * UI.UNIT_VALUE_DISTANCE));

        onSelectBreakTimeMethod();
    }

    private void onSaveTourAltitude() {

        _tourData.setTourAltUp(_altitudeUp);
        _tourData.setTourAltDown(_altitudeDown);

        _isTourDirty = true;

        _tourData = saveTour();
    }

    private void onSelectBreakTimeMethod() {

        switch (_comboBreakMethod.getSelectionIndex()) {
        case BreakTimeTool.BREAK_TIME_METHOD_BY_TIME_DISTANCE:
            _pagebookBreakTime.showPage(_pageBreakByTimeDistance);
            _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeByTime);
            break;

        case BreakTimeTool.BREAK_TIME_METHOD_BY_SLICE_SPEED:
            _pagebookBreakTime.showPage(_pageBreakBySliceSpeed);
            _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeBySliceSpeed);
            break;

        case BreakTimeTool.BREAK_TIME_METHOD_BY_AVG_SPEED:
            _pagebookBreakTime.showPage(_pageBreakByAvgSpeed);
            _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeByAvgSpeed);
            break;

        default:
            // this case should not happen
            _comboBreakMethod.select(BreakTimeTool.BREAK_TIME_METHOD_BY_TIME_DISTANCE);
            _pagebookBreakTime.showPage(_pageBreakByTimeDistance);
            _comboBreakMethod.setToolTipText(Messages.Compute_BreakTime_Label_Description_ComputeByTime);
            break;
        }

        // break method pages have different heights, enforce layout of the whole view part
        _pageSegmenter.layout(true, true);

        onChangeBreakTime();
    }

    /**
     * handle a tour selection event
     * 
     * @param selection
     */
    private void onSelectionChanged(final ISelection selection) {

        _isClearView = false;

        if (_isSaving) {
            return;
        }

        /*
         * run selection async because a tour could be modified and needs to be saved, modifications
         * are not reported to the tour data editor, saving needs also to be asynch with the tour
         * data editor
         */
        Display.getCurrent().asyncExec(new Runnable() {
            public void run() {

                // check if view is disposed
                if (_pageBookUI.isDisposed() || _isClearView) {
                    return;
                }

                TourData nextTourData = null;
                TourChart nextTourChart = null;

                if (selection instanceof SelectionTourData) {

                    final SelectionTourData selectionTourData = (SelectionTourData) selection;

                    nextTourData = selectionTourData.getTourData();
                    nextTourChart = selectionTourData.getTourChart();

                } else if (selection instanceof SelectionTourId) {

                    final SelectionTourId tourIdSelection = (SelectionTourId) selection;

                    if (_tourData != null) {
                        if (_tourData.getTourId().equals(tourIdSelection.getTourId())) {
                            // don't reload the same tour
                            return;
                        }
                    }

                    nextTourData = TourManager.getInstance().getTourData(tourIdSelection.getTourId());

                } else if (selection instanceof SelectionTourIds) {

                    final ArrayList<Long> tourIds = ((SelectionTourIds) selection).getTourIds();
                    if (tourIds != null && tourIds.size() > 0) {

                        final Long tourId = tourIds.get(0);

                        if (_tourData != null) {
                            if (_tourData.getTourId().equals(tourId)) {
                                // don't reload the same tour
                                return;
                            }
                        }

                        nextTourData = TourManager.getInstance().getTourData(tourId);
                    }

                } else if (selection instanceof SelectionDeletedTours) {

                    clearView();

                } else {
                    return;
                }

                if (checkDataValidation(nextTourData) == false) {
                    return;
                }

                if (_tourData != null) {
                    // it's possible that the break time serie was overwritten
                    _tourData.breakTimeSerie = null;
                }

                /*
                 * save previous tour when a new tour is selected
                 */
                if (_tourData != null && _tourData.getTourId() == nextTourData.getTourId()) {

                    // nothing to do, it's the same tour

                } else {

                    final TourData savedTour = saveTour();
                    if (savedTour != null) {

                        /*
                         * when a tour is saved, the change notification is not fired because
                         * another tour is already selected, but to update the tour in a TourViewer,
                         * a change nofification must be fired afterwords
                         */
                        //            Display.getCurrent().asyncExec(new Runnable() {
                        //               public void run() {
                        //                  TourManager.fireEvent(TourEventId.TOUR_CHANGED,
                        //                        new TourEvent(savedTour),
                        //                        TourSegmenterView.this);
                        //               }
                        //            });
                    }

                    if (nextTourChart == null) {
                        nextTourChart = getActiveTourChart(nextTourData);
                    }

                    _tourChart = nextTourChart;

                    setTour(nextTourData, false);
                }
            }
        });
    }

    private void onSelectSegmenterType(final boolean isUserSelected) {

        final TourSegmenter selectedSegmenter = getSelectedSegmenter();
        if (selectedSegmenter == null) {
            clearView();
            return;
        }

        final SegmenterType selectedSegmenterType = selectedSegmenter.segmenterType;

        /*
         * keep segmenter type which the user selected, try to reselect this segmenter when a new
         * tour is displayed
         */
        if (isUserSelected) {
            _userSelectedSegmenterType = selectedSegmenterType;
        }

        boolean isShowSaveAltitude = false;

        if (selectedSegmenterType == SegmenterType.ByAltitudeWithDP) {

            _pageBookSegmenter.showPage(_pageSegTypeDP);
            isShowSaveAltitude = true;

        } else if (selectedSegmenterType == SegmenterType.ByPulseWithDP) {

            _pageBookSegmenter.showPage(_pageSegTypeDP);

        } else if (selectedSegmenterType == SegmenterType.ByMarker) {

            _pageBookSegmenter.showPage(_pageSegTypeByMarker);

        } else if (selectedSegmenterType == SegmenterType.ByDistance) {

            _pageBookSegmenter.showPage(_pageSegTypeByDistance);

        } else if (selectedSegmenterType == SegmenterType.ByComputedAltiUpDown) {

            _pageBookSegmenter.showPage(_pageSegTypeByAltiUpDown);
            isShowSaveAltitude = true;

        } else if (selectedSegmenterType == SegmenterType.ByBreakTime) {

            _pageBookSegmenter.showPage(_pageSegTypeByBreakTime);

            // update ui + layout
            onSelectBreakTimeMethod();
        }

        _containerSaveAltitude.setVisible(isShowSaveAltitude);

        _pageSegmenter.layout();

        createSegments();
    }

    private void onSetDefaults(final Composite parent) {

        saveBreakTimeValuesInPrefStore();

        PreferencesUtil.createPreferenceDialogOn(parent.getShell(), PrefPageComputedValues.ID, null,
                PrefPageComputedValues.TAB_FOLDER_BREAK_TIME).open();
    }

    public ColumnViewer recreateViewer(final ColumnViewer columnViewer) {

        _containerViewer.setRedraw(false);
        {
            _segmentViewer.getTable().dispose();

            createUI80SegmentViewer(_containerViewer);
            _containerViewer.layout();

            // update the viewer
            reloadViewer();
        }
        _containerViewer.setRedraw(true);

        return _segmentViewer;
    }

    public void reloadViewer() {
        // force input to be reloaded
        _segmentViewer.setInput(new Object[0]);
    }

    private void restoreState() {

        /*
         * selected segmenter, this is not working because the next selected tour can remove the
         * selected segementer
         */
        //      int segmenterIndex = -1;
        //      try {
        //         segmenterIndex = _state.getInt(STATE_SELECTED_SEGMENTER_INDEX);
        //      } catch (final NumberFormatException e) {}
        //      if (segmenterIndex < 0) {
        //         segmenterIndex = 0;
        //      }
        //      _cboSegmenterType.select(segmenterIndex);

        // selected distance
        int stateDistance = 10;
        try {
            stateDistance = _state.getInt(STATE_SELECTED_DISTANCE);
        } catch (final NumberFormatException e) {
        }
        _scaleDistance.setSelection(stateDistance);
        updateUIDistance();

        // selected min altitude
        int minAltitudeIndex = PrefPageComputedValues.DEFAULT_MIN_ALTITUDE_INDEX;
        try {
            minAltitudeIndex = _state.getInt(STATE_SELECTED_MIN_ALTITUDE_INDEX);
        } catch (final NumberFormatException e) {
        }
        if (minAltitudeIndex < 0) {
            minAltitudeIndex = PrefPageComputedValues.DEFAULT_MIN_ALTITUDE_INDEX;
        }
        if (_cboMinAltitude.getItemCount() == 0) {
            updateUIMinAltitude();
        }

        _cboMinAltitude.select(minAltitudeIndex);
        updateUIMinAltitude();

        /*
         * break time
         */
        final BreakTimeTool btConfig = BreakTimeTool.getPrefValues();

        // method
        _comboBreakMethod.select(Util.getStateInt(_state, STATE_SELECTED_BREAK_METHOD,
                BreakTimeTool.BREAK_TIME_METHOD_BY_TIME_DISTANCE));

        // break time by time/distance
        _spinnerBreakShortestTime.setSelection(
                Util.getStateInt(_state, STATE_BREAK_TIME_MIN_TIME_VALUE, btConfig.breakShortestTime));

        final float breakDistance = Util.getStateFloat(_state, STATE_BREAK_TIME_MIN_DISTANCE_VALUE,
                btConfig.breakMaxDistance) / UI.UNIT_VALUE_DISTANCE_SMALL;
        _spinnerBreakMaxDistance.setSelection((int) (breakDistance + 0.5));

        // break by slice speed
        final float stateSliceSpeed = Util.getStateFloat(_state, STATE_BREAK_TIME_MIN_SLICE_SPEED,
                btConfig.breakMinSliceSpeed);

        _spinnerBreakMinSliceSpeed
                .setSelection((int) (stateSliceSpeed * SPEED_DIGIT_VALUE * UI.UNIT_VALUE_DISTANCE));

        // break by avg speed
        final float stateAvgSpeed = Util.getStateFloat(_state, STATE_BREAK_TIME_MIN_AVG_SPEED,
                btConfig.breakMinAvgSpeed);

        _spinnerBreakMinAvgSpeed.setSelection((int) (stateAvgSpeed * SPEED_DIGIT_VALUE * UI.UNIT_VALUE_DISTANCE));
    }

    /**
     * saves break time values in the pref store
     */
    private void saveBreakTimeValuesInPrefStore() {

        _prefStore.setValue(ITourbookPreferences.BREAK_TIME_METHOD, _comboBreakMethod.getSelectionIndex());

        // by time/distance
        _prefStore.setValue(ITourbookPreferences.BREAK_TIME_SHORTEST_TIME,
                _spinnerBreakShortestTime.getSelection());
        final float breakDistance = _spinnerBreakMaxDistance.getSelection() * UI.UNIT_VALUE_DISTANCE_SMALL;
        _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MAX_DISTANCE, breakDistance);

        // by slice speed
        final float breakMinSliceSpeed = _spinnerBreakMinSliceSpeed.getSelection() / SPEED_DIGIT_VALUE
                / UI.UNIT_VALUE_DISTANCE;
        _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MIN_SLICE_SPEED, breakMinSliceSpeed);

        // by avg speed
        final float breakMinAvgSpeed = _spinnerBreakMinAvgSpeed.getSelection() / SPEED_DIGIT_VALUE
                / UI.UNIT_VALUE_DISTANCE;
        _prefStore.setValue(ITourbookPreferences.BREAK_TIME_MIN_AVG_SPEED, breakMinAvgSpeed);
    }

    /**
     * save break time values in the viewer settings
     */
    private void saveBreakTimeValuesInState() {

        _state.put(STATE_SELECTED_BREAK_METHOD, _comboBreakMethod.getSelectionIndex());

        // by time/distance
        final float breakDistance = _spinnerBreakMaxDistance.getSelection() * UI.UNIT_VALUE_DISTANCE_SMALL;
        _state.put(STATE_BREAK_TIME_MIN_DISTANCE_VALUE, breakDistance);
        _state.put(STATE_BREAK_TIME_MIN_TIME_VALUE, _spinnerBreakShortestTime.getSelection());

        // by slice speed
        final float breakMinSliceSpeed = _spinnerBreakMinSliceSpeed.getSelection() / SPEED_DIGIT_VALUE
                / UI.UNIT_VALUE_DISTANCE;
        _state.put(STATE_BREAK_TIME_MIN_SLICE_SPEED, breakMinSliceSpeed);

        // by avg speed
        final float breakMinAvgSpeed = _spinnerBreakMinAvgSpeed.getSelection() / SPEED_DIGIT_VALUE
                / UI.UNIT_VALUE_DISTANCE;
        _state.put(STATE_BREAK_TIME_MIN_AVG_SPEED, breakMinAvgSpeed);
    }

    private void saveState() {

        _columnManager.saveState(_state);

        _state.put(STATE_SELECTED_SEGMENTER_INDEX, _cboSegmenterType.getSelectionIndex());
        _state.put(STATE_SELECTED_MIN_ALTITUDE_INDEX, _cboMinAltitude.getSelectionIndex());
        _state.put(STATE_SELECTED_DISTANCE, _scaleDistance.getSelection());

        saveBreakTimeValuesInState();
    }

    private TourData saveTour() {

        if (_tourData != null) {
            // it's possible that the break time serie was overwritten
            _tourData.breakTimeSerie = null;
        }

        if (_isTourDirty == false || _tourData == null || _savedDpTolerance == -1) {
            // nothing to do
            return null;
        }

        TourData savedTour;
        _isSaving = true;
        {
            savedTour = TourManager.saveModifiedTour(_tourData);
        }
        _isSaving = false;

        _isTourDirty = false;

        return savedTour;
    }

    private void setCellColor(final ViewerCell cell, final int altiDiff) {

        if (altiDiff == 0) {
            cell.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
        } else if (altiDiff > 0) {
            cell.setBackground(JFaceResources.getColorRegistry().get(UI.VIEW_COLOR_BG_SEGMENTER_UP));
        } else if (altiDiff < 0) {
            cell.setBackground(JFaceResources.getColorRegistry().get(UI.VIEW_COLOR_BG_SEGMENTER_DOWN));
        }
    }

    @Override
    public void setFocus() {
        _scaleTolerance.setFocus();
    }

    private void setMaxDistanceScale() {

        if (UI.UNIT_VALUE_DISTANCE == UI.UNIT_MILE) {

            // imperial

            _maxDistanceScale = MAX_DISTANCE_SCALE_MILE;
            _scaleDistancePage = 8;

        } else {

            // metric

            _maxDistanceScale = MAX_DISTANCE_SCALE_METRIC;
            _scaleDistancePage = 10;
        }
    }

    /**
     * Sets the tour for the segmenter
     * 
     * @param tourData
     * @param forceUpdate
     */
    private void setTour(final TourData tourData, final boolean forceUpdate) {

        if (tourData == null || (forceUpdate == false && tourData == _tourData)) {
            return;
        }

        _isDirtyDisabled = true;
        {
            _tourData = tourData;

            _pageBookUI.showPage(_pageSegmenter);

            // update tour title
            _lblTitle.setText(TourManager.getTourTitleDetailed(_tourData));

            // keep original dp tolerance
            _savedDpTolerance = _dpTolerance = _tourData.getDpTolerance();

            // update segmenter values, the factor is defined by experimentals
            final float factor = 1 / 2.05f;
            final double tolerance = Math.pow(_dpTolerance * 50, factor);

            _scaleTolerance.setSelection((int) tolerance);
            _lblToleranceValue.setText(Integer.toString(_tourData.getDpTolerance()));

            _btnSaveTour.setEnabled(_tourData.getTourPerson() != null);
        }
        _isDirtyDisabled = false;

        updateUISegmenterSelector();
        onSelectSegmenterType(false);
    }

    /**
     * when dp tolerance was changed set the tour dirty
     */
    private void setTourDirty() {

        if (_isDirtyDisabled) {
            return;
        }

        if (_tourData != null && _savedDpTolerance != _tourData.getDpTolerance()) {
            _isTourDirty = true;
        }
    }

    /**
     * update ascending altitude computed value
     */
    private void updateUIAltitude() {

        final TourSegmenter selectedSegmenter = getSelectedSegmenter();
        if (selectedSegmenter == null) {
            clearView();
            return;
        }

        final SegmenterType selectedSegmenterType = selectedSegmenter.segmenterType;
        int[] altitudeSegments;
        if (selectedSegmenterType == SegmenterType.ByComputedAltiUpDown) {
            altitudeSegments = _tourData.segmentSerieComputedAltitudeDiff;
        } else {
            altitudeSegments = _tourData.segmentSerieAltitudeDiff;
        }

        if (altitudeSegments == null) {
            _lblAltitudeUp.setText(UI.EMPTY_STRING);
            return;
        }

        // compute total alti up/down from the segments
        _altitudeUp = 0;
        _altitudeDown = 0;

        for (final int altitude : altitudeSegments) {
            if (altitude > 0) {
                _altitudeUp += altitude;
            } else {
                _altitudeDown += altitude;
            }
        }

        final StringBuilder sb = new StringBuilder();
        sb.append(Integer.toString((int) (_altitudeUp / UI.UNIT_VALUE_ALTITUDE)));
        sb.append(UI.SLASH_WITH_SPACE);
        sb.append(Integer.toString((int) (_tourData.getTourAltUp() / UI.UNIT_VALUE_ALTITUDE)));
        sb.append(UI.SPACE);
        sb.append(UI.UNIT_LABEL_ALTITUDE);

        _lblAltitudeUp.setText(sb.toString());
    }

    private void updateUIBreakTime() {

        _lblTourBreakTime.setText(Long.toString(_tourBreakTime) + UI.SPACE + Messages.App_Unit_Seconds_Small
                + UI.SPACE4 + UI.format_hh_mm_ss(_tourBreakTime));

        _containerBreakTime.layout();
    }

    private void updateUIDistance() {

        float scaleDistance = getDistance() / UI.UNIT_VALUE_DISTANCE;

        if (UI.UNIT_VALUE_DISTANCE == UI.UNIT_MILE) {

            // imperial

            scaleDistance /= 1000;

            final int distanceInt = (int) scaleDistance;
            final float distanceFract = scaleDistance - distanceInt;

            // create distance for imperials which shows the fraction with 1/8, 1/4, 3/8 ...
            final StringBuilder sb = new StringBuilder();
            if (distanceInt > 0) {
                sb.append(Integer.toString(distanceInt));
                sb.append(UI.SPACE);
            }

            if (Math.abs(distanceFract - 0.125f) <= 0.01) {
                sb.append("1/8"); //$NON-NLS-1$
                sb.append(UI.SPACE);
            } else if (Math.abs(distanceFract - 0.25f) <= 0.01) {
                sb.append("1/4"); //$NON-NLS-1$
                sb.append(UI.SPACE);
            } else if (Math.abs(distanceFract - 0.375) <= 0.01) {
                sb.append("3/8"); //$NON-NLS-1$
                sb.append(UI.SPACE);
            } else if (Math.abs(distanceFract - 0.5f) <= 0.01) {
                sb.append("1/2"); //$NON-NLS-1$
                sb.append(UI.SPACE);
            } else if (Math.abs(distanceFract - 0.625) <= 0.01) {
                sb.append("5/8"); //$NON-NLS-1$
                sb.append(UI.SPACE);
            } else if (Math.abs(distanceFract - 0.75f) <= 0.01) {
                sb.append("3/4"); //$NON-NLS-1$
                sb.append(UI.SPACE);
            } else if (Math.abs(distanceFract - 0.875) <= 0.01) {
                sb.append("7/8"); //$NON-NLS-1$
                sb.append(UI.SPACE);
            }

            sb.append(UI.UNIT_LABEL_DISTANCE);

            // update UI
            _lblDistanceValue.setText(sb.toString());

        } else {

            // metric

            // format distance
            String distanceText;
            final float selectedDistance = scaleDistance / 1000;
            if (selectedDistance >= 10) {
                distanceText = _nf_0_0.format(selectedDistance);
            } else {
                distanceText = _nf_1_1.format(selectedDistance);
            }

            // update UI
            _lblDistanceValue.setText(distanceText + UI.SPACE + UI.UNIT_LABEL_DISTANCE);
        }
    }

    private void updateUIMinAltitude() {

        // preserve selection
        int prevSelection = _cboMinAltitude.getSelectionIndex();
        if (prevSelection < 0) {
            prevSelection = PrefPageComputedValues.DEFAULT_MIN_ALTITUDE_INDEX;
        }

        _cboMinAltitude.removeAll();

        for (final int minAlti : PrefPageComputedValues.ALTITUDE_MINIMUM) {
            _cboMinAltitude.add(Integer.toString((int) (minAlti / UI.UNIT_VALUE_ALTITUDE)));
        }

        _cboMinAltitude.select(prevSelection);
    }

    private void updateUISegmenterSelector() {

        final TourSegmenter currentSegmenter = getSelectedSegmenter();
        final int availableSegmenterData = checkSegmenterData(_tourData);

        // get all segmenters which can segment current tour
        _availableSegmenter.clear();
        for (final TourSegmenter tourSegmenter : _allTourSegmenter.values()) {

            final int requiredDataSeries = tourSegmenter.requiredDataSeries;

            if ((availableSegmenterData & requiredDataSeries) == requiredDataSeries) {
                _availableSegmenter.add(tourSegmenter);
            }
        }

        // sort by name
        Collections.sort(_availableSegmenter);

        /*
         * fill list box
         */
        int segmenterIndex = 0;
        int previousSegmenterIndex = -1;
        int userSelectedSegmenterIndex = -1;

        _cboSegmenterType.removeAll();
        for (final TourSegmenter tourSegmenter : _availableSegmenter) {

            _cboSegmenterType.add(tourSegmenter.name);

            if (tourSegmenter.segmenterType == _userSelectedSegmenterType) {
                userSelectedSegmenterIndex = segmenterIndex;
            }
            if (tourSegmenter == currentSegmenter) {
                previousSegmenterIndex = segmenterIndex;
            }

            segmenterIndex++;
        }

        // reselect previous segmenter
        if (userSelectedSegmenterIndex != -1) {
            _cboSegmenterType.select(userSelectedSegmenterIndex);
        } else {
            if (previousSegmenterIndex != -1) {
                _cboSegmenterType.select(previousSegmenterIndex);
            } else {
                _cboSegmenterType.select(0);
            }
        }
    }
}