org.eclipse.wst.xml.ui.internal.tabletree.XMLTableTreeViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wst.xml.ui.internal.tabletree.XMLTableTreeViewer.java

Source

/*****************************************************************************
 * Copyright (c) 2004, 2011 IBM Corporation and others. 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: IBM Corporation - initial API and implementation
 ****************************************************************************/
package org.eclipse.wst.xml.ui.internal.tabletree;

import java.util.Iterator;

import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener;
import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent;
import org.eclipse.jface.viewers.ICellEditorListener;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
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.Item;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.eclipse.wst.xml.ui.internal.actions.NodeAction;
import org.eclipse.wst.xml.ui.internal.contentoutline.XMLNodeActionManager;
import org.eclipse.wst.xml.ui.internal.dnd.DragNodeCommand;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

public class XMLTableTreeViewer extends TreeViewer implements IDesignViewer {

    class NodeActionMenuListener implements IMenuListener {
        public void menuAboutToShow(IMenuManager menuManager) {
            // used to disable NodeSelection listening while running
            // NodeAction
            XMLNodeActionManager nodeActionManager = new XMLNodeActionManager(
                    ((IDOMDocument) getInput()).getModel(), XMLTableTreeViewer.this) {
                public void beginNodeAction(NodeAction action) {
                    super.beginNodeAction(action);
                }

                public void endNodeAction(NodeAction action) {
                    super.endNodeAction(action);
                }
            };
            nodeActionManager.fillContextMenu(menuManager, getSelection());
        }
    }

    private class SelectionProvider implements IPostSelectionProvider {

        public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
            XMLTableTreeViewer.this.addPostSelectionChangedListener(listener);
        }

