com.bdaum.zoom.ui.internal.views.HierarchyView.java Source code

Java tutorial

Introduction

Here is the source code for com.bdaum.zoom.ui.internal.views.HierarchyView.java

Source

/*
 * This file is part of the ZoRa project: http://www.photozora.org.
 *
 * ZoRa 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * ZoRa 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 ZoRa; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * (c) 2009 Berthold Daum  
 */

package com.bdaum.zoom.ui.internal.views;

import java.io.File;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;

import com.bdaum.aoModeling.runtime.AomObject;
import com.bdaum.aoModeling.runtime.IIdentifiableObject;
import com.bdaum.zoom.cat.model.asset.Asset;
import com.bdaum.zoom.cat.model.asset.AssetImpl;
import com.bdaum.zoom.cat.model.composedTo.ComposedToImpl;
import com.bdaum.zoom.cat.model.derivedBy.DerivedByImpl;
import com.bdaum.zoom.cat.model.group.CriterionImpl;
import com.bdaum.zoom.cat.model.group.PostProcessorImpl;
import com.bdaum.zoom.cat.model.group.SmartCollectionImpl;
import com.bdaum.zoom.cat.model.group.SortCriterionImpl;
import com.bdaum.zoom.core.BagChange;
import com.bdaum.zoom.core.Constants;
import com.bdaum.zoom.core.Core;
import com.bdaum.zoom.core.IAssetProvider;
import com.bdaum.zoom.core.IPostProcessor;
import com.bdaum.zoom.core.QueryField;
import com.bdaum.zoom.core.db.IDbManager;
import com.bdaum.zoom.core.internal.CoreActivator;
import com.bdaum.zoom.image.ImageUtilities;
import com.bdaum.zoom.job.OperationJob;
import com.bdaum.zoom.operations.internal.LinkOperation;
import com.bdaum.zoom.operations.internal.ModifyRelationLegendOperation;
import com.bdaum.zoom.operations.internal.RelationDescription;
import com.bdaum.zoom.operations.internal.UnlinkOperation;
import com.bdaum.zoom.ui.AssetSelection;
import com.bdaum.zoom.ui.IZoomActionConstants;
import com.bdaum.zoom.ui.Ui;
import com.bdaum.zoom.ui.dialogs.AcousticMessageDialog;
import com.bdaum.zoom.ui.internal.HelpContextIds;
import com.bdaum.zoom.ui.internal.Icons;
import com.bdaum.zoom.ui.internal.UiActivator;
import com.bdaum.zoom.ui.internal.UiUtilities;
import com.bdaum.zoom.ui.internal.actions.ZoomActionFactory;
import com.bdaum.zoom.ui.internal.dialogs.DescriptionDialog;
import com.bdaum.zoom.ui.preferences.PreferenceConstants;

@SuppressWarnings("restriction")
public class HierarchyView extends ImageView implements ISelectionChangedListener, IDragHost {

    @SuppressWarnings("serial")
    public static class HierarchyPostProcessor extends PostProcessorImpl implements IPostProcessor {

        private final Node node;

        public HierarchyPostProcessor(Node node) {
            this.node = node;
        }

        @Override
        public IPostProcessor clone() {
            return new HierarchyPostProcessor(node);
        }

        public List<Asset> process(List<Asset> set) {
            removeShownNodes(set, node);
            return set;
        }

        private void removeShownNodes(List<Asset> set, Node parent) {
            Asset a = parent.getAsset();
            if (a != null)
                set.remove(a);
            for (Node child : parent.getChildren())
                removeShownNodes(set, child);
        }

    }

    public class Node {

        private Asset asset;
        private IIdentifiableObject description;
        private List<Node> children;
        private Node parent;
        private int typ;
        private Image image;
        private static final int THUMBSIZE = 56;

        public Node(Node parent, Asset asset, int type) {
            this.asset = asset;
            this.parent = parent;
            this.typ = type;
        }

        public IIdentifiableObject getDescription() {
            return description;
        }

        public void setDescription(AomObject description) {
            this.description = description;
        }

        public Asset getAsset() {
            return asset == null ? null : Core.getCore().getDbManager().obtainAsset(asset.getStringId());
        }

