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

Java tutorial

Introduction

Here is the source code for au.gov.ga.earthsci.layer.ui.LayerTreeLabelProvider.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.globes.ElevationModel;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.terrain.CompoundElevationModel;

import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;

import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.DecoratingStyledCellLabelProvider;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;

import au.gov.ga.earthsci.application.IconLoader;
import au.gov.ga.earthsci.application.ImageRegistry;
import au.gov.ga.earthsci.common.ui.util.SWTUtil;
import au.gov.ga.earthsci.common.ui.util.TextStyler;
import au.gov.ga.earthsci.common.ui.viewers.IFireableLabelProvider;
import au.gov.ga.earthsci.core.retrieve.IRetrieval;
import au.gov.ga.earthsci.core.retrieve.IRetrievalListener;
import au.gov.ga.earthsci.core.retrieve.IRetrievalService;
import au.gov.ga.earthsci.core.retrieve.IRetrievalServiceListener;
import au.gov.ga.earthsci.core.retrieve.RetrievalAdapter;
import au.gov.ga.earthsci.core.retrieve.RetrievalServiceFactory;
import au.gov.ga.earthsci.layer.delegator.ILayerDelegator;
import au.gov.ga.earthsci.layer.elevation.IElevationModelLayer;
import au.gov.ga.earthsci.layer.tree.FolderNode;
import au.gov.ga.earthsci.layer.tree.ILayerNode;
import au.gov.ga.earthsci.layer.tree.ILayerTreeNode;
import au.gov.ga.earthsci.layer.tree.LayerNode;
import au.gov.ga.earthsci.worldwind.common.util.Loader;
import au.gov.ga.earthsci.worldwind.common.util.Loader.LoadingListener;

/**
 * Label provider for the layer tree.
 * 
 * @author Michael de Hoog (michael.dehoog@ga.gov.au)
 */
public class LayerTreeLabelProvider extends DecoratingStyledCellLabelProvider implements IFireableLabelProvider {
    private final LayerTreeLabelProviderDelegate delegate;
    private final IRetrievalService retrievalService;
    private final Set<IRetrieval> refreshingRetrievals = new HashSet<IRetrieval>();

    private Color downloadBackgroundColor;
    private Color downloadForegroundColor;
    private static final int DOWNLOAD_WIDTH = 50;

    public LayerTreeLabelProvider() {
        this(new LayerTreeLabelProviderDelegate());
    }

    private LayerTreeLabelProvider(LayerTreeLabelProviderDelegate delegate) {
        super(delegate, delegate, null);
        this.delegate = delegate;
        this.retrievalService = RetrievalServiceFactory.getServiceInstance();
        retrievalService.addListener(retrievalServiceListener);
    }

    void packup() {
        retrievalService.removeListener(retrievalServiceListener);
        if (downloadBackgroundColor != null) {
            downloadBackgroundColor.dispose();
            downloadForegroundColor.dispose();
            downloadBackgroundColor = null;
            downloadForegroundColor = null;
        }
    }

    @Override
    public void update(ViewerCell cell) {
        super.update(cell);

        //ensure that the paint method is called too:
        Rectangle bounds = cell.getBounds();
        getViewer().getControl().redraw(bounds.x, bounds.y, bounds.width, bounds.height, true);
    }

