net.tourbook.ui.views.tourMarker.TourMarkerAllView.java Source code

Java tutorial

Introduction

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

Source

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

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import net.tourbook.application.TourbookPlugin;
import net.tourbook.common.UI;
import net.tourbook.common.util.ColumnDefinition;
import net.tourbook.common.util.ColumnManager;
import net.tourbook.common.util.ITourViewer;
import net.tourbook.common.util.PostSelectionProvider;
import net.tourbook.common.util.SQL;
import net.tourbook.common.util.TableColumnDefinition;
import net.tourbook.common.util.Util;
import net.tourbook.data.TourData;
import net.tourbook.data.TourMarker;
import net.tourbook.database.TourDatabase;
import net.tourbook.preferences.ITourbookPreferences;
import net.tourbook.tour.ActionOpenMarkerDialog;
import net.tourbook.tour.ITourEventListener;
import net.tourbook.tour.SelectionTourIds;
import net.tourbook.tour.SelectionTourMarker;
import net.tourbook.tour.TourEvent;
import net.tourbook.tour.TourEventId;
import net.tourbook.tour.TourManager;
import net.tourbook.ui.ITourProvider;
import net.tourbook.ui.Messages;
import net.tourbook.ui.TableColumnFactory;
import net.tourbook.ui.action.ActionEditQuick;
import net.tourbook.ui.action.ActionEditTour;
import net.tourbook.ui.action.ActionModifyColumns;
import net.tourbook.ui.action.ActionOpenTour;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.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.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.part.ViewPart;

/**
 * This view displays all available {@link TourMarker}.
 */
public class TourMarkerAllView extends ViewPart implements ITourProvider, ITourViewer {

    public static final String ID = "net.tourbook.ui.views.TourMarkerAllView"; //$NON-NLS-1$
    //
    private static final String COLUMN_ALTITUDE = "Altitude"; //$NON-NLS-1$
    private static final String COLUMN_DATE = "Date"; //$NON-NLS-1$
    private static final String COLUMN_DESCRIPTION = "Description"; //$NON-NLS-1$
    private static final String COLUMN_LATITUDE = "Latitude"; //$NON-NLS-1$
    private static final String COLUMN_LONGITUDE = "Longitude"; //$NON-NLS-1$
    private static final String COLUMN_MARKER_ID = "MarkerId"; //$NON-NLS-1$
    private static final String COLUMN_NAME = "Name"; //$NON-NLS-1$
    private static final String COLUMN_TOUR_ID = "TourId"; //$NON-NLS-1$
    private static final String COLUMN_TIME = "Time"; //$NON-NLS-1$
    private static final String COLUMN_URL_ADDRESS = "UrlAddress"; //$NON-NLS-1$
    private static final String COLUMN_URL_LABEL = "UrlLabel"; //$NON-NLS-1$
    //
    static final String STATE_GEO_FILTER_AREA = "STATE_GEO_FILTER_AREA"; //$NON-NLS-1$
    private static final String STATE_GPS_FILTER = "STATE_GPS_FILTER"; //$NON-NLS-1$
    static final String STATE_IS_LAT_LON_DIGITS_ENABLED = "STATE_IS_LAT_LON_DIGITS_ENABLED"; //$NON-NLS-1$
    static final String STATE_LAT_LON_DIGITS = "STATE_LAT_LON_DIGITS"; //$NON-NLS-1$
    private static final String STATE_SELECTED_MARKER_ITEM = "STATE_SELECTED_MARKER_ITEM"; //$NON-NLS-1$
    private static final String STATE_SORT_COLUMN_DIRECTION = "STATE_SORT_COLUMN_DIRECTION"; //$NON-NLS-1$
    private static final String STATE_SORT_COLUMN_ID = "STATE_SORT_COLUMN_ID"; //$NON-NLS-1$

    static final double DEFAULT_GEO_FILTER_AREA = 0.05;
    static final boolean DEFAULT_IS_LAT_LON_DIGITS_ENABLED = true;
    static final int DEFAULT_LAT_LON_DIGITS = 5;
    //
    private static int GPS_MARKER_FILTER_IS_DISABLED = 0;
    private static int GPS_MARKER_FILTER_WITH_GPS = 1;
    private static int GPS_MARKER_FILTER_WITHOUT_GPS = 2;
    //
    private final IPreferenceStore _prefStore = TourbookPlugin.getPrefStore();
    private final IDialogSettings _state = TourbookPlugin.getState(ID);
    //
    private PostSelectionProvider _postSelectionProvider;
    //
    private IPartListener2 _partListener;
    private IPropertyChangeListener _prefChangeListener;
    private ITourEventListener _tourEventListener;
    //
    private ActionEditTour _actionEditTour;
    private ActionModifyColumns _actionModifyColumns;
    private ActionOpenMarkerDialog _actionOpenMarkerDialog;
    private ActionOpenTour _actionOpenTour;
    private ActionEditQuick _actionQuickEdit;
    private ActionTourMarkerFilter _actionTourMarkerFilter;
    private ActionMarkerFilterWithGPS _actionTourFilterWithGPS;
    private ActionMarkerFilterWithNoGPS _actionTourFilterWithoutGPS;
    //
    private CheckboxTableViewer _markerViewer;
    private MarkerComparator _markerComparator = new MarkerComparator();
    private ColumnManager _columnManager;
    private SelectionAdapter _columnSortListener;

    private ArrayList<TourMarkerItem> _allMarkerItems = new ArrayList<TourMarkerItem>();

    private int _gpsMarkerFilter = GPS_MARKER_FILTER_IS_DISABLED;

    /**
     * Number of digits for the lat/lon columns.
     */
    private int _latLonDigits;
    private boolean _isLatLonDigitsEnabled;

    /**
     * Is <code>true</code> when markers are filtered with the geo filter.
     */
    private boolean _isGeoFilterActive;
    private double _geoFilterLat;
    private double _geoFilterLon;
    private double _geoFilterMaxDiff;

    private boolean _isInUpdate;

