au.gov.ga.earthsci.layer.ui.LayerTreePart.java Source code

Java tutorial

Introduction

Here is the source code for au.gov.ga.earthsci.layer.ui.LayerTreePart.java

Source

/*******************************************************************************
 * Copyright 2012 Geoscience Australia
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package au.gov.ga.earthsci.layer.ui;

import gov.nasa.worldwind.View;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.view.orbit.OrbitView;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;

import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.beans.IBeanListProperty;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.services.EMenuService;
import org.eclipse.e4.ui.services.IServiceConstants;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Shell;

import au.gov.ga.earthsci.application.Activator;
import au.gov.ga.earthsci.application.ImageRegistry;
import au.gov.ga.earthsci.application.parts.globe.handlers.TargetModeSwitcher;
import au.gov.ga.earthsci.common.databinding.ITreeChangeListener;
import au.gov.ga.earthsci.common.databinding.ObservableListTreeSupport;
import au.gov.ga.earthsci.common.databinding.TreeChangeAdapter;
import au.gov.ga.earthsci.common.ui.dialogs.StackTraceDialog;
import au.gov.ga.earthsci.layer.tree.ILayerTreeNode;
import au.gov.ga.earthsci.layer.ui.dnd.LayerTransfer;
import au.gov.ga.earthsci.layer.ui.dnd.LocalLayerTransfer;
import au.gov.ga.earthsci.layer.worldwind.ITreeModel;
import au.gov.ga.earthsci.worldwind.common.WorldWindowRegistry;
import au.gov.ga.earthsci.worldwind.common.layers.Bounded;
import au.gov.ga.earthsci.worldwind.common.layers.Bounds;
import au.gov.ga.earthsci.worldwind.common.util.Util;
import au.gov.ga.earthsci.worldwind.common.view.orbit.FlyToOrbitViewAnimator;
import au.gov.ga.earthsci.worldwind.common.view.orbit.FlyToSectorAnimator;
import au.gov.ga.earthsci.worldwind.common.view.target.TargetOrbitView;

/**
 * Part that shows the hierarchical tree of layers.
 * 
 * @author Michael de Hoog (michael.dehoog@ga.gov.au)
 */
public class LayerTreePart {
    public static final String PART_ID = "au.gov.ga.earthsci.application.layertree.part"; //$NON-NLS-1$

    @Inject
    private ITreeModel model;

    @Inject
    private IEclipseContext context;

    @Inject
    private ESelectionService selectionService;
    private boolean settingSelection = false;

    @Inject
    private EPartService partService;

    @Inject
    @Optional
    @Named(IServiceConstants.ACTIVE_SHELL)
    private Shell shell;

    private CTabFolder tabFolder;
    private CTabItem structureTabItem;
    private CTabItem orderTabItem;
    private CheckboxTreeViewer structureViewer;
    private TreeViewer orderViewer;
    private LayerTreeLabelProvider labelProvider;
    private Clipboard clipboard;
    private ObservableListTreeSupport<ILayerTreeNode> observableListTreeSupport;
    private final DrawOrderModel drawOrderModel = new DrawOrderModel();

    @PostConstruct
    public void init(Composite parent, EMenuService menuService) {
        tabFolder = new CTabFolder(parent, SWT.BOTTOM);

        structureTabItem = new CTabItem(tabFolder, SWT.NONE);
        structureTabItem.setText("Structure");
        createStructureViewer(tabFolder, menuService, structureTabItem);

        orderTabItem = new CTabItem(tabFolder, SWT.NONE);
        orderTabItem.setText("Draw order");
        createOrderViewer(tabFolder, menuService, orderTabItem);

        tabFolder.pack();
        tabFolder.setSelection(structureTabItem);
    }

