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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (C) 2005, 2015 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.util.ArrayList;

import net.tourbook.Messages;
import net.tourbook.application.TourbookPlugin;
import net.tourbook.chart.Chart;
import net.tourbook.chart.ChartDataModel;
import net.tourbook.chart.ChartDataSerie;
import net.tourbook.chart.ChartDataXSerie;
import net.tourbook.chart.ChartDataYSerie;
import net.tourbook.chart.ChartDrawingData;
import net.tourbook.chart.ColorCache;
import net.tourbook.chart.ComputeChartValue;
import net.tourbook.chart.GraphDrawingData;
import net.tourbook.chart.SelectionChartInfo;
import net.tourbook.chart.SelectionChartXSliderPosition;
import net.tourbook.chart.Util;
import net.tourbook.common.UI;
import net.tourbook.data.TourData;
import net.tourbook.preferences.ITourbookPreferences;
import net.tourbook.tour.ITourEventListener;
import net.tourbook.tour.SelectionDeletedTours;
import net.tourbook.tour.SelectionTourChart;
import net.tourbook.tour.SelectionTourData;
import net.tourbook.tour.SelectionTourId;
import net.tourbook.tour.TourEventId;
import net.tourbook.tour.TourManager;
import net.tourbook.ui.tourChart.TourChart;
import net.tourbook.ui.tourChart.TourChartView;

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.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.part.ViewPart;

public class TourChartAnalyzerView extends ViewPart {

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

    private static final int LAYOUT_1_COLUMNS = 0;
    private static final int LAYOUT_2_COLUMNS = 1;
    private static final int LAYOUT_3_COLUMNS = 2;
    private static final int LAYOUT_6_COLUMNS = 3;

    private final IPreferenceStore _prefStore = TourbookPlugin.getPrefStore();

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

    private ChartDataModel _chartDataModel;

    private ChartDrawingData _chartDrawingData;
    private ArrayList<GraphDrawingData> _graphDrawingData;

    private final ArrayList<GraphInfo> _graphInfos = new ArrayList<GraphInfo>();

    private final ColorCache _colorCache = new ColorCache();

    private Color _bgColorHeader;
    private Font _fontBold;

    private SelectionChartInfo _chartInfo;

    private int _layoutFormat;

    private int _valueIndexLeftBackup;
    private int _valueIndexRightBackup;

    /**
     * space between columns
     */
    int _columnSpacing = 1;

    private boolean _isPartVisible = false;

    private PixelConverter _pc;

    //   private int                     _debugCounter;

    private int _valueIndexRightLast;
    private int _valueIndexLeftLast;

    /*
     * UI controls
     */
    private PageBook _pageBook;

    private Composite _innerScContainer;
    private Composite _partContainer;
    private Composite _pageAnalyzer;
    private Composite _pageNoData;

    private ScrolledComposite _scrolledContainer;

    public TourChartAnalyzerView() {
        super();
    }