    private final DateFormat _dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT);
    private final DateFormat _timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT);
    //
    private final NumberFormat _nf1 = NumberFormat.getNumberInstance();
    private final NumberFormat _nf3 = NumberFormat.getNumberInstance();
    private final NumberFormat _nfLatLon = NumberFormat.getNumberInstance();
    {
        _nf1.setMinimumFractionDigits(1);
        _nf1.setMaximumFractionDigits(1);
        _nf3.setMinimumFractionDigits(3);
        _nf3.setMaximumFractionDigits(3);
    }

    /*
     * UI controls
     */
    private PixelConverter _pc;
    private Composite _uiParent;
    private Composite _viewerContainer;
    private TourMarkerItem _markerFilter;

    private class MarkerComparator extends ViewerComparator {

        private static final int ASCENDING = 0;
        private static final int DESCENDING = 1;

        private String __sortColumnId = COLUMN_TOUR_ID;
        private int __sortDirection = ASCENDING;

        @Override
        public int compare(final Viewer viewer, final Object e1, final Object e2) {

            final TourMarkerItem m1 = (TourMarkerItem) e1;
            final TourMarkerItem m2 = (TourMarkerItem) e2;

            boolean _isSortByTime = false;
            double rc = 0;

            // Determine which column and do the appropriate sort
            switch (__sortColumnId) {
            case COLUMN_ALTITUDE:
                rc = m1.altitude - m2.altitude;
                break;

            case COLUMN_DESCRIPTION:
                rc = m1.description.compareTo(m2.description);
                break;

            case COLUMN_LATITUDE:
                rc = m1.latitude - m2.latitude;
                if (rc == 0) {
                    rc = m1.longitude - m2.longitude;
                }
                _isSortByTime = true;
                break;

            case COLUMN_LONGITUDE:
                rc = m1.longitude - m2.longitude;
                if (rc == 0) {
                    rc = m1.latitude - m2.latitude;
                }
                _isSortByTime = true;
                break;

            case COLUMN_MARKER_ID:
                rc = m1.markerId - m2.markerId;
                break;

            case COLUMN_TOUR_ID:
                rc = m1.tourId - m2.tourId;
                break;

            case COLUMN_DATE:
            case COLUMN_TIME:
                rc = m1.time - m2.time;
                break;

            case COLUMN_URL_ADDRESS:
                rc = m1.urlAddress.compareTo(m2.urlAddress);
                break;

            case COLUMN_URL_LABEL:
                rc = m1.urlLabel.compareTo(m2.urlLabel);
                break;

            case COLUMN_NAME:
            default:
                rc = m1.label.compareTo(m2.label);
                _isSortByTime = true;
            }

            if (rc == 0 && _isSortByTime) {
                rc = m1.time - m2.time;
            }

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

            /*
             * MUST return 1 or -1 otherwise long values are not sorted correctly.
             */
            return rc > 0 //
                    ? 1
                    : rc < 0 //
                            ? -1
                            : 0;
        }

        public void setSortColumn(final Widget widget) {

            final ColumnDefinition columnDefinition = (ColumnDefinition) widget.getData();
            final String columnId = columnDefinition.getColumnId();

            if (columnId.equals(__sortColumnId)) {

                // Same column as last sort; toggle the direction

                __sortDirection = 1 - __sortDirection;

            } else {

                // New column; do an ascent sorting

                __sortColumnId = columnId;
                __sortDirection = ASCENDING;
            }

            updateUI_SetSortDirection(__sortColumnId, __sortDirection);
        }
    }

    private class MarkerContentProvider implements IStructuredContentProvider {

        @Override
        public void dispose() {
        }

        @Override
        public Object[] getElements(final Object inputElement) {
            return _allMarkerItems.toArray();
        }

        @Override
        public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
        }
    }

    public class MarkerItemFilter extends ViewerFilter {

        @Override
        public boolean select(final Viewer viewer, final Object parentElement, final Object element) {

            final TourMarkerItem markerItem = (TourMarkerItem) element;

            final boolean isTourWithGPS = markerItem.latitude != TourDatabase.DEFAULT_DOUBLE;
            final boolean isTourWithoutGPS = markerItem.latitude == TourDatabase.DEFAULT_DOUBLE;

            boolean isVisible;

            /*
             * Check GPS filter
             */
            if (_gpsMarkerFilter == GPS_MARKER_FILTER_IS_DISABLED) {

                isVisible = true;

            } else {

                isVisible = _gpsMarkerFilter == GPS_MARKER_FILTER_WITH_GPS ? isTourWithGPS : isTourWithoutGPS;
            }

            if (isVisible == false) {
                return false;
            }

            /*
             * Check geo filter
             */
            if (_isGeoFilterActive == false) {
                return true;
            }

            if (isTourWithoutGPS) {
                return false;
            }

            // Lat filter
            final double lat = markerItem.latitude;
            final double latDiff = _geoFilterLat - lat;
            if (latDiff > 0) {

                if (latDiff > _geoFilterMaxDiff) {
                    return false;
                }

            } else {

                if (-latDiff > _geoFilterMaxDiff) {
                    return false;
                }
            }

            // Lon filter
            final double lon = markerItem.longitude;
            final double lonDiff = _geoFilterLon - lon;
            if (lonDiff > 0) {

                if (lonDiff > _geoFilterMaxDiff) {
                    return false;
                }
            } else {

                if (-lonDiff > _geoFilterMaxDiff) {
                    return false;
                }
            }

            return true;
        }
    }

    class TourMarkerItem {

        public long markerId;

        public long tourId;
        public String label;
        public String description;
        public String urlLabel;
        public String urlAddress;
        public double latitude;
        public double longitude;
        public float altitude;
        public long time;

        @Override
        public boolean equals(final Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (!(obj instanceof TourMarkerItem)) {
                return false;
            }
            final TourMarkerItem other = (TourMarkerItem) obj;
            if (!getOuterType().equals(other.getOuterType())) {
                return false;
            }
            if (markerId != other.markerId) {
                return false;
            }
            return true;
        }

        private TourMarkerAllView getOuterType() {
            return TourMarkerAllView.this;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + getOuterType().hashCode();
            result = prime * result + (int) (markerId ^ (markerId >>> 32));
            return result;
        }

        @Override
        public String toString() {
            return "TourMarkerItem [_label=" + label + "]"; //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    public TourMarkerAllView() {
        super();
    }

    void actionMarkerFilterGPS(final Action actionTourFilter) {

        // toggle with/without GPS actions

        if (actionTourFilter == _actionTourFilterWithGPS) {

            // action with GPS is hit

            if (_gpsMarkerFilter != GPS_MARKER_FILTER_WITH_GPS) {

                _gpsMarkerFilter = GPS_MARKER_FILTER_WITH_GPS;

            } else {

                _gpsMarkerFilter = GPS_MARKER_FILTER_IS_DISABLED;
            }

            _actionTourFilterWithoutGPS.setChecked(false);

        } else {

            // action without GPS is hit

            if (_gpsMarkerFilter != GPS_MARKER_FILTER_WITHOUT_GPS) {

                _gpsMarkerFilter = GPS_MARKER_FILTER_WITHOUT_GPS;

            } else {

                _gpsMarkerFilter = GPS_MARKER_FILTER_IS_DISABLED;
            }

            _actionTourFilterWithGPS.setChecked(false);
        }

        refilterViewer();

        _markerViewer.getTable().setFocus();
    }

    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) {
                if (partRef.getPart(false) == TourMarkerAllView.this) {
                    saveState();
                }
            }

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

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

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

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

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

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

    private void addPrefListener() {

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

                final String property = event.getProperty();

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

                    // measurement system has changed

                    _columnManager.saveState(_state);
                    _columnManager.clearColumns();

                    defineAllColumns();

                    _markerViewer = (CheckboxTableViewer) recreateViewer(_markerViewer);

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

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

                    _markerViewer.refresh();

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

                }
            }
        };
        _prefStore.addPropertyChangeListener(_prefChangeListener);
    }

    private void addTourEventListener() {

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

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

                if (_isInUpdate) {
                    return;
                }

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

                    final ArrayList<TourData> modifiedTours = ((TourEvent) eventData).getModifiedTours();
                    if (modifiedTours != null) {

                        // update modified tour

                        reloadViewer();
                    }

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

                    onTourEvent_TourMarker(eventData);

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

                    reloadViewer();
                }
            }
        };

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

    private void createActions() {

        _actionEditTour = new ActionEditTour(this);
        _actionModifyColumns = new ActionModifyColumns(this);
        _actionOpenMarkerDialog = new ActionOpenMarkerDialog(this, true);
        _actionOpenTour = new ActionOpenTour(this);
        _actionQuickEdit = new ActionEditQuick(this);
        _actionTourFilterWithGPS = new ActionMarkerFilterWithGPS(this);
        _actionTourFilterWithoutGPS = new ActionMarkerFilterWithNoGPS(this);
        _actionTourMarkerFilter = new ActionTourMarkerFilter(this, _uiParent);
    }

    @Override
    public void createPartControl(final Composite parent) {

        _uiParent = parent;

        initUI(parent);

        restoreState_BeforeUI();

        // define all columns for the viewer
        _columnManager = new ColumnManager(this, _state);
        defineAllColumns();

        createUI(parent);

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

        // set selection provider
        getSite().setSelectionProvider(_postSelectionProvider = new PostSelectionProvider(ID));

        createActions();
        fillToolbar();

        // load marker and display them
        //      parent.getDisplay().asyncExec(new Runnable() {
        //         public void run() {
        //
        //         }
        //      });
        BusyIndicator.showWhile(parent.getDisplay(), new Runnable() {
            @Override
            public void run() {

                loadAllMarker();

                updateUI_SetViewerInput();

                restoreState_WithUI();
            }
        });
    }

    private void createUI(final Composite parent) {

        _viewerContainer = new Composite(parent, SWT.NONE);
        GridLayoutFactory.fillDefaults().applyTo(_viewerContainer);
        {
            createUI_10_MarkerViewer(_viewerContainer);
        }
    }

    private void createUI_10_MarkerViewer(final Composite parent) {

        /*
         * create table
         */
        final Table table = new Table(parent, SWT.FULL_SELECTION | SWT.MULTI | SWT.CHECK);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(table);

        table.setHeaderVisible(true);
        table.setLinesVisible(_prefStore.getBoolean(ITourbookPreferences.VIEW_LAYOUT_DISPLAY_LINES));

        /*
         * It took a while that the correct listener is set and also the checked item is fired and
         * not the wrong selection.
         */
        table.addListener(SWT.Selection, new Listener() {

            @Override
            public void handleEvent(final Event event) {
                onSelect_TourMarker(event);
            }
        });
        ;

        /*
         * create table viewer
         */
        _markerViewer = new CheckboxTableViewer(table);

        _columnManager.createColumns(_markerViewer);

        _markerViewer.setUseHashlookup(true);
        _markerViewer.setContentProvider(new MarkerContentProvider());
        _markerViewer.addFilter(new MarkerItemFilter());
        _markerViewer.setComparator(_markerComparator);

        _markerViewer.addDoubleClickListener(new IDoubleClickListener() {
            @Override
            public void doubleClick(final DoubleClickEvent event) {
                onTourMarker_DoubleClick();
            }
        });

        _markerViewer.addCheckStateListener(new ICheckStateListener() {
            @Override
            public void checkStateChanged(final CheckStateChangedEvent event) {
                onTourMarker_StateChanged(event);
            }
        });

        updateUI_SetSortDirection(//
                _markerComparator.__sortColumnId, _markerComparator.__sortDirection);

        createUI_20_ContextMenu();
    }

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

        final MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            @Override
            public void menuAboutToShow(final IMenuManager manager) {
                fillContextMenu(manager);
            }
        });

        final Table table = (Table) _markerViewer.getControl();
        final Menu tableContextMenu = menuMgr.createContextMenu(table);

        _columnManager.createHeaderContextMenu(table, tableContextMenu);
    }

    private void defineAllColumns() {

        defineColumn_Name();

        defineColumn_Date();
        defineColumn_Time();

        defineColumn_Altitude();
        defineColumn_Latitude();
        defineColumn_Longitude();

        defineColumn_Description();
        defineColumn_UrlLabel();
        defineColumn_UrlAddress();

        defineColumn_MarkerId();
        defineColumn_TourId();
    }

    /**
     * Column: Altitude
     */
    private void defineColumn_Altitude() {

        final ColumnDefinition colDef = TableColumnFactory.ALTITUDE_ALTITUDE.createColumn(_columnManager, _pc);

        // overwrite column id to identify the column when table is sorted
        colDef.setColumnId(COLUMN_ALTITUDE);
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                String valueText;
                final float altitude = ((TourMarkerItem) cell.getElement()).altitude;

                if (altitude == TourDatabase.DEFAULT_FLOAT) {
                    valueText = UI.EMPTY_STRING;
                } else {
                    valueText = _nf1.format(altitude / net.tourbook.ui.UI.UNIT_VALUE_ALTITUDE);
                }

                cell.setText(valueText);
            }
        });
    }

    /**
     * Column: Date
     */
    private void defineColumn_Date() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_DATE, SWT.TRAIL);

        colDef.setColumnName(Messages.ColumnFactory_Waypoint_Date);

        colDef.setIsDefaultColumn();
        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(12));
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourMarkerItem tourMarkerItem = (TourMarkerItem) cell.getElement();

                cell.setText(_dateFormatter.format(tourMarkerItem.time));
            }
        });

    }

    /**
     * Column: Description
     */
    private void defineColumn_Description() {

        final ColumnDefinition colDef = TableColumnFactory.WAYPOINT_DESCRIPTION.createColumn(_columnManager, _pc);

        colDef.setIsDefaultColumn();

        // overwrite column id to identify the column when table is sorted
        colDef.setColumnId(COLUMN_DESCRIPTION);
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourMarkerItem marker = (TourMarkerItem) cell.getElement();
                cell.setText(marker.description);
            }
        });
    }

    /**
     * Column: Latitude
     */
    private void defineColumn_Latitude() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_LATITUDE, SWT.TRAIL);

        colDef.setColumnName(Messages.ColumnFactory_latitude);
        colDef.setIsDefaultColumn();

        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(11));
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                String valueText;
                final double latitude = ((TourMarkerItem) cell.getElement()).latitude;

                if (latitude == TourDatabase.DEFAULT_DOUBLE) {
                    valueText = UI.EMPTY_STRING;
                } else {
                    if (_isLatLonDigitsEnabled) {
                        valueText = _nfLatLon.format(latitude);
                    } else {
                        valueText = Double.toString(latitude);
                    }
                }

                cell.setText(valueText);
            }
        });
    }

    /**
     * Column: Longitude
     */
    private void defineColumn_Longitude() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_LONGITUDE, SWT.TRAIL);

        colDef.setColumnName(Messages.ColumnFactory_longitude);
        colDef.setIsDefaultColumn();

        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(11));
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                String valueText;
                final double longitude = ((TourMarkerItem) cell.getElement()).longitude;

                if (longitude == TourDatabase.DEFAULT_DOUBLE) {
                    valueText = UI.EMPTY_STRING;
                } else {
                    if (_isLatLonDigitsEnabled) {
                        valueText = _nfLatLon.format(longitude);
                    } else {
                        valueText = Double.toString(longitude);
                    }
                }

                cell.setText(valueText);
            }
        });
    }

    /**
     * Column: Marker ID
     */
    private void defineColumn_MarkerId() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_MARKER_ID, SWT.LEAD);

        colDef.setColumnName(Messages.ColumnFactory_TourMarkerId);

        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(12));
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                cell.setText(Long.toString(((TourMarkerItem) cell.getElement()).markerId));
            }
        });
    }

    /**
     * Column: Name
     */
    private void defineColumn_Name() {

        final ColumnDefinition colDef = TableColumnFactory.WAYPOINT_NAME.createColumn(_columnManager, _pc);

        colDef.setIsDefaultColumn();

        // overwrite column id to identify the column when table is sorted
        colDef.setColumnId(COLUMN_NAME);
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourMarkerItem marker = (TourMarkerItem) cell.getElement();
                cell.setText(marker.label);
            }
        });
    }

    /**
     * Column: Time
     */
    private void defineColumn_Time() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_TIME, SWT.TRAIL);

        colDef.setColumnName(Messages.ColumnFactory_tour_time_label_hhmmss);

        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(12));

        /*
         * a marker cannot be sorted by time only by date
         */
        //      colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {

                final TourMarkerItem tourMarkerItem = (TourMarkerItem) cell.getElement();

                cell.setText(_timeFormatter.format(tourMarkerItem.time));
            }
        });

    }

    /**
     * Column: TourID
     */
    private void defineColumn_TourId() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_TOUR_ID, SWT.LEAD);

        colDef.setColumnName(Messages.ColumnFactory_TourId);

        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(22));
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                cell.setText(Long.toString(((TourMarkerItem) cell.getElement()).tourId));
            }
        });

    }

    /**
     * Column: Url address
     */
    private void defineColumn_UrlAddress() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_URL_ADDRESS, SWT.LEAD);

        colDef.setColumnName(Messages.ColumnFactory_InetAddress_Url);

        colDef.setIsDefaultColumn();
        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(20));
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                final TourMarkerItem markerItem = (TourMarkerItem) cell.getElement();
                cell.setText(markerItem.urlAddress);
            }
        });
    }

    /**
     * Column: Url label
     */
    private void defineColumn_UrlLabel() {

        final ColumnDefinition colDef = new TableColumnDefinition(_columnManager, COLUMN_URL_LABEL, SWT.LEAD);

        colDef.setColumnName(Messages.ColumnFactory_InetAddress_Label);

        colDef.setIsDefaultColumn();
        colDef.setDefaultColumnWidth(_pc.convertWidthInCharsToPixels(20));
        colDef.setColumnSelectionListener(_columnSortListener);

        colDef.setLabelProvider(new CellLabelProvider() {
            @Override
            public void update(final ViewerCell cell) {
                final TourMarkerItem markerItem = (TourMarkerItem) cell.getElement();
                cell.setText(markerItem.urlLabel);
            }
        });
    }

    @Override
    public void dispose() {

        TourManager.getInstance().removeTourEventListener(_tourEventListener);

        getViewSite().getPage().removePartListener(_partListener);

        _prefStore.removePropertyChangeListener(_prefChangeListener);

        super.dispose();
    }

    private void enableActions() {

        final TourMarker tourMarker = getSelectedTourMarker();
        final boolean isMarkerSelected = tourMarker != null;
        final boolean isGeoFilterDeactivated = _isGeoFilterActive == false;

        _actionEditTour.setEnabled(isMarkerSelected);
        _actionOpenMarkerDialog.setEnabled(isMarkerSelected);
        _actionOpenTour.setEnabled(isMarkerSelected);
        _actionQuickEdit.setEnabled(isMarkerSelected);

        _actionTourFilterWithGPS.setEnabled(isGeoFilterDeactivated);
        _actionTourFilterWithoutGPS.setEnabled(isGeoFilterDeactivated);
    }

    private void fillContextMenu(final IMenuManager menuMgr) {

        final TourMarker tourMarker = getSelectedTourMarker();

        /*
         * update actions
         */
        _actionOpenMarkerDialog.setTourMarker(tourMarker);

        /*
         * fill menu
         */
        menuMgr.add(_actionOpenMarkerDialog);

        menuMgr.add(new Separator());
        menuMgr.add(_actionQuickEdit);
        menuMgr.add(_actionEditTour);
        menuMgr.add(_actionOpenTour);

        enableActions();
    }

    private void fillToolbar() {

        final IActionBars actionBars = getViewSite().getActionBars();

        /*
         * Fill view menu
         */
        final IMenuManager menuMgr = actionBars.getMenuManager();

        menuMgr.add(_actionModifyColumns);

        /*
         * Fill view toolbar
         */
        final IToolBarManager tbm = actionBars.getToolBarManager();

        tbm.add(_actionTourFilterWithGPS);
        tbm.add(_actionTourFilterWithoutGPS);
        tbm.add(_actionTourMarkerFilter);
    }

    private void fireSelection(final StructuredSelection selection) {

        // get unique tour id's
        final HashSet<Long> tourIds = new HashSet<Long>();
        for (final Iterator<?> selectedItem = selection.iterator(); selectedItem.hasNext();) {
            tourIds.add(((TourMarkerItem) selectedItem.next()).tourId);
        }

        final SelectionTourIds selectionTourIds = new SelectionTourIds(new ArrayList<>(tourIds));

        final int uniqueTourIds = tourIds.size();

        if (uniqueTourIds == 1) {

            // only one unique tour is selected, fire a marker selection

            final TourMarkerItem firstMarkerItem = (TourMarkerItem) selection.getFirstElement();

            // get tour by id
            final TourData tourData = TourManager.getInstance().getTourData(firstMarkerItem.tourId);
            if (tourData == null) {
                return;
            }

            final ArrayList<TourMarker> selectedTourMarkers = new ArrayList<TourMarker>();

            for (final Iterator<?> iterator = selection.iterator(); iterator.hasNext();) {

                final long selectedMarkerId = ((TourMarkerItem) iterator.next()).markerId;

                // get marker by id
                for (final TourMarker tourMarker : tourData.getTourMarkers()) {
                    if (tourMarker.getMarkerId() == selectedMarkerId) {
                        selectedTourMarkers.add(tourMarker);
                        break;
                    }
                }
            }

            _isInUpdate = true;
            {
                /*
                 * Set selection in the selection provider that the part selection provider is in
                 * sync with the marker selection, otherwise a selection listener, e.g. tour editor
                 * can display the wrong tour. This happened !!!
                 */
                _postSelectionProvider.setSelectionNoFireEvent(selectionTourIds);

                TourManager.fireEventWithCustomData(//
                        TourEventId.MARKER_SELECTION, new SelectionTourMarker(tourData, selectedTourMarkers),
                        getSite().getPart());
            }
            _isInUpdate = false;

        } else if (uniqueTourIds > 1) {

            // multiple tours are selected, fire a part selection

            _isInUpdate = true;
            {
                _postSelectionProvider.setSelection(selectionTourIds);
            }
            _isInUpdate = false;
        }
    }

    @Override
    public ColumnManager getColumnManager() {
        return _columnManager;
    }

    /**
     * @return Returns the tour marker when the marker filter is active otherwise it returns
     *         <code>null</code>.
     */
    TourMarkerItem getFilterMarker() {
        return _markerFilter;
    }

    public Object getMarkerViewer() {
        return _markerViewer;
    }

    private TourMarker getSelectedTourMarker() {

        final TourMarkerItem tourMarkerItem = getSelectedTourMarkerItem();
        if (tourMarkerItem == null) {
            return null;
        }

        // get tour by id
        final TourData tourData = TourManager.getInstance().getTourData(tourMarkerItem.tourId);
        if (tourData == null) {
            return null;
        }

        final long markerId = tourMarkerItem.markerId;

        // get marker by id
        for (final TourMarker tourMarker : tourData.getTourMarkers()) {
            if (tourMarker.getMarkerId() == markerId) {
                return tourMarker;
            }
        }

        return null;
    }

    private TourMarkerItem getSelectedTourMarkerItem() {

        final StructuredSelection selection = getViewerSelection();

        if (selection.getFirstElement() instanceof TourMarkerItem) {
            return (TourMarkerItem) selection.getFirstElement();
        }

        return null;
    }

    @Override
    public ArrayList<TourData> getSelectedTours() {

        final ArrayList<TourData> selectedTours = new ArrayList<TourData>();

        final StructuredSelection selection = getViewerSelection();

        for (final Iterator<?> iterator = selection.iterator(); iterator.hasNext();) {

            final Object element = iterator.next();

            if (element instanceof TourMarkerItem) {

                // get TourData from the tour marker item

                final TourMarkerItem tourMarkerItem = (TourMarkerItem) element;

                // get tour by id
                final TourData tourData = TourManager.getInstance().getTourData(tourMarkerItem.tourId);

                if (tourData != null) {
                    selectedTours.add(tourData);
                }
            }
        }

        return selectedTours;
    }

    /**
     * @param sortColumnId
     * @return Returns the column widget by it's column id, when column id is not found then the
     *         first column is returned.
     */
    private TableColumn getSortColumn(final String sortColumnId) {

        final TableColumn[] allColumns = _markerViewer.getTable().getColumns();

        for (final TableColumn column : allColumns) {

            final String columnId = ((ColumnDefinition) column.getData()).getColumnId();

            if (columnId.equals(sortColumnId)) {
                return column;
            }
        }

        return allColumns[0];
    }

    @Override
    public ColumnViewer getViewer() {
        return _markerViewer;
    }

    private StructuredSelection getViewerSelection() {

        return (StructuredSelection) _markerViewer.getSelection();
    }

    private void initUI(final Composite parent) {

        _pc = new PixelConverter(parent);

        _columnSortListener = new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                onSelect_SortColumn(e);
            }
        };
    }

    boolean isGeoFilterActive() {
        return _isGeoFilterActive;
    }

    boolean isLatLonDigits() {
        return _isLatLonDigitsEnabled;
    }

    private void loadAllMarker() {

        //      final long start = System.nanoTime();

        _allMarkerItems.clear();

        //      final double latLonFactor = Math.pow(10, _latLonDigits);

        Connection conn = null;
        PreparedStatement statement = null;
        ResultSet result = null;

        try {

            conn = TourDatabase.getInstance().getConnection();

            final String tblTourMarker = TourDatabase.TABLE_TOUR_MARKER;
            final String tourKey = TourDatabase.KEY_TOUR;

            final String sql = "SELECT " // //$NON-NLS-1$
                    + "MarkerID, " //                  // 1 //$NON-NLS-1$
                    + (tourKey + ", ") //               // 2 //$NON-NLS-1$
                    + "Label, " //                     // 3 //$NON-NLS-1$
                    + "description, " //               // 4 //$NON-NLS-1$
                    + "urlText, " //                  // 5 //$NON-NLS-1$
                    + "urlAddress, " //                  // 6 //$NON-NLS-1$
                    + "latitude, " //                  // 7 //$NON-NLS-1$
                    + "longitude, " //                  // 8 //$NON-NLS-1$
                    + "altitude, " //                  // 9 //$NON-NLS-1$
                    + "tourTime " //                  // 10 //$NON-NLS-1$
                    //
                    + UI.NEW_LINE
                    //
                    + (" FROM " + tblTourMarker + UI.NEW_LINE) //$NON-NLS-1$
            //
            ;

            statement = conn.prepareStatement(sql);
            result = statement.executeQuery();

            while (result.next()) {

                final TourMarkerItem markerItem = new TourMarkerItem();
                _allMarkerItems.add(markerItem);

                final String dbLabel = result.getString(3);
                final String dbDescription = result.getString(4);
                final String dbUrlLabel = result.getString(5);
                final String dbUrlAddress = result.getString(6);

                /*
                 * Reduce accuracy that markers sorted by lat/lon are near together
                 */
                final double dbLatitude = result.getDouble(7);
                final double dbLongitude = result.getDouble(8);
                if (dbLatitude == TourDatabase.DEFAULT_DOUBLE) {

                    markerItem.latitude = dbLatitude;
                    markerItem.longitude = dbLongitude;

                } else {

                    //               if (_isLatLonDigitsEnabled) {
                    //
                    //                  // set lat/lon accuracy
                    //
                    //                  final long longLat = (long) (dbLatitude * latLonFactor);
                    //                  final long longLon = (long) (dbLongitude * latLonFactor);
                    //
                    //                  markerItem.latitude = longLat / latLonFactor;
                    //                  markerItem.longitude = longLon / latLonFactor;
                    //
                    //               } else {
                    //
                    markerItem.latitude = dbLatitude;
                    markerItem.longitude = dbLongitude;
                    //
                    //               }
                }

                markerItem.markerId = result.getLong(1);
                markerItem.tourId = result.getLong(2);
                markerItem.label = dbLabel == null ? UI.EMPTY_STRING : dbLabel;
                markerItem.description = dbDescription == null ? UI.EMPTY_STRING : dbDescription;
                markerItem.urlLabel = dbUrlLabel == null ? UI.EMPTY_STRING : dbUrlLabel;
                markerItem.urlAddress = dbUrlAddress == null ? UI.EMPTY_STRING : dbUrlAddress;
                markerItem.altitude = result.getFloat(9);
                markerItem.time = result.getLong(10);
            }

        } catch (final SQLException e) {
            SQL.showException(e);
        } finally {
            Util.closeSql(conn);
            Util.closeSql(statement);
            Util.closeSql(result);
        }

        //      System.out.println((UI.timeStampNano() + " " + this.getClass().getName() + " \t")
        //            + (((float) (System.nanoTime() - start) / 1000000) + " ms"));
        //      // TODO remove SYSTEM.OUT.PRINTLN
    }

    private void onSelect_SortColumn(final SelectionEvent e) {

        _viewerContainer.setRedraw(false);
        {
            // keep selection
            final ISelection selectionBackup = getViewerSelection();
            {
                // update viewer with new sorting
                _markerComparator.setSortColumn(e.widget);
                _markerViewer.refresh();
            }
            updateUI_SelectTourMarker(selectionBackup, null);
        }
        _viewerContainer.setRedraw(true);
    }

    private void onSelect_TourMarker(final Event event) {

        if (_isInUpdate) {
            return;
        }

        StructuredSelection selection;

        if (event.detail == SWT.CHECK) {

            // checkbox is hit

            /**
             * !!! VERY important !!!
             * <p>
             * Table.getSelection() returns the wrong selection when the user clicked in a checkbox.
             */

            final TableItem item = (TableItem) event.item;
            final Object selectedMarker = item.getData();

            selection = new StructuredSelection(selectedMarker);

        } else {

            // checkbox is not hit

            selection = (StructuredSelection) _markerViewer.getSelection();
        }

        fireSelection(selection);
    }

    private void onTourEvent_TourMarker(final Object eventData) {

        if (eventData instanceof SelectionTourMarker) {

            // select the tour marker item in the view

            final SelectionTourMarker selection = (SelectionTourMarker) eventData;
            final ArrayList<TourMarker> allTourMarker = selection.getSelectedTourMarker();

            final long selectedTourMarkerId = allTourMarker.get(0).getMarkerId();

            // find tour marker in marker items
            TourMarkerItem selectedMarkerItem = null;
            for (final TourMarkerItem tourMarker : _allMarkerItems) {
                if (tourMarker.markerId == selectedTourMarkerId) {
                    selectedMarkerItem = tourMarker;
                    break;
                }
            }

            if (selectedMarkerItem != null) {

                final StructuredSelection markerViewerSelection = new StructuredSelection(selectedMarkerItem);

                updateUI_SelectTourMarker(markerViewerSelection, null);
            }
        }
    }

    private void onTourMarker_DoubleClick() {

        final TourMarker tourMarker = getSelectedTourMarker();

        if (tourMarker == null) {
            return;
        }

        _actionOpenMarkerDialog.setTourMarker(tourMarker);
        _actionOpenMarkerDialog.run();
    }

    private void onTourMarker_StateChanged(final CheckStateChangedEvent event) {

        final boolean isChecked = event.getChecked();
        final Object checkedElement = event.getElement();
        final Object[] checkedElements = _markerViewer.getCheckedElements();

        final TourMarkerItem markerItem = (TourMarkerItem) checkedElement;
        final boolean isMarkerWithGPS = markerItem.latitude != TourDatabase.DEFAULT_DOUBLE;

        Object checkedMarker = null;

        _isGeoFilterActive = false;
        _markerFilter = null;

        if (isMarkerWithGPS) {

            if (isChecked) {

                _isGeoFilterActive = true;
                _markerFilter = markerItem;

                /*
                 * Ensure that the checked marker is also selected. It is possible that a selected
                 * and checked marker gets unchecked and another marker is checked that it is not
                 * selected.
                 */

                boolean isCheckAndSelected = false;

                final TableItem[] seletedItems = _markerViewer.getTable().getSelection();
                for (final TableItem tableItem : seletedItems) {
                    if (tableItem.getData() == checkedElement) {
                        isCheckAndSelected = true;
                        break;
                    }
                }

                if (isCheckAndSelected == false) {

                    updateUI_SelectTourMarker(new StructuredSelection(checkedElement), null);
                }
            }

        } else {

            // prevent to check tour marker without GPS

            _markerViewer.setGrayed(checkedElement, true);

            checkedMarker = checkedElement;
        }

        if (isChecked && checkedElements.length > 1) {

            // only 1 marker can be checked

            checkedMarker = checkedElement;
        }

        if (checkedMarker != null) {

            for (final Object element : checkedElements) {
                if (element != checkedMarker) {
                    _markerViewer.setChecked(element, false);
                }
            }

            updateUI_SelectTourMarker(new StructuredSelection(checkedMarker), null);

        }

        if (isMarkerWithGPS) {
            updateUI_GeoFilter(markerItem);
        }

        enableActions();
    }

    @Override
    public ColumnViewer recreateViewer(final ColumnViewer columnViewer) {

        _viewerContainer.setRedraw(false);
        {
            // keep selection
            final ISelection selectionBackup = getViewerSelection();
            final Object[] checkedElements = _markerViewer.getCheckedElements();
            {
                _markerViewer.getTable().dispose();

                createUI_10_MarkerViewer(_viewerContainer);

                // update UI
                _viewerContainer.layout();

                // update the viewer
                updateUI_SetViewerInput();
            }
            updateUI_SelectTourMarker(selectionBackup, checkedElements);
        }
        _viewerContainer.setRedraw(true);

        _markerViewer.getTable().setFocus();

        return _markerViewer;
    }

    private void refilterViewer() {

        BusyIndicator.showWhile(_viewerContainer.getDisplay(), new Runnable() {
            @Override
            public void run() {

                _viewerContainer.setRedraw(false);
                {
                    // keep selection
                    final ISelection selectionBackup = getViewerSelection();
                    final Object[] checkedElements = _markerViewer.getCheckedElements();
                    {
                        _markerViewer.refresh(false);
                    }
                    updateUI_SelectTourMarker(selectionBackup, checkedElements);
                }
                _viewerContainer.setRedraw(true);
            }
        });
    }

    @Override
    public void reloadViewer() {

        loadAllMarker();

        _viewerContainer.setRedraw(false);
        {
            // keep selection
            final ISelection selectionBackup = getViewerSelection();
            final Object[] checkedElements = _markerViewer.getCheckedElements();
            {
                updateUI_SetViewerInput();
            }
            updateUI_SelectTourMarker(selectionBackup, checkedElements);
        }
        _viewerContainer.setRedraw(true);
    }

    private void restoreState_BeforeUI() {

        // GPS tour filter
        _gpsMarkerFilter = Util.getStateInt(_state, STATE_GPS_FILTER, GPS_MARKER_FILTER_IS_DISABLED);

        // lat/lon digits
        _isLatLonDigitsEnabled = Util.getStateBoolean(_state, TourMarkerAllView.STATE_IS_LAT_LON_DIGITS_ENABLED,
                TourMarkerAllView.DEFAULT_IS_LAT_LON_DIGITS_ENABLED);

        _latLonDigits = Util.getStateInt(_state, TourMarkerAllView.STATE_LAT_LON_DIGITS,
                TourMarkerAllView.DEFAULT_LAT_LON_DIGITS);
        setup_LatLonDigits();

        // geo filter accuracy
        _geoFilterMaxDiff = Util.getStateDouble(_state, STATE_GEO_FILTER_AREA, DEFAULT_GEO_FILTER_AREA);

        // sorting
        final String sortColumnId = Util.getStateString(_state, STATE_SORT_COLUMN_ID, COLUMN_NAME);
        final int sortDirection = Util.getStateInt(_state, STATE_SORT_COLUMN_DIRECTION, MarkerComparator.ASCENDING);

        // update comparator
        _markerComparator.__sortColumnId = sortColumnId;
        _markerComparator.__sortDirection = sortDirection;
    }

    private void restoreState_WithUI() {

        // GPS tour filter
        _actionTourFilterWithGPS.setChecked(_gpsMarkerFilter == GPS_MARKER_FILTER_WITH_GPS);
        _actionTourFilterWithoutGPS.setChecked(_gpsMarkerFilter == GPS_MARKER_FILTER_WITHOUT_GPS);

        /*
         * select marker item
         */
        final long stateMarkerId = Util.getStateLong(_state, //
                STATE_SELECTED_MARKER_ITEM, TourDatabase.ENTITY_IS_NOT_SAVED);

        if (stateMarkerId != TourDatabase.ENTITY_IS_NOT_SAVED) {

            // select marker item by it's ID
            for (final TourMarkerItem markerItem : _allMarkerItems) {
                if (markerItem.markerId == stateMarkerId) {

                    updateUI_SelectTourMarker(new StructuredSelection(markerItem), null);

                    return;
                }
            }
        }

        enableActions();
    }

    private void saveState() {

        _columnManager.saveState(_state);

        _state.put(STATE_SORT_COLUMN_ID, _markerComparator.__sortColumnId);
        _state.put(STATE_SORT_COLUMN_DIRECTION, _markerComparator.__sortDirection);

        _state.put(STATE_GPS_FILTER, _gpsMarkerFilter);

        /*
         * selected marker item
         */
        long markerId = TourDatabase.ENTITY_IS_NOT_SAVED;
        final StructuredSelection selection = getViewerSelection();
        final Object firstItem = selection.getFirstElement();

        if (firstItem instanceof TourMarkerItem) {
            final TourMarkerItem markerItem = (TourMarkerItem) firstItem;
            markerId = markerItem.markerId;
        }
        _state.put(STATE_SELECTED_MARKER_ITEM, markerId);
    }

    @Override
    public void setFocus() {
        _markerViewer.getTable().setFocus();
    }

    /**
     * Validates geo position accuracy and setup's the position formatter.
     */
    private void setup_LatLonDigits() {

        _nfLatLon.setMinimumFractionDigits(_latLonDigits);
        _nfLatLon.setMaximumFractionDigits(_latLonDigits);
    }

    @Override
    public void updateColumnHeader(final ColumnDefinition colDef) {
        // TODO Auto-generated method stub

    }

    void updateUI_GeoFilter() {

        // geo filter accuracy
        _geoFilterMaxDiff = Util.getStateDouble(_state, STATE_GEO_FILTER_AREA, DEFAULT_GEO_FILTER_AREA);

        refilterViewer();
    }

    private void updateUI_GeoFilter(final TourMarkerItem markerItem) {

        _geoFilterLat = markerItem.latitude;
        _geoFilterLon = markerItem.longitude;

        _viewerContainer.setRedraw(false);
        {
            _markerViewer.refresh(false);

            /*
             * Select and reveal marker item
             */
            final Table table = _markerViewer.getTable();
            final TableItem[] tableItems = table.getItems();

            int checkedIndex = 0;

            for (; checkedIndex < tableItems.length; checkedIndex++) {
                if (tableItems[checkedIndex].getData() == markerItem) {
                    break;
                }
            }

            if (checkedIndex < tableItems.length) {
                table.setSelection(checkedIndex);
            }

        }
        _viewerContainer.setRedraw(true);

    }

    void updateUI_LatLonDigits(final boolean isEnabled, final int selectedDigits) {

        if (isEnabled == _isLatLonDigitsEnabled && selectedDigits == _latLonDigits) {
            // nothing has changed
            return;
        }

        _isLatLonDigitsEnabled = isEnabled;
        _latLonDigits = selectedDigits;

        setup_LatLonDigits();

        _viewerContainer.setRedraw(false);
        {
            // keep selection
            final ISelection selectionBackup = getViewerSelection();
            final Object[] checkedElements = _markerViewer.getCheckedElements();
            {
                _markerViewer.refresh();
            }
            updateUI_SelectTourMarker(selectionBackup, checkedElements);
        }
        _viewerContainer.setRedraw(true);

    }

    /**
     * Select and reveal tour marker item.
     * 
     * @param selection
     * @param checkedElements
     */
    private void updateUI_SelectTourMarker(final ISelection selection, final Object[] checkedElements) {

        _isInUpdate = true;
        {
            _markerViewer.setSelection(selection, true);

            if (checkedElements != null && checkedElements.length > 0) {
                _markerViewer.setCheckedElements(checkedElements);
            }

            final Table table = _markerViewer.getTable();
            table.showSelection();
        }
        _isInUpdate = false;
    }

    /**
     * Set the sort column direction indicator for a column.
     * 
     * @param sortColumnId
     * @param isAscendingSort
     */
    private void updateUI_SetSortDirection(final String sortColumnId, final int sortDirection) {

        final Table table = _markerViewer.getTable();
        final TableColumn tc = getSortColumn(sortColumnId);

        table.setSortColumn(tc);
        table.setSortDirection(sortDirection == MarkerComparator.ASCENDING ? SWT.UP : SWT.DOWN);
    }

    private void updateUI_SetViewerInput() {

        _isInUpdate = true;
        {
            _markerViewer.setInput(new Object[0]);
        }
        _isInUpdate = false;
    }

}