com.google.dart.tools.ui.internal.text.functions.DartOutlineInformationControl_NEW.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.tools.ui.internal.text.functions.DartOutlineInformationControl_NEW.java

Source

/*
 * Copyright (c) 2014, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
 * 
 * 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 com.google.dart.tools.ui.internal.text.functions;

import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.internal.text.editor.DartEditor;
import com.google.dart.tools.ui.internal.text.editor.DartOutlinePage_NEW;
import com.google.dart.tools.ui.internal.text.editor.LightNodeElements;
import com.google.dart.tools.ui.internal.util.GridDataFactory;

import org.dartlang.analysis.server.protocol.ElementKind;
import org.dartlang.analysis.server.protocol.Outline;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.texteditor.ITextEditor;

import java.util.List;

/**
 * Show outline in light-weight control.
 */
public class DartOutlineInformationControl_NEW extends PopupDialog implements IInformationControl {
    /**
     * {@link ViewerFilter} that allows only branches with outlines satisfying {@link #matcher}.
     */
    private class NameFilter extends ViewerFilter {
        @Override
        public boolean select(Viewer viewer, Object parentElement, Object element) {
            Outline outline = (Outline) element;
            // no filter
            if (matcher == null) {
                return true;
            }
            // maybe "outline" matches
            String name = outline.getElement().getName();
            if (name != null && matcher.match(name)) {
                return true;
            }
            // ...or has matching children
            return hasMatchingChild(outline);
        }

        private boolean hasMatchingChild(Outline outline) {
            for (Outline child : outline.getChildren()) {
                if (select(viewer, outline, child)) {
                    return true;
                }
            }
            return false;
        }
    }

    private class OutlineTreeViewer extends TreeViewer {
        private OutlineTreeViewer(Tree tree) {
            super(tree);
            setUseHashlookup(true);
        }

        @Override
        protected Widget internalGetWidgetToSelect(Object elementOrTreePath) {
            if (elementOrTreePath instanceof TreeItem) {
                return (TreeItem) elementOrTreePath;
            }
            return super.internalGetWidgetToSelect(elementOrTreePath);
        }

        TreeItem findItem2(Object o) {
            return (TreeItem) super.findItem(o);
        }
    }

    /**
     * @return the deepest {@link Outline} enclosing given offset, may be <code>null</code>.
     */
    private static Outline findOutlineEnclosingOffset(List<Outline> outlines, int offset) {
        for (Outline outline : outlines) {
            if (outline.containsInclusive(offset)) {
                Outline deeperOutline = findOutlineEnclosingOffset(outline.getChildren(), offset);
                if (deeperOutline != null) {
                    return deeperOutline;
                }
                return outline;
            }
        }
        return null;
    }

    private final DartEditor editor;
    private Text filterText;
    private OutlineTreeViewer viewer;

    private DartElementPrefixPatternMatcher matcher;

    public DartOutlineInformationControl_NEW(Shell parent, int shellStyle, ITextEditor textEditor) {
        super(parent, shellStyle, true, true, true, true, true, "titleText", "infoText");
        editor = textEditor instanceof DartEditor ? (DartEditor) textEditor : null;
        create();
    }

    @Override
    public void addDisposeListener(DisposeListener listener) {
        getShell().addDisposeListener(listener);
    }

    @Override
    public void addFocusListener(FocusListener listener) {
        getShell().addFocusListener(listener);
    }

    @Override
    public Point computeSizeHint() {
        return getShell().getSize();
    }

    @Override
    public void dispose() {
        close();
    }

    @Override
    public boolean isFocusControl() {
        return viewer.getControl().isFocusControl() || filterText.isFocusControl();
    }

    @Override
    public void removeDisposeListener(DisposeListener listener) {
        getShell().removeDisposeListener(listener);
    }

    @Override
    public void removeFocusListener(FocusListener listener) {
        getShell().removeFocusListener(listener);
    }

    @Override
    public void setBackgroundColor(Color background) {
        applyBackgroundColor(background, getContents());
    }