    private void addListeners() {

        final IWorkbenchPage page = getSite().getPage();

        _partContainer.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(final ControlEvent event) {
                onResizeUI();
            }
        });

        _postSelectionListener = new ISelectionListener() {
            @Override
            public void selectionChanged(final IWorkbenchPart part, final ISelection selection) {
                onSelectionChanged(selection);
            }
        };
        page.addPostSelectionListener(_postSelectionListener);
    }

    private void addPartListener() {

        _partListener = new IPartListener2() {

            @Override
            public void partActivated(final IWorkbenchPartReference partRef) {
            }

            @Override
            public void partBroughtToTop(final IWorkbenchPartReference partRef) {
            }

            @Override
            public void partClosed(final IWorkbenchPartReference partRef) {

                final IWorkbenchPart closedPart = partRef.getPart(false);
                if (closedPart instanceof TourChartView) {

                    // chart is closed, hide tour chart analyzer data
                    clearView();
                }
            }

            @Override
            public void partDeactivated(final IWorkbenchPartReference partRef) {
            }

            @Override
            public void partHidden(final IWorkbenchPartReference partRef) {
                if (partRef.getPart(false) == TourChartAnalyzerView.this) {
                    _isPartVisible = false;
                }
            }

            @Override
            public void partInputChanged(final IWorkbenchPartReference partRef) {
            }

            @Override
            public void partOpened(final IWorkbenchPartReference partRef) {

                /**
                 * partVisible event is NOT fired when the app is opened even when the part is
                 * displayed, this is a workaround.
                 */
                if (partRef.getPart(false) == TourChartAnalyzerView.this) {
                    _isPartVisible = true;
                }
            }

            @Override
            public void partVisible(final IWorkbenchPartReference partRef) {
                if (partRef.getPart(false) == TourChartAnalyzerView.this) {
                    _isPartVisible = true;
                }
            }
        };

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

    private void addPrefListeners() {

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

                final String property = event.getProperty();

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

                    // dispose old colors
                    _colorCache.dispose();

                    updateInfo(_chartInfo, false);
                }
            }
        };

        _prefStore.addPropertyChangeListener(_prefChangeListener);
    }

    private void addTourEventListener() {

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

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

                if ((eventId == TourEventId.TOUR_SELECTION) && eventData instanceof ISelection) {

                    onSelectionChanged((ISelection) eventData);

                } else if (eventId == TourEventId.SLIDER_POSITION_CHANGED && eventData instanceof ISelection) {

                    onSelectionChanged((ISelection) eventData);

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

                    clearView();
                }
            }
        };

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

    private void clearView() {

        _chartInfo = null;

        _pageBook.showPage(_pageNoData);
    }

    @Override
    public void createPartControl(final Composite parent) {

        createUI(parent);

        addPartListener();
        addListeners();
        addPrefListeners();
        addTourEventListener();

        _pageBook.showPage(_pageNoData);

        showTour();
    }

    /**
     * @param parent
     */
    private void createUI(final Composite parent) {

        initUI(parent);

        _partContainer = parent;

        _pageBook = new PageBook(parent, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(_pageBook);

        _pageNoData = UI.createUI_PageNoData(_pageBook, Messages.TourAnalyzer_Label_NoTourOrChart);

        _pageAnalyzer = new Composite(_pageBook, SWT.NONE);
        GridLayoutFactory.fillDefaults().applyTo(_pageAnalyzer);
        _pageAnalyzer.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_GREEN));
        //      _pageAnalyzer.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
    }

    /**
     * This UI can only be created when {@link #_chartDataModel} is set.
     * 
     * @param isForceRecreate
     */
    private void createUI_10_Analyzer(final boolean isForceRecreate) {

        // get layout format (number of columns)
        final int clientWidth = _partContainer.getClientArea().width;
        final int layoutFormat = clientWidth < _pc.convertHorizontalDLUsToPixels(100)
                //
                ? LAYOUT_1_COLUMNS
                : clientWidth < _pc.convertHorizontalDLUsToPixels(150)
                        //
                        ? LAYOUT_2_COLUMNS
                        : clientWidth < _pc.convertHorizontalDLUsToPixels(300)
                                //
                                ? LAYOUT_3_COLUMNS
                                : LAYOUT_6_COLUMNS;

        final int numColumns = layoutFormat == LAYOUT_1_COLUMNS //
                ? 2
                : layoutFormat == LAYOUT_2_COLUMNS //
                        ? 3
                        : layoutFormat == LAYOUT_3_COLUMNS ? 4 : 8;

        // check if UI needs to be created
        if (isForceRecreate //

                // check if layout has changed
                || layoutFormat != _layoutFormat) {

            _layoutFormat = layoutFormat;

            createUI_20_CreateAnalyzer(numColumns);

            _pageBook.showPage(_pageAnalyzer);
        }
    }

    private void createUI_20_CreateAnalyzer(final int numColumns) {

        // recreate the UI
        if (_scrolledContainer != null) {
            _scrolledContainer.dispose();
            _scrolledContainer = null;
        }

        // create scrolled container
        _scrolledContainer = new ScrolledComposite(_pageAnalyzer, SWT.V_SCROLL | SWT.H_SCROLL);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(_scrolledContainer);
        _scrolledContainer.setExpandVertical(true);
        _scrolledContainer.setExpandHorizontal(true);

        _scrolledContainer.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(final ControlEvent e) {
                _scrolledContainer.setMinSize(_innerScContainer.computeSize(SWT.DEFAULT, SWT.DEFAULT));
            }
        });

        // create inner container
        _innerScContainer = new Composite(_scrolledContainer, SWT.NONE);
        GridLayoutFactory.fillDefaults().spacing(0, 0).numColumns(numColumns).applyTo(_innerScContainer);
        _innerScContainer.setBackground(_bgColorHeader);
        //      _innerScContainer.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
        //      _innerScContainer.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));

        _scrolledContainer.setContent(_innerScContainer);

        _columnSpacing = 1;

        switch (_layoutFormat) {
        case LAYOUT_1_COLUMNS:
            createUI_70_1_Columns();
            break;

        case LAYOUT_2_COLUMNS:
            createUI_70_2_Columns();
            break;

        case LAYOUT_3_COLUMNS:
            createUI_70_3_Columns();
            break;

        default:
            createUI_70_6_Columns();
            break;
        }
    }

    private void createUI_70_1_Columns() {

        _graphInfos.clear();

        // create graph info list
        for (final ChartDataSerie xyData : _chartDataModel.getXyData()) {
            _graphInfos.add(new GraphInfo(this, xyData, _innerScContainer));
        }

        // ----------------------------------------------------------------

        createUIHeader_10_Left();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_10_Left();
            graphInfo.createUI_ValueUnit();
        }

        // ----------------------------------------------------------------

        createVerticalBorder();

        createUIHeader_20_Right();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_20_Right();
            graphInfo.createUI_ValueUnit();
        }

        // ----------------------------------------------------------------

        createVerticalBorder();

        createUIHeader_50_Diff();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_50_Diff();
            graphInfo.createUI_ValueUnit();
        }

        // ----------------------------------------------------------------

        createVerticalBorder();

        createUIHeader_60_Avg();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_60_Avg();
            graphInfo.createUI_ValueUnit();
        }

        // ----------------------------------------------------------------

        createVerticalBorder();

        createUIHeader_30_Min();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_30_Min();
            graphInfo.createUI_ValueUnit();
        }

        // ----------------------------------------------------------------

        createVerticalBorder();

        createUIHeader_40_Max();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_40_Max();
            graphInfo.createUI_ValueUnit();
        }
    }

    private void createUI_70_2_Columns() {

        createUIHeader_10_Left();
        createUIHeader_20_Right();
        createUIHeader_UnitLabel();

        // add all graphs and the x axis to the layout
        _graphInfos.clear();
        for (final ChartDataSerie xyData : _chartDataModel.getXyData()) {

            final GraphInfo graphInfo = new GraphInfo(this, xyData, _innerScContainer);

            graphInfo.createUI_Info_10_Left();
            graphInfo.createUI_Info_20_Right();
            graphInfo.createUI_ValueUnit();

            _graphInfos.add(graphInfo);
        }

        // ----------------------------------------------------------------

        createVerticalBorder();

        createUIHeader_50_Diff();
        createUIHeader_60_Avg();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_50_Diff();
            graphInfo.createUI_Info_60_Avg();
            graphInfo.createUI_ValueUnit();
        }

        // ----------------------------------------------------------------

        createVerticalBorder();

        createUIHeader_30_Min();
        createUIHeader_40_Max();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_30_Min();
            graphInfo.createUI_Info_40_Max();
            graphInfo.createUI_ValueUnit();
        }
    }

    private void createUI_70_3_Columns() {

        createUIHeader_10_Left();
        createUIHeader_20_Right();
        createUIHeader_50_Diff();
        createUIHeader_UnitLabel();

        // add all graphs and the x axis to the layout
        _graphInfos.clear();
        for (final ChartDataSerie xyData : _chartDataModel.getXyData()) {

            final GraphInfo graphInfo = new GraphInfo(this, xyData, _innerScContainer);

            graphInfo.createUI_Info_10_Left();
            graphInfo.createUI_Info_20_Right();
            graphInfo.createUI_Info_50_Diff();
            graphInfo.createUI_ValueUnit();

            _graphInfos.add(graphInfo);
        }

        createVerticalBorder();

        createUIHeader_30_Min();
        createUIHeader_40_Max();
        createUIHeader_60_Avg();
        createUIHeader_UnitLabel();

        for (final GraphInfo graphInfo : _graphInfos) {
            graphInfo.createUI_Info_30_Min();
            graphInfo.createUI_Info_40_Max();
            graphInfo.createUI_Info_60_Avg();
            graphInfo.createUI_ValueUnit();
        }
    }

    private void createUI_70_6_Columns() {

        createUIHeader_ValueLabel();
        createUIHeader_10_Left();
        createUIHeader_20_Right();
        createUIHeader_30_Min();
        createUIHeader_40_Max();
        createUIHeader_50_Diff();
        createUIHeader_60_Avg();
        createUIHeader_UnitLabel();

        // add all graphs and the x axis to the layout
        _graphInfos.clear();
        for (final ChartDataSerie xyData : _chartDataModel.getXyData()) {

            final GraphInfo graphInfo = new GraphInfo(this, xyData, _innerScContainer);

            graphInfo.createUI_ValueLabel();

            graphInfo.createUI_Info_10_Left();
            graphInfo.createUI_Info_20_Right();
            graphInfo.createUI_Info_30_Min();
            graphInfo.createUI_Info_40_Max();
            graphInfo.createUI_Info_50_Diff();
            graphInfo.createUI_Info_60_Avg();

            graphInfo.createUI_ValueUnit();

            _graphInfos.add(graphInfo);
        }
    }

    private void createUIHeader_10_Left() {

        final Label label = new Label(_innerScContainer, SWT.TRAIL);
        label.setText(Messages.TourAnalyzer_Label_left + UI.SPACE);
        label.setFont(_fontBold);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);
    }

    private void createUIHeader_20_Right() {

        final Label label = new Label(_innerScContainer, SWT.TRAIL);
        label.setText(Messages.TourAnalyzer_Label_right + UI.SPACE);
        label.setFont(_fontBold);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);
    }

    private void createUIHeader_30_Min() {

        final Label label = new Label(_innerScContainer, SWT.TRAIL);
        label.setText(Messages.TourAnalyzer_Label_minimum + UI.SPACE);
        label.setFont(_fontBold);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);
    }

    private void createUIHeader_40_Max() {

        final Label label = new Label(_innerScContainer, SWT.TRAIL);
        label.setText(Messages.TourAnalyzer_Label_maximum + UI.SPACE);
        label.setFont(_fontBold);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);
    }

    private void createUIHeader_50_Diff() {

        final Label label = new Label(_innerScContainer, SWT.TRAIL);
        label.setText(Messages.TourAnalyzer_Label_difference + UI.SPACE);
        label.setFont(_fontBold);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);
    }

    private void createUIHeader_60_Avg() {

        final Label label = new Label(_innerScContainer, SWT.TRAIL);
        label.setText(Messages.TourAnalyzer_Label_average + UI.SPACE);
        label.setFont(_fontBold);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);
    }

    private void createUIHeader_UnitLabel() {

        final Label label = new Label(_innerScContainer, SWT.LEFT);
        label.setText(UI.EMPTY_STRING);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);

        // spacer
        //      final Canvas canvas = new Canvas(_innerScContainer, SWT.NONE);
        //      GridDataFactory.fillDefaults()//
        //            .align(SWT.BEGINNING, SWT.BEGINNING)
        //            .hint(0, 0)
        //            .applyTo(canvas);
    }

    private void createUIHeader_ValueLabel() {

        final Label label = new Label(_innerScContainer, SWT.NONE);
        label.setText(UI.SPACE + Messages.TourAnalyzer_Label_value);
        label.setFont(_fontBold);
        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
        label.setBackground(_bgColorHeader);
    }

    private void createVerticalBorder() {

        GridData gd;

        final int columns = _layoutFormat == LAYOUT_1_COLUMNS //
                ? 1
                : _layoutFormat == LAYOUT_2_COLUMNS //
                        ? 2
                        : _layoutFormat == LAYOUT_3_COLUMNS //
                                ? 3
                                : 7;

        gd = new GridData(SWT.FILL, SWT.FILL, false, false);
        gd.heightHint = 3;

        Label label;

        for (int columnIndex = 0; columnIndex < columns; columnIndex++) {
            label = new Label(_innerScContainer, SWT.NONE);
            label.setText(UI.SPACE1);
            label.setLayoutData(gd);
        }

        label = new Label(_innerScContainer, SWT.NONE);
        label.setText(UI.SPACE1);
        label.setLayoutData(gd);
    }

    @Override
    public void dispose() {

        final IWorkbenchPage page = getSite().getPage();

        page.removePostSelectionListener(_postSelectionListener);
        page.removePartListener(_partListener);

        _prefStore.removePropertyChangeListener(_prefChangeListener);

        _colorCache.dispose();

        super.dispose();
    }

    /**
     * @param rgb
     * @return Returns the color from the color cache, the color must not be disposed this is done
     *         when the cache is disposed
     */
    Color getColor(final RGB rgb) {

        final String colorKey = rgb.toString();

        final Color color = _colorCache.get(colorKey);

        if (color == null) {
            return _colorCache.getColor(colorKey, rgb);
        } else {
            return color;
        }
    }

    private void initUI(final Composite parent) {

        _pc = new PixelConverter(parent);

        _bgColorHeader = Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
        _fontBold = JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT);
    }

    private void onResizeUI() {

        if (_chartDataModel == null) {
            return;
        }

        updateInfo(_chartInfo, true);
    }

    private void onSelectionChanged(final ISelection selection) {

        if (_isPartVisible == false) {
            return;
        }

        if (selection instanceof SelectionChartInfo) {

            updateInfo((SelectionChartInfo) selection, false);

        } else if (selection instanceof SelectionChartXSliderPosition) {

            updateInfo(((SelectionChartXSliderPosition) selection));

        } else if (selection instanceof SelectionTourData) {

            final SelectionTourData selectionTourData = (SelectionTourData) selection;

            TourChart tourChart = selectionTourData.getTourChart();

            if (tourChart == null) {
                tourChart = TourManager.getActiveTourChart(selectionTourData.getTourData());
            }

            if (tourChart != null) {
                updateInfo(tourChart.getChartInfo(), false);
            }

        } else if (selection instanceof SelectionTourId) {

            updateInfo();

        } else if (selection instanceof SelectionTourChart) {

            final TourChart tourChart = ((SelectionTourChart) selection).getTourChart();

            if (tourChart != null) {
                updateInfo(tourChart.getChartInfo(), false);
            }

        } else if (selection instanceof SelectionDeletedTours) {

            clearView();
        }
    }

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

    private void showTour() {

        final ISelection selection = getSite().getWorkbenchWindow().getSelectionService().getSelection();
        onSelectionChanged(selection);

        if (_chartInfo == null) {

            //         _pageBook.showPage(_pageNoTour);

            // a tour is not displayed, find a tour provider which provides a tour
            Display.getCurrent().asyncExec(new Runnable() {
                @Override
                public void run() {

                    // validate widget
                    if (_pageBook.isDisposed()) {
                        return;
                    }

                    /*
                     * check if tour was set from a selection provider
                     */
                    if (_chartInfo != null) {
                        return;
                    }

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

                    if (selectedTours != null && selectedTours.size() > 0) {

                        final TourData selectedTour = selectedTours.get(0);
                        final SelectionTourId tourSelection = new SelectionTourId(selectedTour.getTourId());

                        onSelectionChanged(tourSelection);
                    }
                }
            });
        }
    }

    private void updateInfo() {

        /*
         * Run this delayed because the tour chart may not yet contain the data when a new tour is
         * selected.
         */

        _partContainer.getDisplay().asyncExec(new Runnable() {
            @Override
            public void run() {

                final TourChart tourChart = TourManager.getInstance().getActiveTourChart();

                if (tourChart == null || tourChart.isDisposed()) {

                    clearView();
                    return;
                }

                updateInfo(tourChart.getChartInfo(), false);
            }
        });
    }

    private void updateInfo(final SelectionChartInfo chartInfo, final boolean isForceRecreate) {

        //      final long startTime = System.currentTimeMillis();

        if (chartInfo == null) {
            clearView();
            return;
        }

        if (_pageBook.isDisposed()) {
            clearView();
            return;
        }

        _chartInfo = chartInfo;

        // check if the layout needs to be recreated
        boolean isLayoutDirty = false;
        if (_chartDataModel != chartInfo.chartDataModel) {

            // data model changed, another data model can have other visible data
            isLayoutDirty = true;
        }

        // init vars which are used in createLayout()
        _chartDataModel = chartInfo.chartDataModel;
        _chartDrawingData = chartInfo.chartDrawingData;

        if (_chartDrawingData == null) {
            // this happened
            clearView();
            return;
        }

        _graphDrawingData = _chartDrawingData.graphDrawingData;

        if ((_graphDrawingData == null) || (_graphDrawingData.size() == 0) || (_graphDrawingData.get(0) == null)) {
            // this happened
            clearView();
            return;
        }

        final boolean isForceUpdate = isForceRecreate || isLayoutDirty;

        createUI_10_Analyzer(isForceUpdate);

        updateInfo_Values(chartInfo, isForceUpdate);

        // optimize layout
        if (isForceUpdate) {

            // refresh the layout after the data has changed
            _pageAnalyzer.layout();
        }

        //      final long endTime = System.currentTimeMillis();
        //      System.out.println(++_debugCounter + "  " + (endTime - startTime) + " ms");
    }

    /**
     * selection is not from a chart, so it's possible that the chart has not yet the correct slider
     * positon, we create the chart info from the slider position
     */
    private void updateInfo(final SelectionChartXSliderPosition sliderPosition) {

        Chart chart = sliderPosition.getChart();

        if (chart == null) {

            final TourChart tourChart = TourManager.getInstance().getActiveTourChart();
            if (tourChart == null || tourChart.isDisposed()) {

                clearView();

                return;
            }

            chart = tourChart;
        }

        final SelectionChartInfo chartInfo = new SelectionChartInfo(chart);

        chartInfo.chartDataModel = chart.getChartDataModel();
        chartInfo.chartDrawingData = chart.getChartDrawingData();

        chartInfo.leftSliderValuesIndex = sliderPosition.getLeftSliderValueIndex();
        chartInfo.rightSliderValuesIndex = sliderPosition.getRightSliderValueIndex();

        updateInfo(chartInfo, false);
    }

    private void updateInfo_Values(final SelectionChartInfo chartInfo, final boolean isForceUpdate) {

        final ChartDataXSerie xData = _graphDrawingData.get(0).getXData();

        int valuesIndexLeft = chartInfo.leftSliderValuesIndex;
        int valuesIndexRight = chartInfo.rightSliderValuesIndex;

        if (valuesIndexLeft == SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION) {
            valuesIndexLeft = _valueIndexLeftBackup;
        } else {
            _valueIndexLeftBackup = valuesIndexLeft;
        }
        if (valuesIndexRight == SelectionChartXSliderPosition.IGNORE_SLIDER_POSITION) {
            valuesIndexRight = _valueIndexRightBackup;
        } else {
            _valueIndexRightBackup = valuesIndexRight;
        }

        /*
         * Optimize performance, this can propably be ignore because it must be deeply zoomed in to
         * see a performance gain.
         */
        if (isForceUpdate == false && _valueIndexLeftLast == valuesIndexLeft
                && _valueIndexRightLast == valuesIndexRight) {
            return;
        }

        _valueIndexLeftLast = valuesIndexLeft;
        _valueIndexRightLast = valuesIndexRight;

        for (final GraphInfo graphInfo : _graphInfos) {

            final ChartDataSerie serieData = graphInfo.chartData;

            TourChartAnalyzerInfo analyzerInfo = (TourChartAnalyzerInfo) serieData
                    .getCustomData(TourManager.CUSTOM_DATA_ANALYZER_INFO);

            if (analyzerInfo == null) {
                // create default average object
                analyzerInfo = new TourChartAnalyzerInfo();
            }

            final int valueDecimals = analyzerInfo.getAvgDecimals();
            final int valueDivisor2 = (int) Math.pow(10, valueDecimals);

            final int unitType = serieData.getAxisUnit();
            final int valueDivisor = serieData.getValueDivisor();

            double[] values = null;
            if (serieData instanceof ChartDataYSerie) {

                final ChartDataYSerie yData = (ChartDataYSerie) serieData;
                values = yData.getHighValuesDouble()[0];

            } else if (serieData instanceof ChartDataXSerie) {

                final ChartDataXSerie graphXData = (ChartDataXSerie) serieData;
                values = graphXData.getHighValuesDouble()[0];
            }

            if (values == null) {
                continue;
            }

            final int endIndex = values.length - 1;
            if (valuesIndexLeft > endIndex) {
                valuesIndexLeft = endIndex;
            }
            if (valuesIndexRight > endIndex) {
                valuesIndexRight = endIndex;
            }

            valuesIndexLeft = Math.max(0, valuesIndexLeft);
            valuesIndexRight = Math.max(0, valuesIndexRight);

            // values at the left/right slider
            final double leftValue = values[valuesIndexLeft];
            final double rightValue = values[valuesIndexRight];

            int dataIndex = valuesIndexLeft;
            float avg = 0;
            int avgDiv = 0;
            double min = 0;
            double max = 0;

            /*
             * Compute min/max/avg values
             */
            while (dataIndex <= valuesIndexRight) {

                final double value = values[dataIndex];

                avg += value;
                avgDiv++;

                if (dataIndex == valuesIndexLeft) {

                    // this is the first value in the dataseries, set initial value
                    min = value;
                    max = value;

                } else {

                    // optimized for speed

                    min = (value <= min) ? value : min; //Math.min(value, min);
                    max = (value >= max) ? value : max; //Math.max(value, max);
                }

                dataIndex++;
            }

            /*
             * Compute average values
             */
            final ComputeChartValue computeAvg = analyzerInfo.getComputeChartValue();
            if (computeAvg != null) {

                // average is computed by a callback method

                computeAvg.valueIndexLeft = valuesIndexLeft;
                computeAvg.valueIndexRight = valuesIndexRight;
                computeAvg.xData = xData;
                computeAvg.yData = (ChartDataYSerie) serieData;
                computeAvg.chartModel = _chartDataModel;

                avg = computeAvg.compute();

            } else {

                if (avgDiv != 0) {
                    avg = avg / avgDiv;
                }
            }

            /*
             * Set values into the labels, optimize performance by displaying only changed values
             */
            if (graphInfo.leftValue != leftValue) {
                graphInfo.leftValue = leftValue;
                graphInfo.lblLeft
                        .setText(Util.formatNumber(leftValue, unitType, valueDivisor, valueDecimals) + UI.SPACE);
            }

            if (graphInfo.rightValue != rightValue) {
                graphInfo.rightValue = rightValue;
                graphInfo.lblRight
                        .setText(Util.formatNumber(rightValue, unitType, valueDivisor, valueDecimals) + UI.SPACE);
            }

            if (graphInfo.minValue != min) {
                graphInfo.minValue = min;
                graphInfo.lblMin.setText(Util.formatNumber(min, unitType, valueDivisor, valueDecimals) + UI.SPACE);
            }

            if (graphInfo.maxValue != max) {
                graphInfo.maxValue = max;
                graphInfo.lblMax.setText(Util.formatNumber(max, unitType, valueDivisor, valueDecimals) + UI.SPACE);
            }

            // set average value
            if (analyzerInfo.isShowAvg()) {

                float avgValue = avg;

                if (analyzerInfo.isShowAvgDecimals()) {

                    avgValue *= valueDivisor2;

                    if (graphInfo.avgValue != (int) avgValue) {

                        graphInfo.avgValue = (int) avgValue;
                        graphInfo.lblAvg.setText(
                                Util.formatNumber(avgValue, unitType, valueDivisor2, valueDecimals) + UI.SPACE);
                    }

                } else {

                    if (graphInfo.avgValue != (int) avgValue) {

                        graphInfo.avgValue = (int) avgValue;
                        graphInfo.lblAvg
                                .setText(Util.formatValue((int) avgValue, unitType, valueDivisor, true) + UI.SPACE);
                    }
                }

            } else {
                graphInfo.lblAvg.setText(UI.EMPTY_STRING);
            }

            final double diffValue = rightValue - leftValue;
            if (graphInfo.diffValue != diffValue) {
                graphInfo.diffValue = diffValue;
                graphInfo.lblDiff
                        .setText(Util.formatNumber(diffValue, unitType, valueDivisor, valueDecimals) + UI.SPACE);
            }
        }
    }
}