org.eclipse.mylyn.internal.tasks.ui.editors.outline.QuickOutlineDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.mylyn.internal.tasks.ui.editors.outline.QuickOutlineDialog.java

Source

/*******************************************************************************
 *  Copyright (c) 2006, 2010 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
 *     Tasktop Technologies - adapted for Mylyn
 *     Frank Becker - adapted for Mylyn Task Editor
 *******************************************************************************/

package org.eclipse.mylyn.internal.tasks.ui.editors.outline;

import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlExtension;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.mylyn.commons.workbench.DecoratingPatternStyledCellLabelProvider;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorOutlineContentProvider;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorOutlineModel;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorOutlineNode;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorPage;
import org.eclipse.mylyn.tasks.ui.editors.TaskEditor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
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.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.forms.editor.IFormPage;

/**
 * @author Mik Kersten
 * @author Frank Becker
 * @author Steffen Pingel
 */
public class QuickOutlineDialog extends PopupDialog implements IInformationControl, IInformationControlExtension,
        IInformationControlExtension2, DisposeListener {

    public final class Filter extends PatternFilter {
        @Override
        protected boolean wordMatches(String text) {
            return super.wordMatches(text);
        }
    }

    public final class TaskEditorOutlineLabelDecorator implements ILabelDecorator {

        public String decorateText(String text, Object element) {
            if (element instanceof TaskEditorOutlineNode) {
                TaskEditorOutlineNode node = (TaskEditorOutlineNode) element;
                if (node.getTaskRelation() != null) {
                    return NLS.bind(Messages.QuickOutlineDialog_Node_Label_Decoration, text,
                            node.getTaskRelation().toString());
                }
            }
            return null;
        }

        public void addListener(ILabelProviderListener listener) {
            // ignore
        }

        public void dispose() {
            // ignore
        }

        public boolean isLabelProperty(Object element, String property) {
            return false;
        }

        public void removeListener(ILabelProviderListener listener) {
            // ignore
        }

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

    }

    private class OpenListener implements IOpenListener, IDoubleClickListener, MouseListener {

        private final Viewer viewer;

        public OpenListener(Viewer viewer) {
            this.viewer = viewer;
        }

        public void mouseDoubleClick(MouseEvent e) {
            setSelection(e);
        }

        public void mouseDown(MouseEvent e) {
            setSelection(e);
        }

        public void mouseUp(MouseEvent e) {
            // ignore

        }

        public void doubleClick(DoubleClickEvent event) {
            open(null);
        }

        public void open(OpenEvent event) {
            AbstractTaskEditorPage taskEditorPage = getTaskEditorPage();
            if (taskEditorPage == null) {
                return;
            }

            StructuredSelection selection = (StructuredSelection) viewer.getSelection();
            Object select = (selection).getFirstElement();
            taskEditorPage.selectReveal(select);
        }

        private void setSelection(MouseEvent event) {
            try {
                Object selection = ((Tree) event.getSource()).getSelection()[0].getData();
                viewer.setSelection(new StructuredSelection(selection));
                open(null);
            } catch (Exception e) {
                // ignore
            }
        }

    }

    public static final String ID_VIEWER = "org.eclipse.mylyn.internal.tasks.ui.taskdata.quick"; //$NON-NLS-1$

    private TreeViewer viewer;

    private Text filterText;

    private Filter namePatternFilter;

    private OpenListener openListener;

    private final IWorkbenchWindow window;

    public QuickOutlineDialog(IWorkbenchWindow window) {
        super(window.getShell(), SWT.RESIZE, true, true, true, true, true, null, null);
        this.window = window;
        setInfoText(Messages.QuickOutlineDialog_Press_Esc_Info_Text);
        create();
    }

    @Override
    public boolean close() {
        // nothing additional to dispose
        return super.close();
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        createViewer(parent);
        createUIListenersTreeViewer();
        addDisposeListener(this);

        return viewer.getControl();
    }

    private void createViewer(Composite parent) {
        Control composite = super.createDialogArea(parent);
        viewer = createCommonViewer((Composite) composite);
        openListener = new OpenListener(viewer);

        viewer.addOpenListener(openListener);
        viewer.getTree().addMouseListener(openListener);

        namePatternFilter = new Filter();
        namePatternFilter.setIncludeLeadingWildcard(true);
        viewer.addFilter(namePatternFilter);

        AbstractTaskEditorPage taskEditorPage = getTaskEditorPage();
        if (taskEditorPage != null) {
            try {
                viewer.getControl().setRedraw(false);
                TaskEditorOutlineNode root = TaskEditorOutlineNode.parse(taskEditorPage.getModel().getTaskData(),
                        true);
                viewer.setInput(new TaskEditorOutlineModel(root));
                viewer.expandAll();
                TaskEditorOutlineNode attributesNode = root.getChild(TaskEditorOutlineNode.LABEL_ATTRIBUTES);
                if (attributesNode != null) {
                    viewer.collapseToLevel(attributesNode, AbstractTreeViewer.ALL_LEVELS);
                }
            } finally {
                viewer.getControl().setRedraw(true);
            }
        }
    }

    protected TreeViewer createCommonViewer(Composite parent) {
        TreeViewer viewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
        viewer.setUseHashlookup(true);
        viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        viewer.setContentProvider(new TaskEditorOutlineContentProvider());
        viewer.setLabelProvider(new DecoratingPatternStyledCellLabelProvider(new QuickOutlineLabelProvider(),
                new TaskEditorOutlineLabelDecorator(), null));
        return viewer;
    }

    @Override
    protected void fillDialogMenu(IMenuManager dialogMenu) {
        dialogMenu.add(new Separator());
        super.fillDialogMenu(dialogMenu);
    }

    private void createUIListenersTreeViewer() {
        final Tree tree = viewer.getTree();
        tree.addKeyListener(new KeyListener() {
            public void keyPressed(KeyEvent e) {
                if (e.character == 0x1B) {
                    // Dispose on ESC key press
                    dispose();
                }
            }

            public void keyReleased(KeyEvent e) {
                // ignore
            }
        });

        tree.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseUp(MouseEvent e) {
                handleTreeViewerMouseUp(tree, e);
            }
        });

        tree.addSelectionListener(new SelectionListener() {
            public void widgetSelected(SelectionEvent e) {
                // ignore
            }

            public void widgetDefaultSelected(SelectionEvent e) {
                gotoSelectedElement();
            }
        });
    }

    private void handleTreeViewerMouseUp(final Tree tree, MouseEvent e) {
        if ((tree.getSelectionCount() < 1) || (e.button != 1) || (tree.equals(e.getSource()) == false)) {
            return;
        }
        // Selection is made in the selection changed listener
        Object object = tree.getItem(new Point(e.x, e.y));
        TreeItem selection = tree.getSelection()[0];
        if (selection.equals(object)) {
            gotoSelectedElement();
        }
    }

    private Object getSelectedElement() {
        if (viewer == null) {
            return null;
        }
        return ((IStructuredSelection) viewer.getSelection()).getFirstElement();
    }

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

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

    public Point computeSizeHint() {
        // Note that it already has the persisted size if persisting is enabled.
        return getShell().getSize();
    }

    public void dispose() {
        close();
    }

    @Override
    protected Point getDefaultSize() {
        return new Point(400, 300);
    }

    public boolean isFocusControl() {
        if (viewer.getControl().isFocusControl() || filterText.isFocusControl()) {
            return true;
        }
        return false;
    }

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

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

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

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

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

    public void setInformation(String information) {
        // See IInformationControlExtension2
    }

    public void setLocation(Point location) {
        /*
         * If the location is persisted, it gets managed by PopupDialog - fine. Otherwise, the location is
         * computed in Window#getInitialLocation, which will center it in the parent shell / main
         * monitor, which is wrong for two reasons:
         * - we want to center over the editor / subject control, not the parent shell
         * - the center is computed via the initalSize, which may be also wrong since the size may 
         *   have been updated since via min/max sizing of AbstractInformationControlManager.
         * In that case, override the location with the one computed by the manager. Note that
         * the call to constrainShellSize in PopupDialog.open will still ensure that the shell is
         * entirely visible.
         */
        if (getPersistLocation() == false || getDialogSettings() == null) {
            getShell().setLocation(location);
        }
    }

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

    public void setSizeConstraints(int maxWidth, int maxHeight) {
        // Ignore
    }

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

    public boolean hasContents() {
        if ((viewer == null) || (viewer.getInput() == null)) {
            return false;
        }
        return true;
    }

    public void setInput(Object input) {
        if (input != null) {
            viewer.setSelection(new StructuredSelection(input));
        }
    }

    public void widgetDisposed(DisposeEvent e) {
        // Note: We do not reuse the dialog
        viewer = null;
        filterText = null;
    }

    @Override
    protected Control createTitleControl(Composite parent) {
        Composite control = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout(1, false);
        layout.marginHeight = 0;
        control.setLayout(layout);
        control.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // Applies only to dialog title - not body.  See createDialogArea
        // Create the text widget
        createUIWidgetFilterText(control);
        // Add listeners to the text widget
        createUIListenersFilterText();
        // Return the text widget
        return control;
    }

    private void createUIWidgetFilterText(Composite parent) {
        // Create the widget
        filterText = new Text(parent, SWT.NONE);
        // Set the font 
        GC gc = new GC(parent);
        gc.setFont(parent.getFont());
        FontMetrics fontMetrics = gc.getFontMetrics();
        gc.dispose();
        // Create the layout
        GridData data = new GridData(GridData.FILL_HORIZONTAL);
        data.heightHint = Dialog.convertHeightInCharsToPixels(fontMetrics, 1);
        data.horizontalAlignment = GridData.FILL;
        data.verticalAlignment = GridData.CENTER;
        filterText.setLayoutData(data);
    }

    /**
     * 
     */
    private void gotoSelectedElement() {
        Object selectedElement = getSelectedElement();
        if (selectedElement == null) {
            return;
        }
        dispose();
    }

    private void createUIListenersFilterText() {
        filterText.addKeyListener(new KeyListener() {
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == 0x0D) {
                    // Return key was pressed
                    gotoSelectedElement();
                } else if (e.keyCode == SWT.ARROW_DOWN) {
                    // Down key was pressed
                    viewer.getTree().setFocus();
                } else if (e.keyCode == SWT.ARROW_UP) {
                    // Up key was pressed
                    viewer.getTree().setFocus();
                } else if (e.character == 0x1B) {
                    // Escape key was pressed
                    dispose();
                }
            }

            public void keyReleased(KeyEvent e) {
                // NO-OP
            }
        });
        // Handle text modify events
        filterText.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                String text = ((Text) e.widget).getText();
                int length = text.length();
                if (length > 0) {
                    // Append a '*' pattern to the end of the text value if it
                    // does not have one already
                    if (text.charAt(length - 1) != '*') {
                        text = text + '*';
                    }
                    // Prepend a '*' pattern to the beginning of the text value
                    // if it does not have one already
                    if (text.charAt(0) != '*') {
                        text = '*' + text;
                    }
                }
                // Set and update the pattern
                setMatcherString(text, true);
            }
        });
    }

    /**
     * Sets the patterns to filter out for the receiver.
     * <p>
     * The following characters have special meaning: ? => any character * => any string
     * </p>
     * 
     * @param pattern
     *            the pattern
     * @param update
     *            <code>true</code> if the viewer should be updated
     */
    private void setMatcherString(String pattern, boolean update) {
        namePatternFilter.setPattern(pattern);
        if (update) {
            stringMatcherUpdated();
        }
    }

    /**
     * The string matcher has been modified. The default implementation refreshes the view and selects the first matched
     * element
     */
    private void stringMatcherUpdated() {
        // Refresh the tree viewer to re-filter
        viewer.getControl().setRedraw(false);
        viewer.refresh();
        viewer.expandAll();
        selectFirstMatch();
        viewer.getControl().setRedraw(true);
    }

    protected AbstractTaskEditorPage getTaskEditorPage() {
        IWorkbenchPage activePage = window.getActivePage();
        if (activePage == null) {
            return null;
        }
        IEditorPart editorPart = activePage.getActiveEditor();
        AbstractTaskEditorPage taskEditorPage = null;
        if (editorPart instanceof TaskEditor) {
            TaskEditor taskEditor = (TaskEditor) editorPart;
            IFormPage formPage = taskEditor.getActivePageInstance();
            if (formPage instanceof AbstractTaskEditorPage) {
                taskEditorPage = (AbstractTaskEditorPage) formPage;
            }
        }
        return taskEditorPage;
    }

    /**
     * Selects the first element in the tree which matches the current filter pattern.
     */
    private void selectFirstMatch() {
        Tree tree = viewer.getTree();
        Object element = findFirstMatchToPattern(tree.getItems());
        if (element != null) {
            viewer.setSelection(new StructuredSelection(element), true);
        } else {
            viewer.setSelection(StructuredSelection.EMPTY);
        }
    }

    /**
     * @param items
     * @return
     */
    private Object findFirstMatchToPattern(TreeItem[] items) {
        // Match the string pattern against labels
        ILabelProvider labelProvider = (ILabelProvider) viewer.getLabelProvider();
        // Process each item in the tree
        for (TreeItem item : items) {
            Object element = item.getData();
            // Return the element if it matches the pattern
            if (element != null) {
                String label = labelProvider.getText(element);
                if (namePatternFilter.wordMatches(label)) {
                    return element;
                }
            }
            // Recursively check the elements children for a match
            element = findFirstMatchToPattern(item.getItems());
            // Return the child element match if found
            if (element != null) {
                return element;
            }
        }
        // No match found
        return null;
    }

}