    @Override
    public void setFocus() {
        getShell().forceFocus();
        filterText.setFocus();
    }

    @Override
    public void setForegroundColor(Color foreground) {
        applyForegroundColor(foreground, getContents());
    }

    @Override
    public void setInformation(String information) {
        // ignore
    }

    @Override
    public void setLocation(Point location) {
        if (!getPersistSize() || getDialogSettings() == null) {
            getShell().setLocation(location);
        }
    }

    @Override
    public void setSize(int width, int height) {
        getShell().setSize(width, height);
    }

    @Override
    public void setSizeConstraints(int maxWidth, int maxHeight) {
        // ignore
    }

    @Override
    public void setVisible(boolean visible) {
        if (visible) {
            open();
        } else {
            saveDialogBounds(getShell());
            getShell().setVisible(false);
        }
    }

    @Override
    protected void configureShell(Shell shell) {
        super.configureShell(shell);
        shell.setText("Outline");
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        final Tree tree = new Tree(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
        GridDataFactory.create(tree).hintHeightChars(20).grab().fill();
        if (editor == null) {
            return tree;
        }
        // create TreeViewer
        viewer = new OutlineTreeViewer(tree);
        viewer.addFilter(new NameFilter());
        viewer.setComparer(DartOutlinePage_NEW.OUTLINE_COMPARER);
        viewer.setContentProvider(new DartOutlinePage_NEW.OutlineContentProvider());
        viewer.setLabelProvider(
                new DelegatingStyledCellLabelProvider(new DartOutlinePage_NEW.OutlineLabelProvider()));
        viewer.setInput(editor.getOutline());
        selectOutlineEnclosingEditorSelection();
        // close on ESC
        tree.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.character == 0x1B) {
                    dispose();
                }
            }
        });
        // goto on Enter
        tree.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetDefaultSelected(SelectionEvent e) {
                gotoSelectedOutline();
            }
        });
        // select item under mouse
        tree.addMouseMoveListener(new MouseMoveListener() {
            TreeItem lastItem = null;

            @Override
            public void mouseMove(MouseEvent e) {
                if (tree.equals(e.getSource())) {
                    Object o = tree.getItem(new Point(e.x, e.y));
                    if (o instanceof TreeItem) {
                        if (!o.equals(lastItem)) {
                            lastItem = (TreeItem) o;
                            tree.setSelection(new TreeItem[] { lastItem });
                        }
                    }
                }
            }
        });
        // goto after click
        tree.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseUp(MouseEvent e) {
                // has selection
                if (tree.getSelectionCount() < 1) {
                    return;
                }
                // first (usually left) button
                if (e.button != 1) {
                    return;
                }
                // select element under mouse
                Object targetItem = tree.getItem(new Point(e.x, e.y));
                if (targetItem != null) {
                    gotoSelectedOutline();
                }
            }
        });
        // done
        return tree;
    }

    protected void createHorizontalSeparator(Composite parent) {
        Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT);
        separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    }

    @Override
    protected Control createTitleControl(Composite parent) {
        filterText = new Text(parent, SWT.NONE);
        GridDataFactory.create(filterText).grabHorizontal().fillHorizontal().alignVerticalMiddle();
        Dialog.applyDialogFont(filterText);
        filterText.setText("");
        // navigation
        filterText.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == 0x0D) {
                    gotoSelectedOutline();
                }
                if (e.keyCode == SWT.ARROW_DOWN) {
                    viewer.getTree().setFocus();
                }
                if (e.keyCode == SWT.ARROW_UP) {
                    viewer.getTree().setFocus();
                }
                if (e.character == 0x1B) {
                    dispose();
                }
            }
        });
        // filter change
        filterText.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                String text = filterText.getText();
                setMatcherString(text);
            }
        });
        return filterText;
    }

    @Override
    protected IDialogSettings getDialogSettings() {
        String sectionName = "com.google.dart.tools.ui.functions.QuickOutline";
        IDialogSettings pluginSettings = DartToolsPlugin.getDefault().getDialogSettings();
        IDialogSettings settings = pluginSettings.getSection(sectionName);
        if (settings == null) {
            settings = pluginSettings.addNewSection(sectionName);
        }
        return settings;
    }

    @Override
    protected boolean hasInfoArea() {
        return false;
    }

    /**
     * @return the first {@link TreeItem} matching {@link #matcher}.
     */
    private TreeItem findFirstMatchingItem(TreeItem[] items) {
        for (TreeItem item : items) {
            // no filter  - first is good
            if (matcher == null) {
                return item;
            }
            // check "outline" for filter
            {
                String label = item.getText();
                if (matcher.match(label)) {
                    return item;
                }
            }
            // not "outline", but may be one of its children
            item = findFirstMatchingItem(item.getItems());
            if (item != null) {
                return item;
            }
        }
        // no matching outlines
        return null;
    }

    /**
     * Opens selected {@link Outline} in the {@link #editor}.
     */
    private void gotoSelectedOutline() {
        if (viewer != null) {
            IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
            Outline outline = (Outline) selection.getFirstElement();
            editor.setSelection_NEW(outline, true);
            dispose();
        }
    }

    /**
     * Selects the first {@link TreeItem} which matches the {@link #matcher}.
     */
    private void selectFirstMatch() {
        Tree tree = viewer.getTree();
        TreeItem item = findFirstMatchingItem(tree.getItems());
        if (item != null) {
            viewer.setSelection(new StructuredSelection(item), true);
        } else {
            viewer.setSelection(StructuredSelection.EMPTY);
        }
    }

    /**
     * Attempts to find {@link Outline} corresponding to the selection in {@link #editor} and select
     * it in {@link #viewer}.
     */
    private void selectOutlineEnclosingEditorSelection() {
        // may be small unit, expand as possible
        LightNodeElements.expandTreeItemsTimeBoxed(viewer, 50L * 1000000L);
        // prepare "outline" to select
        final Outline outline;
        {
            Outline input = (Outline) viewer.getInput();
            List<Outline> outlineList = input.getChildren();
            ITextSelection editorSelection = (ITextSelection) editor.getSelectionProvider().getSelection();
            int editorOffset = editorSelection.getOffset();
            outline = findOutlineEnclosingOffset(outlineList, editorOffset);
        }
        // select "outline"
        if (outline != null) {
            // select "outline" to expect its parents
            viewer.setSelection(new StructuredSelection(outline), true);
            // make root of "outline" top item 
            {
                Outline parent = outline.getParent();
                while (parent != null && !parent.getElement().getKind().equals(ElementKind.COMPILATION_UNIT)) {
                    if (parent.getParent() == null) {
                        TreeItem parentItem = viewer.findItem2(parent);
                        if (parentItem != null) {
                            viewer.getTree().setTopItem(parentItem);
                        }
                        break;
                    }
                    parent = parent.getParent();
                }
            }
            // schedule "outline" selection again for case when parent of "outline" has many children
            Display.getCurrent().asyncExec(new Runnable() {
                @Override
                public void run() {
                    viewer.setSelection(new StructuredSelection(outline), true);
                }
            });
        }
    }

    /**
     * Sets the patterns to filter out {@link #viewer}.
     * <p>
     * The following characters have special meaning: ? => any character * => any string
     */
    private void setMatcherString(String pattern) {
        // update "stringMatcher"
        if (pattern.length() == 0) {
            matcher = null;
        } else {
            matcher = new DartElementPrefixPatternMatcher(pattern);
        }
        // refresh "viewer"
        viewer.getControl().setRedraw(false);
        try {
            viewer.collapseAll();
            viewer.refresh(false);
            LightNodeElements.expandTreeItemsTimeBoxed(viewer, 75L * 1000000L);
            selectFirstMatch();
        } finally {
            viewer.getControl().setRedraw(true);
        }
    }
}