org.eclipse.team.svn.ui.repository.RepositoryTreeViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.team.svn.ui.repository.RepositoryTreeViewer.java

Source

/*******************************************************************************
 * Copyright (c) 2005-2008 Polarion Software.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Alexander Gurov - Initial API and implementation
 *******************************************************************************/

package org.eclipse.team.svn.ui.repository;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.util.TransferDragSourceListener;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.team.svn.core.connector.SVNRevision;
import org.eclipse.team.svn.core.operation.CompositeOperation;
import org.eclipse.team.svn.core.operation.IActionOperation;
import org.eclipse.team.svn.core.operation.remote.AbstractCopyMoveResourcesOperation;
import org.eclipse.team.svn.core.operation.remote.CopyResourcesOperation;
import org.eclipse.team.svn.core.operation.remote.MoveResourcesOperation;
import org.eclipse.team.svn.core.resource.IRepositoryResource;
import org.eclipse.team.svn.core.utility.ProgressMonitorUtility;
import org.eclipse.team.svn.core.utility.SVNUtility;
import org.eclipse.team.svn.ui.RemoteResourceTransfer;
import org.eclipse.team.svn.ui.RemoteResourceTransferrable;
import org.eclipse.team.svn.ui.SVNUIMessages;
import org.eclipse.team.svn.ui.dialog.DefaultDialog;
import org.eclipse.team.svn.ui.operation.RefreshRemoteResourcesOperation;
import org.eclipse.team.svn.ui.panel.common.CommentPanel;
import org.eclipse.team.svn.ui.repository.model.IDataTreeNode;
import org.eclipse.team.svn.ui.repository.model.IParentTreeNode;
import org.eclipse.team.svn.ui.repository.model.IToolTipProvider;
import org.eclipse.team.svn.ui.repository.model.RepositoryBranches;
import org.eclipse.team.svn.ui.repository.model.RepositoryFile;
import org.eclipse.team.svn.ui.repository.model.RepositoryFolder;
import org.eclipse.team.svn.ui.repository.model.RepositoryResource;
import org.eclipse.team.svn.ui.repository.model.RepositoryRevision;
import org.eclipse.team.svn.ui.repository.model.RepositoryRoot;
import org.eclipse.team.svn.ui.repository.model.RepositoryTags;
import org.eclipse.team.svn.ui.repository.model.RepositoryTrunk;
import org.eclipse.team.svn.ui.repository.model.ToolTipVariableSetProvider;
import org.eclipse.team.svn.ui.utility.UIMonitorUtility;

/**
 * Repository TreeViewer implementation
 * 
 * @author Alexander Gurov
 */
public class RepositoryTreeViewer extends TreeViewer {

    public static final String FMT_REPOSITORY_RESOURCE = "{" + ToolTipVariableSetProvider.NAME_OF_NAME + "}" + //$NON-NLS-1$ //$NON-NLS-2$
            "{" + ToolTipVariableSetProvider.NAME_OF_LAST_CHANGE_DATE + "}" + //$NON-NLS-1$ //$NON-NLS-2$
            "{" + ToolTipVariableSetProvider.NAME_OF_LAST_AUTHOR + "}"; //$NON-NLS-1$ //$NON-NLS-2$
    public static final String FMT_REPOSITORY_FILE = RepositoryTreeViewer.FMT_REPOSITORY_RESOURCE + "{" //$NON-NLS-1$
            + ToolTipVariableSetProvider.NAME_OF_SIZE + "}" + //$NON-NLS-1$
            "{" + ToolTipVariableSetProvider.NAME_OF_LOCK_OWNER + "}" + //$NON-NLS-1$ //$NON-NLS-2$
            "{" + ToolTipVariableSetProvider.NAME_OF_LOCK_CREATION_DATE + "}" + //$NON-NLS-1$ //$NON-NLS-2$
            "{" + ToolTipVariableSetProvider.NAME_OF_LOCK_EXPIRATION_DATE + "}" + //$NON-NLS-1$ //$NON-NLS-2$
            "{" + ToolTipVariableSetProvider.NAME_OF_LOCK_COMMENT + "}"; //$NON-NLS-1$ //$NON-NLS-2$
    public static final String FMT_REPOSITORY_FOLDER = RepositoryTreeViewer.FMT_REPOSITORY_RESOURCE;
    public static final String FMT_REPOSITORY_BRANCHES = RepositoryTreeViewer.FMT_REPOSITORY_FOLDER;
    public static final String FMT_REPOSITORY_ROOT = RepositoryTreeViewer.FMT_REPOSITORY_FOLDER;
    public static final String FMT_REPOSITORY_TAGS = RepositoryTreeViewer.FMT_REPOSITORY_FOLDER;
    public static final String FMT_REPOSITORY_TRUNK = RepositoryTreeViewer.FMT_REPOSITORY_FOLDER;