    protected void createStructureViewer(Composite parent, EMenuService menuService, CTabItem tabItem) {
        structureViewer = new CheckboxTreeViewer(parent, SWT.MULTI);
        tabItem.setControl(structureViewer.getControl());
        structureViewer.getTree()
                .setBackgroundImage(ImageRegistry.getInstance().get(ImageRegistry.ICON_TRANSPARENT));
        context.set(TreeViewer.class, structureViewer);

        clipboard = new Clipboard(parent.getDisplay());
        context.set(Clipboard.class, clipboard);

        //create a property change listener for updating the labels whenever a property
        //changes on an ILayerTreeNode instance
        final PropertyChangeListener anyChangeListener = new PropertyChangeListener() {
            @Override
            public void propertyChange(final PropertyChangeEvent evt) {
                updateElementLabel(evt.getSource());
            }
        };
        //create a property change listener that ensures the expanded state of the tree
        //is kept in sync with the value of the expanded property for each node
        final PropertyChangeListener expandedChangeListener = new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                syncExpandedNodes();
            }
        };

        //setup the label provider
        labelProvider = new LayerTreeLabelProvider();

        //create a bean list property associated with ILayerTreeNode's children property
        IBeanListProperty<ILayerTreeNode, ILayerTreeNode> childrenProperty = BeanProperties
                .list(ILayerTreeNode.class, "children", ILayerTreeNode.class); //$NON-NLS-1$
        //setup a factory for creating observables observing ILayerTreeNodes
        IObservableFactory<ILayerTreeNode, IObservableList<ILayerTreeNode>> observableFactory = childrenProperty
                .listFactory();

        //listen for any changes (additions/removals) to any of the children in the tree
        observableListTreeSupport = new ObservableListTreeSupport<ILayerTreeNode>(observableFactory);
        observableListTreeSupport.addListener(new ITreeChangeListener<ILayerTreeNode>() {
            @Override
            public void elementAdded(ILayerTreeNode element) {
                element.addPropertyChangeListener(anyChangeListener);
                element.addPropertyChangeListener("expanded", expandedChangeListener); //$NON-NLS-1$
            }

            @Override
            public void elementRemoved(ILayerTreeNode element) {
                element.removePropertyChangeListener(anyChangeListener);
                element.removePropertyChangeListener("expanded", expandedChangeListener); //$NON-NLS-1$
            }
        });

        //create a content provider that listens for changes to any children in the tree
        ObservableListTreeContentProvider<ILayerTreeNode> contentProvider = new ObservableListTreeContentProvider<ILayerTreeNode>(
                observableFactory, null);

        //set the viewer's providers
        structureViewer.setContentProvider(contentProvider);
        structureViewer.setLabelProvider(labelProvider);
        structureViewer.setCheckStateProvider(new LayerTreeCheckStateProvider());

        //set the viewer and listener inputs
        structureViewer.setInput(model.getRootNode());
        observableListTreeSupport.setInput(model.getRootNode());

        //Listen for any additions to the tree, and expand added node's parent, so that
        //added nodes are always visible. This is done after the input is set up, so that
        //we don't expand all the nodes that are already in the tree.
        observableListTreeSupport.addListener(new TreeChangeAdapter<ILayerTreeNode>() {
            @Override
            public void elementAdded(ILayerTreeNode element) {
                //for any children added, expand the nodes
                if (!element.isRoot()) {
                    element.getParent().setExpanded(true);
                }

                //if the nodes were already expanded, the expanded property change event
                //is not fired, so we need to sync the expanded state anyway
                syncExpandedNodes();

                //when a layer is added, we should activate this part and select the added element
                activateAndSelectElement(element);
            }
        });

        //expand any nodes that should be expanded after unpersisting
        syncExpandedNodes();

        //enable/disable any nodes that are checked/unchecked
        structureViewer.addCheckStateListener(new ICheckStateListener() {
            @Override
            public void checkStateChanged(CheckStateChangedEvent event) {
                Object element = event.getElement();
                if (element instanceof ILayerTreeNode) {
                    ILayerTreeNode node = (ILayerTreeNode) element;
                    node.enableChildren(event.getChecked());
                }
            }
        });

        //setup the selection tracking
        structureViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection selection = (IStructuredSelection) structureViewer.getSelection();
                List<?> list = selection.toList();
                ILayerTreeNode[] array = list.toArray(new ILayerTreeNode[list.size()]);
                settingSelection = true;
                selectionService.setSelection(array.length == 1 ? array[0] : array);
                settingSelection = false;
            }
        });
        structureViewer.getTree().addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetDefaultSelected(SelectionEvent e) {
                IStructuredSelection selection = (IStructuredSelection) structureViewer.getSelection();
                ILayerTreeNode firstElement = (ILayerTreeNode) selection.getFirstElement();
                if (firstElement != null) {
                    selectLayer(firstElement);
                }
            }
        });

        //setup tree expansion/collapse listening
        structureViewer.addTreeListener(new ITreeViewerListener() {
            @Override
            public void treeExpanded(TreeExpansionEvent event) {
                ILayerTreeNode layerNode = (ILayerTreeNode) event.getElement();
                layerNode.setExpanded(true);
            }

            @Override
            public void treeCollapsed(TreeExpansionEvent event) {
                ILayerTreeNode layerNode = (ILayerTreeNode) event.getElement();
                layerNode.setExpanded(false);
            }
        });

        //make tree cells unselectable by selecting outside
        structureViewer.getTree().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                ViewerCell cell = structureViewer.getCell(new Point(e.x, e.y));
                if (cell == null) {
                    structureViewer.setSelection(StructuredSelection.EMPTY);
                }
            }
        });

        //setup cell editing
        structureViewer
                .setCellEditors(new CellEditor[] { new TextCellEditor(structureViewer.getTree(), SWT.BORDER) });
        structureViewer.setColumnProperties(new String[] { "layer" }); //$NON-NLS-1$
        structureViewer.setCellModifier(new ICellModifier() {
            @Override
            public void modify(Object element, String property, Object value) {
                if (element instanceof Item) {
                    element = ((Item) element).getData();
                }
                ((ILayerTreeNode) element).setLabel((String) value);
            }

            @Override
            public Object getValue(Object element, String property) {
                if (element instanceof Item) {
                    element = ((Item) element).getData();
                }
                return ((ILayerTreeNode) element).getLabelOrName();
            }

            @Override
            public boolean canModify(Object element, String property) {
                return true;
            }
        });

        ColumnViewerEditorActivationStrategy activationStrategy = new ColumnViewerEditorActivationStrategy(
                structureViewer) {
            @Override
            protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
                return event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC;
            }
        };
        TreeViewerEditor.create(structureViewer, activationStrategy, ColumnViewerEditor.KEYBOARD_ACTIVATION);

        //add drag and drop support
        int ops = DND.DROP_COPY | DND.DROP_MOVE;
        structureViewer.addDragSupport(ops,
                new Transfer[] { LocalLayerTransfer.getInstance(), LayerTransfer.getInstance() },
                new LayerTreeDragSourceListener(structureViewer));
        structureViewer
                .addDropSupport(ops,
                        new Transfer[] { LocalLayerTransfer.getInstance(), LayerTransfer.getInstance(),
                                FileTransfer.getInstance() },
                        new LayerTreeDropAdapter(structureViewer, model, context));

        //add context menu
        menuService.registerContextMenu(structureViewer.getTree(),
                "au.gov.ga.earthsci.application.layertree.popupmenu"); //$NON-NLS-1$
    }

    protected void createOrderViewer(Composite parent, EMenuService menuService, CTabItem tabItem) {
        orderViewer = new TreeViewer(parent, SWT.MULTI);
        tabItem.setControl(orderViewer.getControl());
        orderViewer.getTree().setBackgroundImage(ImageRegistry.getInstance().get(ImageRegistry.ICON_TRANSPARENT));
        orderViewer.setAutoExpandLevel(2);

        drawOrderModel.setInput(model.getRootNode());

        //create a bean list property associated with ILayerTreeNode's children property
        IBeanListProperty<DrawOrderModel.IDrawOrderModelElement, DrawOrderModel.IDrawOrderModelElement> childrenProperty = BeanProperties
                .list(DrawOrderModel.IDrawOrderModelElement.class, "children", //$NON-NLS-1$
                        DrawOrderModel.IDrawOrderModelElement.class);
        //setup a factory for creating observables observing ILayerTreeNodes
        IObservableFactory<DrawOrderModel.IDrawOrderModelElement, IObservableList<DrawOrderModel.IDrawOrderModelElement>> observableFactory = childrenProperty
                .listFactory();

        //create a content provider that listens for changes to any children in the tree
        ObservableListTreeContentProvider<DrawOrderModel.IDrawOrderModelElement> contentProvider = new ObservableListTreeContentProvider<DrawOrderModel.IDrawOrderModelElement>(
                observableFactory, null);

        DrawOrderLabelProvider labelProvider = new DrawOrderLabelProvider();

        //set the viewer's providers
        orderViewer.setContentProvider(contentProvider);
        orderViewer.setLabelProvider(new DelegatingStyledCellLabelProvider(labelProvider));

        //set the viewer and listener inputs
        orderViewer.setInput(drawOrderModel.getRoot());

        //add drag and drop support
        int ops = DND.DROP_MOVE;
        orderViewer.addDragSupport(ops, new Transfer[] { LocalLayerTransfer.getInstance() },
                new DrawOrderDragSourceListener(orderViewer));
        orderViewer.addDropSupport(ops, new Transfer[] { LocalLayerTransfer.getInstance() },
                new DrawOrderDropAdapter(orderViewer));
    }

    @PreDestroy
    private void packup() {
        observableListTreeSupport.dispose();
        context.remove(TreeViewer.class);
        context.remove(Clipboard.class);
        labelProvider.packup();
        drawOrderModel.dispose();
    }

    @Focus
    private void setFocus() {
        if (tabFolder.getSelection() == orderTabItem) {
            orderViewer.getTree().setFocus();
        } else {
            structureViewer.getTree().setFocus();
        }
    }

    private void updateElementLabel(final Object element) {
        if (!structureViewer.getControl().isDisposed()) {
            structureViewer.getControl().getDisplay().asyncExec(new Runnable() {
                @Override
                public void run() {
                    LabelProviderChangedEvent event = new LabelProviderChangedEvent(labelProvider, element);
                    labelProvider.fireLabelProviderChanged(event);
                }
            });
        }
    }

    private void syncExpandedNodes() {
        //ensure the expanded elements are kept in sync with the model
        structureViewer.getControl().getDisplay().asyncExec(new Runnable() {
            @Override
            public void run() {
                if (!structureViewer.getControl().isDisposed()) {
                    structureViewer.setExpandedElements(getExpandedNodes());
                }
            }
        });
    }

    private ILayerTreeNode[] getExpandedNodes() {
        List<ILayerTreeNode> list = new ArrayList<ILayerTreeNode>();
        addExpandedChildrenToList(model.getRootNode(), list);
        return list.toArray(new ILayerTreeNode[list.size()]);
    }

    private void addExpandedChildrenToList(ILayerTreeNode parent, List<ILayerTreeNode> list) {
        if (parent.isExpanded()) {
            list.add(parent);
        }
        for (ILayerTreeNode child : parent.getChildren()) {
            addExpandedChildrenToList(child, list);
        }
    }

    public void selectLayer(ILayerTreeNode layer) {
        if (layer.getStatus().isError()) {
            Throwable e = layer.getStatus().getThrowable();
            IStatus status = new Status(IStatus.ERROR, Activator.getBundleName(), e.getLocalizedMessage(), e);
            StackTraceDialog.openError(shell, "Error", null, status);
        } else {
            flyToLayer(layer);
        }
    }

    public static void flyToLayer(ILayerTreeNode layer) {
        //first check if the tree node is pointing to a KML feature; if so, goto the feature 
        /*if (layer instanceof TreeNodeLayerNode)
        {
           TreeNode treeNode = ((TreeNodeLayerNode) layer).getTreeNode();
           if (treeNode instanceof KMLFeatureTreeNode)
           {
        KMLAbstractFeature feature = ((KMLFeatureTreeNode) treeNode).getFeature();
            
        KMLViewController viewController = CustomKMLViewControllerFactory.create(wwd);
        if (viewController != null)
        {
           viewController.goTo(feature);
           wwd.redraw();
           return;
        }
           }
        }*/

        View view = WorldWindowRegistry.INSTANCE.getActiveView();
        if (view == null) {
            return;
        }

        Bounds bounds = Bounded.Reader.getBounds(layer);
        if (bounds == null || !(view instanceof OrbitView)) {
            return;
        }

        boolean targetMode = false;
        if (view instanceof TargetOrbitView) {
            targetMode = !(Bounded.Reader.isFollowTerrain(layer) && bounds.minimum.elevation == 0
                    && bounds.maximum.elevation == 0);
            TargetModeSwitcher.setTargetMode((TargetOrbitView) view, targetMode);
        }

        OrbitView orbitView = (OrbitView) view;
        Position center = orbitView.getCenterPosition();
        Position newCenter;
        if (bounds.toSector().contains(center) && bounds.deltaLatitude.degrees > 90
                && bounds.deltaLongitude.degrees > 90) {
            //handles large datasets, like the blue marble imagery, and landsat
            newCenter = targetMode ? center : new Position(center, 0);
        } else {
            newCenter = bounds.center;
        }

        LatLon endVisibleDelta = new LatLon(bounds.deltaLatitude, bounds.deltaLongitude);
        long lengthMillis = Util.getScaledLengthMillis(1, center, newCenter);

        Angle newHeading = orbitView.getHeading();
        Angle newPitch = orbitView.getPitch();
        if (!targetMode && newPitch.degrees > 45) {
            newPitch = Angle.fromDegrees(45);
        }

        double newZoom = FlyToSectorAnimator.calculateEndZoom(orbitView, endVisibleDelta);
        FlyToOrbitViewAnimator animator = FlyToOrbitViewAnimator.createFlyToOrbitViewAnimator(orbitView, center,
                newCenter, orbitView.getHeading(), newHeading, orbitView.getPitch(), newPitch, orbitView.getZoom(),
                newZoom, lengthMillis, WorldWind.ABSOLUTE);
        orbitView.stopAnimations();
        orbitView.stopMovement();
        orbitView.addAnimator(animator);
        orbitView.firePropertyChange(AVKey.VIEW, null, orbitView);
    }

    protected void activateAndSelectElement(final ILayerTreeNode element) {
        if (!structureViewer.getControl().isDisposed()) {
            structureViewer.getControl().getDisplay().asyncExec(new Runnable() {
                @Override
                public void run() {
                    MPart part = partService.findPart(PART_ID);
                    if (part != null) {
                        partService.activate(part);
                        structureViewer.setSelection(new StructuredSelection(element), true);
                        tabFolder.setSelection(structureTabItem);
                    }
                }
            });
        }
    }

    @Inject
    private void select(@Optional @Named(IServiceConstants.ACTIVE_SELECTION) ILayerTreeNode[] nodes) {
        if (nodes == null || structureViewer == null || settingSelection) {
            return;
        }
        StructuredSelection selection = new StructuredSelection(nodes);
        structureViewer.setSelection(selection, true);
        for (ILayerTreeNode node : nodes) {
            structureViewer.expandToLevel(node, 1);
        }
        tabFolder.setSelection(structureTabItem);
    }

    @Inject
    private void select(@Optional @Named(IServiceConstants.ACTIVE_SELECTION) ILayerTreeNode node) {
        if (node == null) {
            return;
        }
        select(new ILayerTreeNode[] { node });
    }
}