    @Override
    protected void paint(Event event, Object element) {
        if (element instanceof LayerNode) {
            LayerNode layerNode = (LayerNode) element;
            Layer layer = layerNode.getLayer();
            List<IRetrieval> retrievals = getRetrievalsForLayer(layer);
            if (retrievals.size() > 0) {
                if (downloadBackgroundColor == null) {
                    Color listBackground = event.display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
                    boolean darken = SWTUtil.shouldDarken(listBackground);
                    downloadBackgroundColor = darken ? SWTUtil.darker(listBackground)
                            : SWTUtil.lighter(listBackground);
                    downloadForegroundColor = darken ? SWTUtil.darker(downloadBackgroundColor)
                            : SWTUtil.lighter(downloadBackgroundColor);
                }

                GC gc = event.gc;
                Color oldBackground = gc.getBackground();
                Color oldForeground = gc.getForeground();

                float percentage = 0;
                for (IRetrieval retrieval : retrievals) {
                    percentage += Math.max(0, retrieval.getPercentage());
                }
                percentage /= retrievals.size();

                int height = event.height / 2;
                int width = (int) (DOWNLOAD_WIDTH * percentage);
                gc.setBackground(downloadBackgroundColor);
                gc.setForeground(downloadForegroundColor);
                gc.fillRectangle(event.x + event.width, event.y + (event.height - height) / 2, width, height);
                gc.drawRectangle(event.x + event.width, event.y + (event.height - height) / 2, DOWNLOAD_WIDTH,
                        height);

                gc.setBackground(oldBackground);
                gc.setForeground(oldForeground);
            }
        }
        super.paint(event, element);
    }

    private List<IRetrieval> getRetrievalsForLayer(Layer layer) {
        List<IRetrieval> retrievals = new ArrayList<IRetrieval>();
        addRetrievalsForLayer(layer, retrievals);
        return retrievals;
    }

    private void addRetrievalsForLayer(Layer layer, List<IRetrieval> retrievals) {
        retrievals.addAll(Arrays.asList(retrievalService.getRetrievals(layer)));
        if (layer instanceof ILayerDelegator<?>) {
            ILayerDelegator<?> delegator = (ILayerDelegator<?>) layer;
            addRetrievalsForLayer(delegator.getLayer(), retrievals);
        }
        if (layer instanceof IElevationModelLayer) {
            ElevationModel elevationModel = ((IElevationModelLayer) layer).getElevationModel();
            addRetrievalsForElevationModel(elevationModel, retrievals);
        }
    }

    private void addRetrievalsForElevationModel(ElevationModel elevationModel, List<IRetrieval> retrievals) {
        IRetrieval[] array = retrievalService.getRetrievals(elevationModel);
        if (array.length > 0) {
            retrievals.addAll(Arrays.asList(array));
        }
        if (elevationModel instanceof CompoundElevationModel) {
            CompoundElevationModel cem = (CompoundElevationModel) elevationModel;
            for (ElevationModel child : cem.getElevationModels()) {
                addRetrievalsForElevationModel(child, retrievals);
            }
        }
    }