    private static final Map<Class<?>, String> class2Format = new HashMap<Class<?>, String>();

    static {
        RepositoryTreeViewer.class2Format.put(RepositoryResource.class,
                RepositoryTreeViewer.FMT_REPOSITORY_RESOURCE);
        RepositoryTreeViewer.class2Format.put(RepositoryFile.class, RepositoryTreeViewer.FMT_REPOSITORY_FILE);
        RepositoryTreeViewer.class2Format.put(RepositoryFolder.class, RepositoryTreeViewer.FMT_REPOSITORY_FOLDER);
        RepositoryTreeViewer.class2Format.put(RepositoryBranches.class,
                RepositoryTreeViewer.FMT_REPOSITORY_BRANCHES);
        RepositoryTreeViewer.class2Format.put(RepositoryRoot.class, RepositoryTreeViewer.FMT_REPOSITORY_ROOT);
        RepositoryTreeViewer.class2Format.put(RepositoryTags.class, RepositoryTreeViewer.FMT_REPOSITORY_TAGS);
        RepositoryTreeViewer.class2Format.put(RepositoryTrunk.class, RepositoryTreeViewer.FMT_REPOSITORY_TRUNK);
        RepositoryTreeViewer.class2Format.put(RepositoryRevision.class, "");
    }

    public static interface IRefreshVisitor {
        public void visit(Object data);
    }

    public static interface IRefreshListener {
        public void refreshed(Object element);
    }

    protected List<IRefreshListener> refreshListeners = new ArrayList<IRefreshListener>();

    public RepositoryTreeViewer(Composite parent) {
        super(parent);
        this.initialize();
    }

    public RepositoryTreeViewer(Composite parent, int style) {
        super(parent, style);
        this.initialize();
    }

    public RepositoryTreeViewer(Tree tree) {
        super(tree);
        this.initialize();
    }

    public synchronized void addRefreshListener(IRefreshListener listener) {
        if (!this.refreshListeners.contains(listener)) {
            this.refreshListeners.add(listener);
        }
    }

    public synchronized void removeRefreshListener(IRefreshListener listener) {
        this.refreshListeners.remove(listener);
    }

    public void setExpandedState(Object element, boolean expanded) {
        TreeItem[] items = this.getIdenticalNodes(element, true);
        if (items != null && items.length > 0) {
            if (expanded) {
                createChildren(items[0]);
            }
            this.setExpanded(items[0], expanded);
        } else {
            TreeItem[] nodes = this.getIdenticalNodes(element, false);
            if (nodes != null && nodes.length > 0) {
                super.setExpandedState(nodes[0].getData(), expanded);
            }
        }
    }