        public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
            XMLTableTreeViewer.this.removePostSelectionChangedListener(listener);
        }

        public void addSelectionChangedListener(ISelectionChangedListener listener) {
            XMLTableTreeViewer.this.addSelectionChangedListener(listener);
        }

        public ISelection getSelection() {
            return XMLTableTreeViewer.this.getSelection();
        }

        public void removeSelectionChangedListener(ISelectionChangedListener listener) {
            XMLTableTreeViewer.this.removeSelectionChangedListener(listener);
        }

        public void setSelection(ISelection selection) {
            boolean selectionSet = false;
            if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
                IStructuredSelection structuredSelection = (IStructuredSelection) selection;
                if (selection instanceof ITextSelection) {
                    ITextSelection textSelection = (ITextSelection) selection;

                    if (structuredSelection.size() == 1) {
                        if (structuredSelection.getFirstElement() instanceof IDOMNode) {
                            IDOMNode domNode = (IDOMNode) structuredSelection.getFirstElement();
                            IStructuredDocumentRegion startStructuredDocumentRegion = domNode
                                    .getStartStructuredDocumentRegion();
                            if (startStructuredDocumentRegion != null) {
                                ITextRegion matchingRegion = startStructuredDocumentRegion
                                        .getRegionAtCharacterOffset(textSelection.getOffset());
                                int allowedIterations = 40;
                                while (matchingRegion != null
                                        && !matchingRegion.getType().equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)
                                        && allowedIterations > 0) {
                                    matchingRegion = startStructuredDocumentRegion.getRegionAtCharacterOffset(
                                            startStructuredDocumentRegion.getStartOffset(matchingRegion) - 1);
                                    allowedIterations--;
                                }
                                if (matchingRegion != null && matchingRegion.getType()
                                        .equals(DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)) {
                                    String attrName = startStructuredDocumentRegion.getText(matchingRegion);
                                    NamedNodeMap attributes = domNode.getAttributes();
                                    if (attributes != null && attrName.length() > 0) {
                                        Node attr = attributes.getNamedItem(attrName);
                                        if (attr != null) {
                                            selectionSet = true;
                                            XMLTableTreeViewer.this.setSelection(new StructuredSelection(attr));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (!selectionSet) {
                XMLTableTreeViewer.this.setSelection(selection);
            }
        }
    }

    private PaintListener fContentPaintListener = new PaintListener() {

        public void paintControl(PaintEvent e) {
            GC gc = e.gc;
            if (getTree().getItemCount() == 0) {
                gc.setForeground(getTree().getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND));
                gc.setBackground(getTree().getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
                gc.drawString(XMLEditorMessages.XMLTreeExtension_3, 10, 10);
                gc.drawString(XMLEditorMessages.XMLTreeExtension_4, 10, 10 + gc.getFontMetrics().getHeight());
            }
        }

    };

    private ISelectionProvider fSelectionProvider = new SelectionProvider();
    // node: 40%, content: 60%
    static final double[] WEIGHTS = { .4, .6 };

    private ControlAdapter fResizeAdapter = new ControlAdapter() {
        public void controlResized(ControlEvent e) {
            sizeColumns(getTree().getSize().x, WEIGHTS);
        }

        private void sizeColumns(int width, double[] weights) {
            TreeColumn[] columns = getTree().getColumns();
            int size = columns.length;
            for (int i = 0; i < size; i++) {
                columns[i].setWidth((int) (weights[i] * width));
            }
        }
    };

    public XMLTableTreeViewer(Composite parent) {
        super(parent, SWT.FULL_SELECTION | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);

        TreeColumn column = new TreeColumn(this.getTree(), SWT.LEFT);
        column.setText(XMLEditorMessages.XMLTableTreeViewer_1);

        column = new TreeColumn(this.getTree(), SWT.LEFT);
        column.setText(XMLEditorMessages.XMLTableTreeViewer_2);

        this.getTree().setHeaderVisible(true);
        this.getTree().setLinesVisible(true);
        getTree().addControlListener(fResizeAdapter);
        getTree().getAccessible().addAccessibleListener(new AccessibleAdapter() {
            public void getDescription(AccessibleEvent e) {
                if (e.childID == ACC.CHILDID_SELF && getTree().getItemCount() == 0) {
                    e.result = XMLEditorMessages.XMLTreeExtension_3 + "\n" + XMLEditorMessages.XMLTreeExtension_4;
                }
            }
        });
        // set up providers
        propertyDescriptorFactory = new XMLTableTreePropertyDescriptorFactory();

        XMLTableTreeContentProvider provider = new XMLTableTreeContentProvider();
        setContentProvider(provider);
        setLabelProvider(provider);

        setColumnProperties(new String[] { STRUCTURE_PROPERTY, VALUE_PROPERTY });
        setCellEditors(new CellEditor[] { null, new TextCellEditor(this.getTree()) });

        setCellModifier(new XMLCMCellModifier());
        createContextMenu();

        DragSource dragSource = new DragSource(getControl(), DND.DROP_COPY | DND.DROP_MOVE);
        dragSource.addDragListener(createDragSourceListener());
        dragSource.setTransfer(new Transfer[] { LocalSelectionTransfer.getTransfer() });
        DropTarget dropTarget = new DropTarget(getControl(), DND.DROP_COPY | DND.DROP_MOVE);
        dropTarget.addDropListener(createDropTargetListener());
        dropTarget.setTransfer(new Transfer[] { LocalSelectionTransfer.getTransfer() });

        this.getTree().addPaintListener(fContentPaintListener);
    }

    /**
     * This creates a context menu for the viewer and adds a listener as well
     * registering the menu for extension.
     */
    protected void createContextMenu() {
        MenuManager contextMenu = new MenuManager("#PopUp"); //$NON-NLS-1$
        contextMenu.add(new Separator("additions")); //$NON-NLS-1$
        contextMenu.setRemoveAllWhenShown(true);
        contextMenu.addMenuListener(new NodeActionMenuListener());
        Menu menu = contextMenu.createContextMenu(getControl());
        getControl().setMenu(menu);
    }

    private DragSourceListener createDragSourceListener() {
        return new DragSourceListener() {
            public void dragFinished(DragSourceEvent event) {
                LocalSelectionTransfer.getTransfer().setSelection(null);
            }

            public void dragSetData(DragSourceEvent event) {
            }

            public void dragStart(DragSourceEvent event) {
                LocalSelectionTransfer.getTransfer().setSelection(getSelection());
            }
        };
    }

    private DropTargetListener createDropTargetListener() {
        return new DropTargetListener() {
            public void dragEnter(DropTargetEvent event) {
            }

            public void dragLeave(DropTargetEvent event) {
            }

            public void dragOperationChanged(DropTargetEvent event) {
            }

            public void drop(DropTargetEvent event) {
                if (event.operations != DND.DROP_NONE
                        && LocalSelectionTransfer.getTransfer().getSelection() instanceof IStructuredSelection) {
                    dragOver(event);
                    IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer()
                            .getSelection();
                    if (selection != null && !selection.isEmpty() && event.item != null
                            && event.item.getData() != null) {
                        /*
                         * the command uses these numbers instead of the
                         * feedback constants (even though it converts in
                         * the other direction as well)
                         */
                        float feedbackFloat = getHeightInItem(event);

                        final DragNodeCommand command = new DragNodeCommand(event.item.getData(), feedbackFloat,
                                event.operations, event.detail, selection.toList(), XMLTableTreeViewer.this);
                        if (command != null && command.canExecute()) {
                            SafeRunnable.run(new SafeRunnable() {
                                public void run() throws Exception {
                                    command.execute();
                                }
                            });
                        }
                    }
                }
            }

            public void dropAccept(DropTargetEvent event) {
            }

            public void dragOver(DropTargetEvent event) {
                event.feedback = DND.FEEDBACK_SELECT;
                float feedbackFloat = getHeightInItem(event);
                if (feedbackFloat > 0.75) {
                    event.feedback = DND.FEEDBACK_INSERT_AFTER;
                } else if (feedbackFloat < 0.25) {
                    event.feedback = DND.FEEDBACK_INSERT_BEFORE;
                }
                event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
                if (getLocalNodeSelection(event) == null) {
                    event.detail = DND.DROP_NONE;
                }
            }

            private IStructuredSelection getLocalNodeSelection(DropTargetEvent event) {
                IStructuredSelection selection = (IStructuredSelection) LocalSelectionTransfer.getTransfer()
                        .getSelection();
                if (selection != null && !selection.isEmpty()) {
                    for (Iterator i = selection.iterator(); i.hasNext();) {
                        Object node = i.next();
                        if (node instanceof Node) {
                            return selection;
                        }
                    }
                }
                return null;
            }

            private float getHeightInItem(DropTargetEvent event) {
                if (event.item == null)
                    return .5f;
                if (event.item instanceof TreeItem) {
                    TreeItem treeItem = (TreeItem) event.item;
                    Control control = treeItem.getParent();
                    Point point = control.toControl(new Point(event.x, event.y));
                    Rectangle bounds = treeItem.getBounds();
                    return (float) (point.y - bounds.y) / (float) bounds.height;
                } else if (event.item instanceof TableItem) {
                    TableItem tableItem = (TableItem) event.item;
                    Control control = tableItem.getParent();
                    Point point = control.toControl(new Point(event.x, event.y));
                    Rectangle bounds = tableItem.getBounds(0);
                    return (float) (point.y - bounds.y) / (float) bounds.height;
                } else {
                    return 0.0F;
                }
            }
        };
    }

    protected void doRefresh(Object o, boolean fromDelayed) {
        super.refresh(o);
    }

    public ISelectionProvider getSelectionProvider() {
        return fSelectionProvider;
    }

    public String getTitle() {
        return XMLEditorMessages.XMLTableTreeViewer_0;
    }

    protected void handleDispose(DisposeEvent event) {
        super.handleDispose(event);
        this.getTree().removePaintListener(fContentPaintListener);
        getTree().removeControlListener(fResizeAdapter);
        setDocument(null);
    }

    public void setDocument(IDocument document) {
        /*
         * let the text editor to be the one that manages the model's lifetime
         */
        IStructuredModel model = null;
        try {
            model = StructuredModelManager.getModelManager().getExistingModelForRead(document);

            if ((model != null) && (model instanceof IDOMModel)) {
                Document domDoc = null;
                domDoc = ((IDOMModel) model).getDocument();
                setInput(domDoc);
            }
        } finally {
            if (model != null) {
                model.releaseFromRead();
            }
        }

    }

    protected TreeContentHelper treeContentHelper = new TreeContentHelper();
    protected XMLTableTreePropertyDescriptorFactory propertyDescriptorFactory;

    private final static String STRUCTURE_PROPERTY = XMLEditorMessages.XMLTreeExtension_0;
    private final static String VALUE_PROPERTY = XMLEditorMessages.XMLTreeExtension_1;

    private class CellListener extends ColumnViewerEditorActivationListener implements ICellEditorListener {

        private Node node;
        private CellEditor editor;
        private String originalValue;

        CellListener(Node data, CellEditor editor) {
            this.node = data;
            this.editor = editor;

            originalValue = treeContentHelper.getNodeValue(node);
            ((IDOMNode) node).getModel().aboutToChangeModel();
        }

        public void applyEditorValue() {
            editor.removeListener(this);
        }

        public void cancelEditor() {
            final Object value = editor.getValue();
            if (value != null && !value.equals(originalValue)) {
                treeContentHelper.setNodeValue(node, originalValue);
            }
            editor.removeListener(this);
        }

        public void editorValueChanged(boolean oldValidState, boolean newValidState) {
            if (newValidState) {
                treeContentHelper.setNodeValue(node, editor.getValue().toString(), getControl().getShell());
            }
        }

        public void beforeEditorActivated(ColumnViewerEditorActivationEvent event) {
        }

        public void afterEditorActivated(ColumnViewerEditorActivationEvent event) {
        }

        public void beforeEditorDeactivated(ColumnViewerEditorDeactivationEvent event) {
        }

        public void afterEditorDeactivated(ColumnViewerEditorDeactivationEvent event) {
            ((IDOMNode) node).getModel().changedModel();
            getColumnViewerEditor().removeEditorActivationListener(this);
        }

    }

    public class XMLCMCellModifier implements ICellModifier, TreeExtension.ICellEditorProvider {
        public boolean canModify(Object element, String property) {
            boolean result = false;
            if (element instanceof Node) {
                Node node = (Node) element;
                if (property == VALUE_PROPERTY) {
                    result = treeContentHelper.isEditable(node);
                    if (result) {
                        /* Set up the cell editor based on the element */
                        CellEditor[] editors = getCellEditors();
                        if (editors.length > 0) {
                            if (editors[1] != null)
                                editors[1].dispose();
                            editors[1] = getCellEditor(element, 1);
                            if (editors[1] instanceof TextCellEditor) {
                                final CellListener listener = new CellListener(node, editors[1]);
                                getColumnViewerEditor().addEditorActivationListener(listener);
                                editors[1].addListener(listener);
                            }
                        }
                    }

                }
            }
            return result;
        }

        public Object getValue(Object object, String property) {
            String result = null;
            if (object instanceof Node) {
                result = treeContentHelper.getNodeValue((Node) object);
            }
            return (result != null) ? result : ""; //$NON-NLS-1$
        }

        public void modify(Object element, String property, Object value) {
            Item item = (Item) element;
            String oldValue = treeContentHelper.getNodeValue((Node) item.getData());
            String newValue = value.toString();
            if ((newValue != null) && !newValue.equals(oldValue)) {
                treeContentHelper.setNodeValue((Node) item.getData(), value.toString(), getControl().getShell());
            }
        }

        public CellEditor getCellEditor(Object o, int col) {
            IPropertyDescriptor pd = propertyDescriptorFactory.createPropertyDescriptor(o);
            return pd != null ? pd.createPropertyEditor(XMLTableTreeViewer.this.getTree()) : null;
        }
    }

}