        public List<Node> getChildren() {
            if (children == null) {
                IDbManager dbManager = Core.getCore().getDbManager();
                String assetId = asset.getStringId();
                List<Serializable> list = new ArrayList<Serializable>();
                List<AomObject> descriptions = new ArrayList<AomObject>();
                switch (typ) {
                case Constants.DERIVATIVES:
                    for (DerivedByImpl rel : dbManager.obtainObjects(DerivedByImpl.class, "original", assetId, //$NON-NLS-1$
                            QueryField.EQUALS)) {
                        String derivative = rel.getDerivative();
                        if (derivative.indexOf(':') < 0) {
                            list.add(derivative);
                            descriptions.add(rel);
                        }
                    }
                    break;
                case Constants.ORIGINALS:
                    for (DerivedByImpl rel : dbManager.obtainObjects(DerivedByImpl.class, "derivative", assetId, //$NON-NLS-1$
                            QueryField.EQUALS)) {
                        String original = rel.getOriginal();
                        if (original.indexOf(':') < 0) {
                            list.add(original);
                            descriptions.add(rel);
                        }
                    }
                    break;
                case Constants.COMPOSITES:
                    for (ComposedToImpl rel : dbManager.obtainObjects(ComposedToImpl.class, "component", assetId, //$NON-NLS-1$
                            QueryField.CONTAINS)) {
                        list.add(rel.getComposite());
                        descriptions.add(rel);
                    }
                    break;
                case Constants.COMPONENTS:
                    for (ComposedToImpl rel : dbManager.obtainObjects(ComposedToImpl.class, "composite", assetId, //$NON-NLS-1$
                            QueryField.EQUALS))
                        for (String id : rel.getComponent()) {
                            list.add(id);
                            descriptions.add(rel);
                        }
                    break;
                }
                children = new ArrayList<Node>(list.size());
                Iterator<AomObject> it = descriptions.iterator();
                for (Serializable id : list) {
                    AomObject descr = it.next();
                    AssetImpl a = dbManager.obtainAsset((String) id);
                    if (a != null) {
                        Node child = new Node(this, a, typ);
                        child.setDescription(descr);
                        children.add(child);
                    }
                }
            }
            return children;
        }

        public Node getParent() {
            return parent;
        }

        public Image getThumbnail() {
            if (asset == null)
                return null;
            if (image == null) {
                Image thumbnail = getImage(asset);
                Rectangle bounds = thumbnail.getBounds();
                double f = (double) THUMBSIZE / Math.max(bounds.width, bounds.height);
                image = ImageUtilities.scaleSWT(thumbnail, (int) (bounds.width * f), (int) (bounds.height * f),
                        Platform.getPreferencesService().getBoolean(UiActivator.PLUGIN_ID,
                                PreferenceConstants.ADVANCEDGRAPHICS, false, null),
                        1, false, thumbnail.getDevice().getSystemColor(SWT.COLOR_GRAY));
            }
            return image;
        }

        public void dispose() {
            if (image != null)
                image.dispose();
            if (children != null)
                for (Node child : children)
                    child.dispose();
        }

        public String acceptChildren(String[] ids) {
            for (String id : ids) {
                Asset a = searchInHierarchy(id);
                if (a != null)
                    return NLS.bind(Messages.getString("HierarchyView.image_belongs_to_hierarchy"), //$NON-NLS-1$
                            a.getName());
                if (isInOppositeHierarchy(id))
                    return Messages.getString("HierarchyView.cyclic_relationship"); //$NON-NLS-1$
            }
            OperationJob.executeOperation(new LinkOperation(typ, asset.getStringId(), ids), HierarchyView.this);
            return null;
        }

        public boolean isInOppositeHierarchy(String id) {
            int oppositeType = -1;
            switch (typ) {
            case Constants.DERIVATIVES:
                oppositeType = Constants.ORIGINALS;
                break;
            case Constants.ORIGINALS:
                oppositeType = Constants.DERIVATIVES;
                break;
            case Constants.COMPONENTS:
                oppositeType = Constants.COMPOSITES;
                break;
            case Constants.COMPOSITES:
                oppositeType = Constants.COMPONENTS;
                break;
            }
            Node janus = new Node(null, asset, oppositeType);
            boolean result = searchInHierarchy(janus, id) != null;
            janus.dispose();
            return result;
        }