    public void setSelection(ISelection selection) {
        if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
            IStructuredSelection tmp = (IStructuredSelection) selection;
            TreeItem[] nodes = this.getIdenticalNodes(tmp.getFirstElement(), false);
            if (nodes != null && nodes.length > 0) {
                selection = new StructuredSelection(nodes[0].getData());
            }
        }
        super.setSelection(selection);
    }

    public void refresh(final Object element, final IRefreshVisitor visitor, final boolean exact) {
        this.getControl().getDisplay().syncExec(new Runnable() {
            public void run() {
                TreeItem[] nodes = RepositoryTreeViewer.this.getIdenticalNodes(element, exact);
                if (nodes != null && nodes.length != 0) {
                    for (int i = 0; i < nodes.length; i++) {
                        Object data = nodes[i].getData();
                        if (visitor != null) {
                            visitor.visit(data);
                        }
                        RepositoryTreeViewer.this.internalRefresh(nodes[i], data, true, true);
                        RepositoryTreeViewer.this.fireRefresh(data);
                    }
                } else {
                    Object input = RepositoryTreeViewer.this.getInput();
                    if (input instanceof IDataTreeNode) {
                        Object data = ((IDataTreeNode) input).getData();
                        if (data != null && data.equals(element) && visitor != null) {
                            visitor.visit(input);
                        }
                    }
                    RepositoryTreeViewer.super.refresh(null);
                }
            }
        });
    }

    public void fireEmptySelectionEvent() {
        this.fireSelectionChanged(new SelectionChangedEvent(this, StructuredSelection.EMPTY));
    }

    // fix the problem with refresh of identical nodes in the tree
    protected void internalRefresh(Widget widget, Object element, boolean doStruct, boolean updateLabels) {
        if (widget instanceof Item) {
            if (doStruct) {
                this.updatePlus((Item) widget, element);
            }
            if (updateLabels || !this.equals(element, widget.getData())) {
                this.doUpdateItem(widget, element, true);
            } else {
                this.associate(element, (Item) widget);
            }
        }

        if (doStruct) {
            this.internalRefreshStruct(widget, element, updateLabels);
        } else {
            Item[] children = this.getChildren(widget);
            if (children != null) {
                for (int i = 0; i < children.length; i++) {
                    Widget item = children[i];
                    Object data = item.getData();
                    if (data != null) {
                        this.internalRefresh(item, data, doStruct, updateLabels);
                    }
                }
            }
        }
    }

    protected void internalRefreshStruct(Widget widget, Object element, boolean updateLabels) {
        //      this.updateChildren(widget, element, null);
        try {
            //updateChildren(Widget widget, Object parent, Object[] elementChildren, boolean updateLabels)
            Method m = AbstractTreeViewer.class.getDeclaredMethod("updateChildren", //$NON-NLS-1$
                    new Class[] { Widget.class, Object.class, Object[].class, boolean.class });
            m.setAccessible(true);
            m.invoke(this, new Object[] { widget, element, null, Boolean.valueOf(updateLabels) });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        Item[] children = getChildren(widget);
        if (children != null) {
            for (int i = 0; i < children.length; i++) {
                Widget item = children[i];
                Object data = item.getData();
                if (data != null) {
                    this.internalRefreshStruct(item, data, updateLabels);
                }
            }
        }
    }

    public void refresh() {
        this.getControl().getDisplay().syncExec(new Runnable() {
            public void run() {
                RepositoryTreeViewer.super.refresh();
            }
        });
    }

    public TreeItem[] getIdenticalNodes(Object sample, boolean exact) {
        if (sample != null) {
            return this.findUnfreshNodes(this.getTree().getItems(), sample, exact);
        }
        return null;
    }

    protected synchronized void fireRefresh(Object data) {
        Object[] listeners = this.refreshListeners.toArray();
        for (int i = 0; i < listeners.length; i++) {
            ((IRefreshListener) listeners[i]).refreshed(data);
        }
    }

    protected TreeItem[] findUnfreshNodes(TreeItem[] items, Object obj, boolean exact) {
        List<TreeItem> retVal = this.findUnfreshNodesImpl(items, obj, exact);
        return retVal == null ? null : (TreeItem[]) retVal.toArray(new TreeItem[retVal.size()]);
    }

    protected List<TreeItem> findUnfreshNodes(TreeItem item, Object obj, boolean exact) {
        Object data = item.getData();
        if (obj == data || !exact && obj.equals(data)) {
            return Arrays.asList(new TreeItem[] { item });
        }
        if (data instanceof IDataTreeNode) {
            IDataTreeNode dataNode = (IDataTreeNode) data;
            if (obj == dataNode.getData() || !exact && obj.equals(dataNode.getData())) {
                return Arrays.asList(new TreeItem[] { item });
            }
        }
        return this.findUnfreshNodesImpl(item.getItems(), obj, exact);
    }

    protected List<TreeItem> findUnfreshNodesImpl(TreeItem[] items, Object obj, boolean exact) {
        if (items != null) {
            List<TreeItem> retVal = new ArrayList<TreeItem>();
            for (int i = 0; i < items.length; i++) {
                List<TreeItem> tmp = this.findUnfreshNodes(items[i], obj, exact);
                if (tmp != null) {
                    retVal.addAll(tmp);
                }
            }
            return retVal;
        }
        return null;
    }

    protected void handleDoubleClick(IStructuredSelection selection) {
        Object node = selection.getFirstElement();
        if (node instanceof IParentTreeNode) {
            TreeItem[] items = this.getIdenticalNodes(node, true);
            if (items != null && items.length > 0) {
                boolean expanded = !this.getExpanded(items[0]);
                if (expanded) {
                    createChildren(items[0]);
                }
                this.setExpanded(items[0], expanded);
            }
        }
    }

    private void initialize() {
        this.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent e) {
                ISelection selection = e.getSelection();
                if (selection instanceof IStructuredSelection) {
                    IStructuredSelection structured = (IStructuredSelection) selection;
                    if (structured.size() == 1) {
                        RepositoryTreeViewer.this.handleDoubleClick(structured);
                    }
                }
            }
        });

        this.addDragSupport(DND.DROP_COPY | DND.DROP_NONE | DND.DROP_MOVE | DND.DROP_LINK,
                new Transfer[] { RemoteResourceTransfer.getInstance() }, new TransferDragSourceListener() {

                    public void dragFinished(DragSourceEvent event) {
                    }

                    public void dragSetData(DragSourceEvent event) {
                        if (RemoteResourceTransfer.getInstance().isSupportedType(event.dataType)) {
                            IStructuredSelection selection = (IStructuredSelection) RepositoryTreeViewer.this
                                    .getSelection();
                            ArrayList<IRepositoryResource> resources = new ArrayList<IRepositoryResource>();
                            for (Iterator<?> it = selection.iterator(); it.hasNext();) {
                                resources.add(((RepositoryResource) it.next()).getRepositoryResource());
                            }
                            event.data = new RemoteResourceTransferrable(
                                    resources.toArray(new IRepositoryResource[0]), 0);
                        }
                    }

                    public void dragStart(DragSourceEvent event) {
                        IStructuredSelection selection = (IStructuredSelection) RepositoryTreeViewer.this
                                .getSelection();
                        boolean canBeDragged = selection.size() > 0;
                        for (Iterator<?> it = selection.iterator(); it.hasNext();) {
                            if (!(it.next() instanceof RepositoryResource)) {
                                canBeDragged = false;
                            }
                        }
                        event.doit = canBeDragged;
                    }

                    public Transfer getTransfer() {
                        return RemoteResourceTransfer.getInstance();
                    }

                });

        this.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE, new Transfer[] { RemoteResourceTransfer.getInstance() },
                new DropTargetAdapter() {

                    protected int expectedOperation = DND.DROP_MOVE;

                    public void dragOperationChanged(DropTargetEvent event) {
                        this.expectedOperation = event.detail;
                    }

                    public void dragEnter(DropTargetEvent event) {
                        this.expectedOperation = event.detail;
                    }

                    public void dragOver(DropTargetEvent event) {
                        Tree repositoryTree = (Tree) ((DropTarget) event.widget).getControl();
                        TreeItem aboveItem = repositoryTree.getItem(repositoryTree.toControl(event.x, event.y));
                        if (aboveItem == null) {
                            event.detail = DND.DROP_NONE;
                            return;
                        }
                        Object aboveObject = aboveItem.getData();
                        if (!(aboveObject instanceof RepositoryResource) || aboveObject instanceof RepositoryFile) {
                            event.detail = DND.DROP_NONE;
                            return;
                        }
                        RepositoryResource aboveResource = (RepositoryResource) aboveObject;
                        if (aboveResource.getRepositoryResource().getSelectedRevision() != SVNRevision.HEAD) {
                            event.detail = DND.DROP_NONE;
                            return;
                        }
                        IStructuredSelection selection = (IStructuredSelection) RepositoryTreeViewer.this
                                .getSelection();
                        int lastSlashIdx = 0;
                        for (Iterator<?> it = selection.iterator(); it.hasNext();) {
                            RepositoryResource current = (RepositoryResource) it.next();
                            if (lastSlashIdx == 0) {
                                lastSlashIdx = current.getRepositoryResource().getUrl().lastIndexOf("/"); //$NON-NLS-1$
                            }
                            if (current.getRepositoryResource().getUrl().lastIndexOf("/") != lastSlashIdx //$NON-NLS-1$
                                    || aboveResource == current || aboveResource == current.getParent()) {
                                event.detail = DND.DROP_NONE;
                                return;
                            }
                        }
                        event.detail = this.expectedOperation;
                    }

                    public void drop(DropTargetEvent event) {
                        Tree repositoryTree = (Tree) ((DropTarget) event.widget).getControl();
                        RepositoryResource aboveResource = (RepositoryResource) repositoryTree
                                .getItem(repositoryTree.toControl(event.x, event.y)).getData();
                        CommentPanel commentPanel = new CommentPanel(
                                event.detail == DND.DROP_MOVE ? SVNUIMessages.MoveToAction_Select_Title
                                        : SVNUIMessages.CopyToAction_Select_Title);
                        DefaultDialog dialog = new DefaultDialog(UIMonitorUtility.getShell(), commentPanel);
                        if (dialog.open() == IDialogConstants.OK_ID) {
                            AbstractCopyMoveResourcesOperation mainOp = event.detail == DND.DROP_MOVE
                                    ? new MoveResourcesOperation(aboveResource.getRepositoryResource(),
                                            ((RemoteResourceTransferrable) event.data).resources,
                                            commentPanel.getMessage(), null)
                                    : new CopyResourcesOperation(aboveResource.getRepositoryResource(),
                                            ((RemoteResourceTransferrable) event.data).resources,
                                            commentPanel.getMessage(), null);
                            CompositeOperation op = new CompositeOperation(mainOp.getId(),
                                    mainOp.getMessagesClass());
                            op.add(mainOp);
                            ArrayList<IRepositoryResource> toRefresh = new ArrayList<IRepositoryResource>();
                            toRefresh.add(aboveResource.getRepositoryResource());
                            if (event.detail == DND.DROP_MOVE) {
                                toRefresh.addAll(
                                        Arrays.asList(((RemoteResourceTransferrable) event.data).resources));
                            }
                            op.add(new RefreshRemoteResourcesOperation(
                                    SVNUtility.getCommonParents(toRefresh.toArray(new IRepositoryResource[0]))),
                                    new IActionOperation[] { mainOp });
                            ProgressMonitorUtility.doTaskScheduled(op);
                        }
                    }

                });

        this.getTree().addMouseTrackListener(new MouseTrackAdapter() {
            public void mouseHover(MouseEvent e) {
                String tooltipText = ""; //$NON-NLS-1$
                Tree tree = RepositoryTreeViewer.this.getTree();
                TreeItem item = tree.getItem(new Point(e.x, e.y));
                if (item != null) {
                    Object data = item.getData();
                    if (data != null) {
                        if (data instanceof IToolTipProvider) {
                            tooltipText = ((IToolTipProvider) data)
                                    .getToolTipMessage(RepositoryTreeViewer.class2Format.get(data.getClass()));
                        }
                    }
                }
                tree.setToolTipText(tooltipText);
            }

            public void mouseExit(MouseEvent e) {
                RepositoryTreeViewer.this.getTree().setToolTipText(""); //$NON-NLS-1$
            }
        });
    }

}