    private void refreshCallers(final IRetrieval retrieval) {
        //don't queue up multiple updates for the same retrieval, as this floods the UI thread with asyncExec's
        if (refreshingRetrievals.contains(retrieval)) {
            return;
        }

        List<Object> elements = new ArrayList<Object>();
        Object[] callers = retrieval.getCallers();
        for (Object caller : callers) {
            if (caller instanceof ILayerTreeNode) {
                elements.add(caller);
            } else if (caller instanceof Layer || caller instanceof ElevationModel) {
                ILayerNode node = delegate.getNodeForLayerOrElevationModel(caller);
                if (node != null) {
                    elements.add(node);
                }
            }
        }

        if (!elements.isEmpty()) {
            final Object[] array = elements.toArray();
            final ColumnViewer viewer = getViewer();
            if (viewer != null && !viewer.getControl().isDisposed()) {
                refreshingRetrievals.add(retrieval);
                viewer.getControl().getDisplay().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        if (!viewer.getControl().isDisposed()) {
                            viewer.update(array, null);
                        }
                        refreshingRetrievals.remove(retrieval);
                    }
                });
            }
        }
    }

    @Override
    public void fireLabelProviderChanged(LabelProviderChangedEvent event) {
        super.fireLabelProviderChanged(event);
    }

    /**
     * Get the image for the given element. Image element is expected to be an
     * instance of {@link ILayerTreeNode}.
     * 
     * @param imageElement
     *            An {@link ILayerTreeNode} for which to get the image
     * @param viewerElement
     *            Element passed to the label provider's getImage() method
     * @param iconLoader
     *            Loader used for icon loading
     * @return Image for the given element
     */
    public static Image getImage(Object imageElement, Object viewerElement, IconLoader iconLoader) {
        if (imageElement instanceof ILayerTreeNode) {
            ILayerTreeNode node = (ILayerTreeNode) imageElement;

            if (!node.getStatus().isOk()) {
                return ImageRegistry.getInstance().get(ImageRegistry.ICON_ERROR);
            }

            URL imageURL = node.getIconURL();
            if (imageURL != null) {
                return iconLoader.getImage(viewerElement, imageURL);
            } else {
                if (imageElement instanceof LayerNode) {
                    return ImageRegistry.getInstance().get(ImageRegistry.ICON_FILE);
                }
                return ImageRegistry.getInstance().get(ImageRegistry.ICON_FOLDER);
            }
        }
        return null;
    }

    private IRetrievalServiceListener retrievalServiceListener = new IRetrievalServiceListener() {
        @Override
        public void retrievalAdded(IRetrieval retrieval) {
            refreshCallers(retrieval);
            retrieval.addListener(retrievalListener);
        }

        @Override
        public void retrievalRemoved(IRetrieval retrieval) {
            refreshCallers(retrieval);
            retrieval.removeListener(retrievalListener);
        }
    };

    private IRetrievalListener retrievalListener = new RetrievalAdapter() {
        @Override
        public void callersChanged(IRetrieval retrieval) {
            refreshCallers(retrieval);
        }

        @Override
        public void progress(IRetrieval retrieval) {
            refreshCallers(retrieval);
        }
    };

    private static class LayerTreeLabelProviderDelegate extends LabelProvider
            implements ILabelDecorator, IStyledLabelProvider, IFireableLabelProvider, LoadingListener {
        private WeakHashMap<Layer, WeakReference<ILayerNode>> weakLayerToNodeMap = new WeakHashMap<Layer, WeakReference<ILayerNode>>();
        private WeakHashMap<ElevationModel, WeakReference<ILayerNode>> weakElevationModelToNodeMap = new WeakHashMap<ElevationModel, WeakReference<ILayerNode>>();

        private final IconLoader iconLoader = new IconLoader(this);

        private boolean disposed = false;

        private final Font subscriptBoldFont;
        private final Font subscriptFont;
        private final TextStyler informationStyler = new TextStyler();
        private final TextStyler legendStyler = new TextStyler();
        private final TextStyler grayStyler = new TextStyler();

        public LayerTreeLabelProviderDelegate() {
            FontData[] fontDatas = Display.getDefault().getSystemFont().getFontData();
            for (FontData fontData : fontDatas) {
                fontData.setStyle(SWT.BOLD);
                fontData.setHeight((int) (fontData.getHeight() * 0.8));
            }
            subscriptBoldFont = new Font(Display.getDefault(), fontDatas);
            informationStyler.style.foreground = Display.getDefault().getSystemColor(SWT.COLOR_BLUE);
            informationStyler.style.font = subscriptBoldFont;
            legendStyler.style.foreground = Display.getDefault().getSystemColor(SWT.COLOR_DARK_GREEN);
            legendStyler.style.font = subscriptBoldFont;

            fontDatas = Display.getDefault().getSystemFont().getFontData();
            for (FontData fontData : fontDatas) {
                fontData.setHeight((int) (fontData.getHeight() * 0.9));
            }
            subscriptFont = new Font(Display.getDefault(), fontDatas);
            Color listColor = Display.getDefault().getSystemColor(SWT.COLOR_LIST_FOREGROUND);
            Color gray = SWTUtil.shouldDarken(listColor) ? SWTUtil.darker(listColor, 0.6f)
                    : SWTUtil.lighter(listColor, 0.6f);
            grayStyler.style.foreground = gray;
            grayStyler.style.font = subscriptFont;
        }

        @Override
        public void dispose() {
            //because this object is acting as both the decorator and the provider,
            //dispose is called twice, causing a NPE in the super class
            //workaround: set a flag when disposed, disabling multiple disposals
            if (disposed) {
                return;
            }
            disposed = true;

            super.dispose();
            iconLoader.dispose();
            subscriptBoldFont.dispose();
            subscriptFont.dispose();
        }

        @Override
        public String getText(Object element) {
            if (element instanceof ILayerNode) {
                ILayerNode layerNode = (ILayerNode) element;
                Layer layer = layerNode.getGrandLayer();

                if (layer != null) {
                    if (!weakLayerToNodeMap.containsKey(layer)) {
                        //new layer, check if it's a Loader
                        if (layer instanceof Loader) {
                            Loader loader = (Loader) layer;
                            loader.addLoadingListener(this);
                        }
                    }

                    WeakReference<ILayerNode> layerNodeReference = new WeakReference<ILayerNode>(layerNode);
                    weakLayerToNodeMap.put(layer, layerNodeReference);
                    if (layer instanceof IElevationModelLayer) {
                        ElevationModel elevationModel = ((IElevationModelLayer) layer).getElevationModel();
                        addElevationModelToMap(elevationModel, layerNodeReference);
                    }
                }

                return layerNode.getLabelOrName();
            } else if (element instanceof FolderNode) {
                FolderNode folder = (FolderNode) element;
                return folder.getLabelOrName();
            }
            return super.getText(element);
        }

        private void addElevationModelToMap(ElevationModel elevationModel,
                WeakReference<ILayerNode> layerNodeReference) {
            weakElevationModelToNodeMap.put(elevationModel, layerNodeReference);
            if (elevationModel instanceof CompoundElevationModel) {
                CompoundElevationModel cem = (CompoundElevationModel) elevationModel;
                for (ElevationModel child : cem.getElevationModels()) {
                    addElevationModelToMap(child, layerNodeReference);
                }
            }
        }

        public ILayerNode getNodeForLayerOrElevationModel(Object key) {
            WeakReference<ILayerNode> weak = weakLayerToNodeMap.get(key);
            if (weak == null) {
                weak = weakElevationModelToNodeMap.get(key);
            }
            if (weak == null) {
                return null;
            }
            return weak.get();
        }

        @Override
        public Image getImage(Object element) {
            return LayerTreeLabelProvider.getImage(element, element, iconLoader);
        }

        @Override
        public Image decorateImage(Image image, Object element) {
            return null;
        }

        @Override
        public String decorateText(String text, Object element) {
            return text;
        }

        @Override
        public StyledString getStyledText(Object element) {
            StyledString string = new StyledString(getText(element));
            if (element instanceof ILayerTreeNode) {
                ILayerTreeNode node = (ILayerTreeNode) element;

                if (node instanceof ILayerNode) {
                    ILayerNode layerNode = (ILayerNode) node;
                    if (layerNode.getOpacity() < 1) {
                        string.append(String.format(" (%d%%)", (int) (layerNode.getOpacity() * 100)), grayStyler); //$NON-NLS-1$
                    }

                    Layer layer = layerNode.getGrandLayer();
                    if (layer instanceof Loader && ((Loader) layer).isLoading()) {
                        string.append(" " + Messages.LayerTreeLabelProvider_Loading, grayStyler); //$NON-NLS-1$
                    }
                }

                if (node.getInformationURL() != null || node.getLegendURL() != null) {
                    string.append(" "); //$NON-NLS-1$
                    if (node.getInformationURL() != null) {
                        string.append(" i", informationStyler); //$NON-NLS-1$
                    }
                    if (node.getLegendURL() != null) {
                        string.append(" L", legendStyler); //$NON-NLS-1$
                    }
                }
            }
            return string;
        }

        @Override
        public void fireLabelProviderChanged(LabelProviderChangedEvent event) {
            super.fireLabelProviderChanged(event);
        }

        @Override
        public void loadingStateChanged(Loader loader, boolean isLoading) {
            ILayerNode node = getNodeForLayerOrElevationModel(loader);
            if (node != null) {
                fireLabelProviderChanged(new LabelProviderChangedEvent(this, node));
            }
        }
    }
}