        private Asset searchInHierarchy(String id) {
            Node root = this;
            while (root.parent != null)
                root = root.parent;
            return searchInHierarchy(root, id);
        }

        private Asset searchInHierarchy(Node node, String id) {
            Asset a = node.getAsset();
            if (a != null && a.getStringId().equals(id))
                return a;
            for (Node child : node.getChildren()) {
                a = searchInHierarchy(child, id);
                if (a != null)
                    return a;
            }
            return null;
        }
    }

    public final static String ID_DERIVATIVES = "com.bdaum.zoom.ui.views.HierarchyViewDerivatives"; //$NON-NLS-1$
    public final static String ID_ORIGINALS = "com.bdaum.zoom.ui.views.HierarchyViewOriginals"; //$NON-NLS-1$
    public final static String ID_COMPOSITES = "com.bdaum.zoom.ui.views.HierarchyViewComposites"; //$NON-NLS-1$
    public final static String ID_COMPONENTS = "com.bdaum.zoom.ui.views.HierarchyViewComponents"; //$NON-NLS-1$

    public final static String[] IDS = new String[] { ID_DERIVATIVES, ID_ORIGINALS, ID_COMPOSITES, ID_COMPONENTS };

    public class ViewLabelProvider extends StyledCellLabelProvider {

        @Override
        public void update(ViewerCell cell) {
            Object element = cell.getElement();
            List<StyleRange> styles = new ArrayList<StyleRange>(4);
            Image image = getImage(element);
            cell.setImage(image);
            int w = image == null ? 50 : image.getBounds().width / 6;
            GC gc = new GC(cell.getControl());
            int bw = gc.textExtent(" ").x; //$NON-NLS-1$
            gc.dispose();
            int indent = (w + bw - 1) / bw;
            cell.setText(getText(element, styles, indent));
            cell.setStyleRanges(styles.toArray(new StyleRange[styles.size()]));
        }

        private String getText(Object element, List<StyleRange> styles, int indent) {
            if (element instanceof Node) {
                Node node = (Node) element;
                Asset asset = node.getAsset();
                StringBuilder sb = new StringBuilder();
                insertIndent(sb, indent);
                sb.append(UiUtilities.computeImageCaption(asset, null, null, null, null));
                int uriLength = sb.length();
                Date lastModification = asset == null ? null : asset.getLastModification();
                if (lastModification != null) {
                    insertIndent(sb, indent + 3);
                    sb.append(Messages.getString("HierarchyView.modified")).append(df.format(lastModification)); //$NON-NLS-1$
                }
                styles.add(new StyleRange(0, uriLength, viewer.getControl().getForeground(), null, SWT.BOLD));
                IIdentifiableObject description = node.getDescription();
                if (description != null) {
                    sb.append('\n').append(createDescription(description, indent + 3));
                    Color fg = isDescriptionValid(description) ? getSite().getShell().getForeground()
                            : getSite().getShell().getDisplay().getSystemColor(SWT.COLOR_RED);
                    styles.add(new StyleRange(uriLength, sb.length() - uriLength, fg, null));
                }
                return sb.toString();
            }
            return String.valueOf(element);
        }

        private Image getImage(Object element) {
            if (element instanceof Node)
                return ((Node) element).getThumbnail();
            return null;
        }

        private String createDescription(IIdentifiableObject obj, int ind) {
            StringBuilder sb = new StringBuilder();
            String kind = null;
            String tool = null;
            String recipe = null;
            String parmFile = null;
            boolean archived = false;
            if (obj instanceof ComposedToImpl) {
                ComposedToImpl rel = (ComposedToImpl) obj;
                kind = rel.getType();
                tool = rel.getTool();
                recipe = rel.getRecipe();
                parmFile = rel.getParameterFile();
            } else if (obj instanceof DerivedByImpl) {
                DerivedByImpl rel = (DerivedByImpl) obj;
                tool = rel.getTool();
                recipe = rel.getRecipe();
                parmFile = rel.getParameterFile();
                archived = rel.getArchivedRecipe() != null;
            }
            if (kind != null && !kind.isEmpty()) {
                insertIndent(sb, ind);
                sb.append(kind);
            }
            if (tool != null && !tool.isEmpty()) {
                insertIndent(sb, ind);
                sb.append(Messages.getString("HierarchyView.tool")).append(tool); //$NON-NLS-1$
            }
            if (parmFile != null && !parmFile.isEmpty()) {
                File file = new File(parmFile);
                insertIndent(sb, ind);
                sb.append(Messages.getString("HierarchyView.parameter_file")).append(file.getName()); //$NON-NLS-1$
                if (archived)
                    sb.append(Messages.getString("HierarchyView.archived")); //$NON-NLS-1$
            }
            if (recipe != null && !recipe.isEmpty()) {
                insertIndent(sb, ind);
                sb.append(Messages.getString("HierarchyView.recipe")).append(recipe); //$NON-NLS-1$
            }
            return sb.toString();
        }

    }

    private static class ViewContentProvider implements ITreeContentProvider {

        public Object getParent(Object element) {
            if (element instanceof Node)
                return ((Node) element).getParent();
            return null;
        }

        public void dispose() {
            // do nothing
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            // do nothing
        }

        public Object[] getChildren(Object parentElement) {
            if (parentElement instanceof Node)
                return ((Node) parentElement).getChildren().toArray();
            return new Object[0];
        }

        public boolean hasChildren(Object element) {
            return !((Node) element).getChildren().isEmpty();
        }

        public Object[] getElements(Object inputElement) {
            if (inputElement instanceof Node[])
                return (Node[]) inputElement;
            return new Object[0];
        }
    }

    private static final TextTransfer textTransfer = TextTransfer.getInstance();
    private static final Transfer[] transferTypes = new Transfer[] { textTransfer };
    private final static String[] types = new String[] { "derivatives", //$NON-NLS-1$
            "originals", "composites", "components" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    private final static int TYPE_DERIVATIVE = 0;
    private final static int TYPE_ORIGINAL = 1;
    private final static int TYPE_COMPOSITES = 2;
    private final static int TYPE_COMPONENTS = 3;
    private static final int OPERATIONS = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK;
    private static final SimpleDateFormat df = new SimpleDateFormat(
            Messages.getString("HierarchyView.modified_date_format")); //$NON-NLS-1$
    @SuppressWarnings("unused")
    private static final String ID = "com.bdaum.zoom.hierarchy"; //$NON-NLS-1$

    private TreeViewer viewer;
    private int type = -1;
    protected Clipboard clipboard;
    protected Node[] rootNode;
    private IAction unlinkAction;
    private IAction descriptionAction;
    private IAction showPossibleOriginalsAction;
    private IAction showPossibleDerivativesAction;
    private IAction showAssetAction;
    protected boolean cntrlDwn;

    public void insertIndent(StringBuilder sb, int indent) {
        if (sb.length() > 0)
            sb.append('\n');
        for (int i = 0; i < indent; i++)
            sb.append(' ');
    }

    public boolean isDescriptionValid(IIdentifiableObject description) {
        String parmFile = null;
        if (description instanceof ComposedToImpl)
            parmFile = ((ComposedToImpl) description).getParameterFile();
        else if (description instanceof DerivedByImpl)
            parmFile = ((DerivedByImpl) description).getParameterFile();
        if (parmFile != null && !parmFile.isEmpty()) {
            File file;
            try {
                file = new File(new URI(parmFile));
            } catch (URISyntaxException e) {
                file = new File(parmFile);
            }
            return file.exists();
        }
        return true;
    }

    @Override
    public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
        super.setInitializationData(cfig, propertyName, data);
        if (data instanceof String)
            for (int i = 0; i < types.length; i++)
                if (types[i].equals(data)) {
                    type = i;
                    break;
                }
    }

    @Override
    public void createPartControl(Composite parent) {
        viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);
        viewer.setContentProvider(new ViewContentProvider());
        viewer.setLabelProvider(new ViewLabelProvider());
        UiUtilities.installDoubleClickExpansion(viewer);
        getControl().addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.CTRL)
                    cntrlDwn = true;
            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.keyCode == SWT.CTRL)
                    cntrlDwn = false;
            }
        });
        viewer.addSelectionChangedListener(this);
        // Create the help context id for the viewer's control
        PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), HelpContextIds.HIERARCHY_VIEW);
        // Drag & Drop
        viewer.addDropSupport(OPERATIONS, transferTypes, new ViewerDropAdapter(viewer) {
            @Override
            public void dragEnter(DropTargetEvent event) {
                super.dragEnter(event);
                UiActivator.getDefault().setDropTargetEffect(event, viewer.getControl());
            }

            @Override
            public void dragLeave(DropTargetEvent event) {
                UiActivator.getDefault().hideDropTargetEffect();
                super.dragLeave(event);
            }

            @Override
            public void dragOver(DropTargetEvent event) {
                super.dragOver(event);
            }

            @Override
            public boolean validateDrop(Object target, int operation, TransferData transferType) {
                return (textTransfer.isSupportedType(transferType) && (operation & OPERATIONS) != 0);
            }

            @Override
            public boolean performDrop(Object data) {
                if (data instanceof String) {
                    List<String> list = Core.fromStringList((String) data, "\n"); //$NON-NLS-1$
                    Object target = getCurrentTarget();
                    if (target == null && !list.isEmpty())
                        setInput(CoreActivator.getDefault().getDbManager().obtainAsset(list.get(0)));
                    else if (target instanceof Node) {
                        String[] ids = list.toArray(new String[list.size()]);
                        String errorMsg = ((Node) target).acceptChildren(ids);
                        if (errorMsg != null) {
                            AcousticMessageDialog.openError(getSite().getShell(),
                                    Messages.getString("HierarchyView.drag_error"), errorMsg); //$NON-NLS-1$
                            return false;
                        }
                    }
                }
                return true;
            }
        });
        // Listeners
        addKeyListener();
        addGestureListener(viewer.getTree());
        addExplanationListener(true);
        addPartListener();
        addClipboard(viewer.getControl());
        addDragSupport();
        // Other actions
        makeActions(getViewSite().getActionBars());
        installListeners();
        hookContextMenu();
        contributeToActionBars();
        // Hover
        installHoveringController();
        // Action state
        updateActions(false);
    }

    protected void addClipboard(Control control) {
        clipboard = new Clipboard(control.getDisplay());
    }

    @Override
    protected void fillLocalPullDown(IMenuManager manager) {
        manager.add(viewImageAction);
        manager.add(new Separator());
        manager.add(editAction);
        manager.add(editWithAction);
        manager.add(new Separator(IZoomActionConstants.MB_HIERARCHY));
        switch (type) {
        case TYPE_DERIVATIVE:
            manager.add(showOriginalAction);
            break;
        case TYPE_ORIGINAL:
            manager.add(showDerivativesAction);
            break;
        case TYPE_COMPOSITES:
            manager.add(showComponentsAction);
            break;
        case TYPE_COMPONENTS:
            manager.add(showCompositesAction);
            break;
        }
        manager.add(showAssetAction);
        manager.add(addBookmarkAction);
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    @Override
    protected void fillContextMenu(IMenuManager manager) {
        updateActions(true);
        boolean readOnly = dbIsReadonly();
        manager.add(descriptionAction);
        if (!readOnly)
            manager.add(unlinkAction);
        switch (type) {
        case Constants.ORIGINALS:
            manager.add(showPossibleOriginalsAction);
            break;
        case Constants.DERIVATIVES:
            manager.add(showPossibleDerivativesAction);
            break;
        }
        manager.add(new Separator(IZoomActionConstants.MB_LINK));
        fillEditAndSearchGroup(manager, readOnly);
        fillVoiceNote(manager, readOnly);
        fillMetaData(manager, readOnly);
        manager.add(new Separator(IZoomActionConstants.MB_SUBMENUS));
        fillRotateGroup(manager, readOnly);
        fillShowAndDeleteGroup(manager, readOnly);
        super.fillAdditions(manager);
    }

    @Override
    protected void fillLocalToolBar(IToolBarManager manager) {
        manager.add(viewImageAction);
        manager.add(new Separator());
        manager.add(editAction);
        manager.add(editWithAction);
        manager.add(new Separator(IZoomActionConstants.MB_HIERARCHY));
        switch (type) {
        case TYPE_DERIVATIVE:
            manager.add(showOriginalAction);
            break;
        case TYPE_ORIGINAL:
            manager.add(showDerivativesAction);
            break;
        case TYPE_COMPOSITES:
            manager.add(showComponentsAction);
            break;
        case TYPE_COMPONENTS:
            manager.add(showCompositesAction);
            break;
        }
        manager.add(descriptionAction);
        manager.add(unlinkAction);
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    protected void addPartListener() {
        getSite().getWorkbenchWindow().getPartService().addPartListener(new IPartListener() {
            public void partOpened(IWorkbenchPart part) {
                updateView();
            }

            public void partDeactivated(IWorkbenchPart part) {
                // do nothing
            }

            public void partClosed(IWorkbenchPart part) {
                updateView();
            }

            public void partBroughtToTop(IWorkbenchPart part) {
                updateView();
            }

            public void partActivated(IWorkbenchPart part) {
                // do nothing
            }
        });
    }

    @Override
    protected void makeActions(IActionBars bars) {
        super.makeActions(bars);
        showPossibleOriginalsAction = new Action(Messages.getString("HierarchyView.show_candiates_for_originals")) { //$NON-NLS-1$
            @Override
            public void run() {
                Object firstElement = ((IStructuredSelection) viewer.getSelection()).getFirstElement();
                if (firstElement instanceof Node)
                    showCandidates((Node) firstElement, true);
            }
        };
        showPossibleOriginalsAction
                .setToolTipText(Messages.getString("HierarchyView.show_candidates_originals_tooltip")); //$NON-NLS-1$
        showPossibleDerivativesAction = new Action(
                Messages.getString("HierarchyView.show_candidates_for_derivatives")) { //$NON-NLS-1$
            @Override
            public void run() {
                Object firstElement = ((IStructuredSelection) viewer.getSelection()).getFirstElement();
                if (firstElement instanceof Node)
                    showCandidates((Node) firstElement, false);
            }
        };
        showPossibleDerivativesAction
                .setToolTipText(Messages.getString("HierarchyView.show_candidates_derivatives_tooltip")); //$NON-NLS-1$
        showAssetAction = new Action(Messages.getString("HierarchyView.show_hierarchy_for_selected_asset")) { //$NON-NLS-1$
            @Override
            public void run() {
                setInput(getNavigationHistory().getSelectedAssets().get(0));
            }
        };
        showAssetAction.setToolTipText(Messages.getString("HierarchyView.show_hierarchy_tooltip")); //$NON-NLS-1$
        showDerivativesAction = addAction(ZoomActionFactory.SHOWDERIVATIVES.create(bars, this));
        showOriginalAction = addAction(ZoomActionFactory.SHOWORIGINALS.create(bars, this));
        showCompositesAction = ZoomActionFactory.SHOWCOMPOSITES.create(bars, this);
        showComponentsAction = addAction(ZoomActionFactory.SHOWCOMPONENTS.create(bars, this));
        unlinkAction = new Action(Messages.getString("HierarchyView.unlink"), Icons.unlink.getDescriptor()) { //$NON-NLS-1$
            @Override
            public void run() {
                Object firstElement = ((IStructuredSelection) viewer.getSelection()).getFirstElement();
                if (firstElement instanceof Node) {
                    Node node = (Node) firstElement;
                    Node parent = node.getParent();
                    if (parent != null) {
                        Asset a = parent.getAsset();
                        if (a != null)
                            OperationJob.executeOperation(
                                    new UnlinkOperation(type, a.getStringId(), node.getAsset().getStringId()),
                                    HierarchyView.this);
                    }
                }
            }
        };
        unlinkAction.setToolTipText(Messages.getString("HierarchyView.remove_from_hierarchy")); //$NON-NLS-1$
        descriptionAction = new Action(Messages.getString("HierarchyView.edit_description"), //$NON-NLS-1$
                Icons.descriptionEdit.getDescriptor()) {
            @Override
            public void run() {
                Object firstElement = ((IStructuredSelection) viewer.getSelection()).getFirstElement();
                if (firstElement instanceof Node) {
                    Node node = (Node) firstElement;
                    IIdentifiableObject description = node.getDescription();
                    DescriptionDialog dialog = new DescriptionDialog(getSite().getShell(), description,
                            node.getAsset());
                    if (dialog.open() == Window.OK) {
                        RelationDescription newValues = dialog.getResult();
                        boolean changed = false;
                        String kind = ""; //$NON-NLS-1$
                        String tool = ""; //$NON-NLS-1$
                        String recipe = ""; //$NON-NLS-1$
                        String parmFile = ""; //$NON-NLS-1$
                        Date createdAt;
                        if (description instanceof ComposedToImpl) {
                            ComposedToImpl rel = (ComposedToImpl) description;
                            if (rel.getType() != null)
                                kind = rel.getType();
                            if (rel.getTool() != null)
                                tool = rel.getTool();
                            if (rel.getRecipe() != null)
                                recipe = rel.getRecipe();
                            if (rel.getParameterFile() != null)
                                parmFile = rel.getParameterFile();
                            createdAt = rel.getDate();
                            changed = kind.equals(newValues.kind) || tool.equals(newValues.tool)
                                    || recipe.equals(newValues.recipe) || parmFile.equals(newValues.parameterFile)
                                    || createdAt == null || createdAt.equals(newValues.createdAt);
                        } else if (description instanceof DerivedByImpl) {
                            DerivedByImpl rel = (DerivedByImpl) description;
                            if (rel.getTool() != null)
                                tool = rel.getTool();
                            if (rel.getRecipe() != null)
                                recipe = rel.getRecipe();
                            if (rel.getParameterFile() != null)
                                parmFile = rel.getParameterFile();
                            createdAt = rel.getDate();
                            changed = tool.equals(newValues.tool) || recipe.equals(newValues.recipe)
                                    || parmFile.equals(newValues.parameterFile) || createdAt == null
                                    || createdAt.equals(newValues.createdAt);
                        }
                        if (changed)
                            OperationJob.executeOperation(new ModifyRelationLegendOperation(description, newValues),
                                    HierarchyView.this);
                    }
                }
            }
        };
        descriptionAction.setToolTipText(Messages.getString("HierarchyView.edit_link_description")); //$NON-NLS-1$
    }

    protected void showCandidates(Node node, boolean originals) {
        Asset asset = node.getAsset();
        if (asset != null) {
            SmartCollectionImpl collection = new SmartCollectionImpl(
                    NLS.bind(originals ? Messages.getString("HierarchyView.possible_originals") //$NON-NLS-1$
                            : Messages.getString("HierarchyView.possible_derivatives"), //$NON-NLS-1$
                            asset.getName()),
                    false, false, true, false, null, 0, null, 0, null, Constants.INHERIT_LABEL, null, 0,
                    new HierarchyPostProcessor(node));
            collection.addCriterion(new CriterionImpl(QueryField.EXIF_ORIGINALFILENAME.getKey(), null,
                    asset.getOriginalFileName(), QueryField.EQUALS, true));
            collection.addCriterion(new CriterionImpl(QueryField.LASTMOD.getKey(), null,
                    asset.getLastModification(), originals ? QueryField.SMALLER : QueryField.GREATER, true));
            collection.addSortCriterion(new SortCriterionImpl(QueryField.LASTMOD.getKey(), null, originals));
            Ui.getUi().getNavigationHistory(getSite().getWorkbenchWindow())
                    .postSelection(new StructuredSelection(collection));
        }
    }

    @Override
    public void assetsModified(BagChange<Asset> changes, QueryField node) {
        if (node == null)
            updateView();
    }

    @Override
    public void hierarchyModified() {
        updateView();
    }

    protected void updateView() {
        if (rootNode != null && rootNode.length > 0)
            setInput(rootNode[0].getAsset());
    }

    @Override
    public void setFocus() {
        viewer.getControl().setFocus();
    }

    public void setInput(final Asset asset) {
        Shell shell = getSite().getShell();
        if (!shell.isDisposed())
            shell.getDisplay().asyncExec(() -> {
                if (!shell.isDisposed()) {
                    if (rootNode != null)
                        for (Node node : rootNode)
                            node.dispose();
                    if (!viewer.getControl().isDisposed()) {
                        rootNode = new Node[] { new Node(null, asset, type) };
                        viewer.setInput(rootNode);
                        viewer.expandAll();
                    }
                }
            });
    }

    public void selectionChanged(SelectionChangedEvent event) {
        updateActions(false);
        IStructuredSelection selection = (IStructuredSelection) event.getSelection();
        if (cntrlDwn) {
            Object item = selection.getFirstElement();
            if (item instanceof Node && descriptionAction.isEnabled())
                descriptionAction.run();
            cntrlDwn = false;
        }
        fireSelectionChanged(new SelectionChangedEvent(this, nodeToAssetSelection(selection)));
    }

    private static AssetSelection nodeToAssetSelection(IStructuredSelection selection) {
        AssetSelection assetSelection = new AssetSelection(selection.size());
        Iterator<?> iterator = selection.iterator();
        while (iterator.hasNext()) {
            Object object = iterator.next();
            if (object instanceof Node) {
                Asset a = ((Node) object).getAsset();
                if (a != null)
                    assetSelection.add(a);
            }
        }
        return assetSelection;
    }

    @Override
    public void updateActions(boolean force) {
        if (showPossibleDerivativesAction != null && (isVisible() || force) && !viewer.getControl().isDisposed()) {
            boolean writable = !dbIsReadonly();
            super.updateActions(force);
            boolean enabled = false;
            boolean selected = false;
            Object firstElement = ((IStructuredSelection) viewer.getSelection()).getFirstElement();
            if (firstElement instanceof Node) {
                selected = true;
                enabled = (((Node) firstElement).getParent() != null);
            }
            showPossibleDerivativesAction.setEnabled(selected);
            showPossibleOriginalsAction.setEnabled(selected);
            unlinkAction.setEnabled(enabled && writable);
            descriptionAction.setEnabled(enabled);
            showComponentsAction.setEnabled(enabled);
            showCompositesAction.setEnabled(enabled);
            showDerivativesAction.setEnabled(enabled);
            showOriginalAction.setEnabled(enabled);
            showAssetAction.setEnabled(getNavigationHistory().getSelectedAssets().size() == 1);
        }
    }

    private void fireSelectionChanged(SelectionChangedEvent event) {
        for (Object listener : listeners.getListeners())
            ((ISelectionChangedListener) listener).selectionChanged(event);
    }

    public void setSelection(ISelection selection) {
        // do nothing
    }

    public ISelection getSelection() {
        return getAssetSelection();
    }

    public Object findObject(MouseEvent e) {
        return findObject(e.x, e.y);
    }

    public Object findObject(int x, int y) {
        ViewerCell cell = viewer.getCell(new Point(x, y));
        Node node = (Node) ((cell == null) ? null : cell.getElement());
        return (node == null) ? null : node.getAsset();
    }

    public AssetSelection getAssetSelection() {
        return nodeToAssetSelection((IStructuredSelection) viewer.getSelection());
    }

    @Override
    protected int getSelectionCount(boolean local) {
        return ((IStructuredSelection) viewer.getSelection()).size();
    }

    public Control getControl() {
        return viewer.getControl();
    }

    @Override
    public void refresh() {
        updateView();
    }

    @Override
    public boolean assetsChanged() {
        updateActions(false);
        return false;
    }

    @Override
    public boolean collectionChanged() {
        return false;
    }

    @Override
    public boolean selectionChanged() {
        return false;
    }

    @Override
    public Object getContent() {
        if (rootNode != null && rootNode.length == 1)
            return rootNode[0].getAsset();
        return super.getContent();
    }

    public boolean cursorOverImage(int x, int y) {
        return true;
    }

    public IAssetProvider getAssetProvider() {
        return null;
    }

    public ImageRegion findBestFaceRegion(int x, int y, boolean all) {
        return null;
    }

}