Package org.erlide.ui.editors.erl.outline

Source Code of org.erlide.ui.editors.erl.outline.QuickOutlinePopupDialog

/*******************************************************************************
* Copyright (c) 2006 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.erlide.ui.editors.erl.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.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
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.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.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.erlide.engine.model.root.IErlElement;
import org.erlide.ui.ErlideUIMessages;
import org.erlide.ui.actions.SortAction;
import org.erlide.ui.editors.erl.AbstractErlangEditor;
import org.erlide.ui.editors.erl.outline.filters.FilterDescriptor;
import org.erlide.ui.editors.util.EditorUtility;
import org.erlide.ui.navigator.ErlElementSorter;
import org.erlide.ui.util.StringMatcher;
import org.erlide.util.ErlLogger;

/**
* AbstractInfoPopupDialog
*
*/
public class QuickOutlinePopupDialog extends PopupDialog implements IInformationControl,
        IInformationControlExtension, IInformationControlExtension2, DisposeListener {

    TreeViewer fTreeViewer;
    private final IOutlineContentCreator fOutlineContentCreator;
    private final IOutlineSelectionHandler fOutlineSelectionHandler;
    private Text fFilterText;
    private StringMatcher fStringMatcher;
    private QuickOutlineNamePatternFilter fNamePatternFilter;
    private SortAction fSortAction;
    private ITreeContentProvider fTreeContentProvider;
    private ILabelProvider fTreeLabelProvider;
    private final AbstractErlangEditor fEditor;

    // private ViewerComparator fTreeViewerComparator;
    // private ViewerComparator fTreeViewerDefaultComparator;

    public QuickOutlinePopupDialog(final Shell parent, final int shellStyle,
            final AbstractErlangEditor editor, final IOutlineContentCreator creator,
            final IOutlineSelectionHandler handler) {
        super(parent, shellStyle, true, true, true, true, true, null, null);
        // Set outline creator
        fOutlineContentCreator = creator;
        // Set outline handler
        fOutlineSelectionHandler = handler;
        // Initialize the other fields
        fEditor = editor;
        initialize();
        // Create all controls early to preserve the life cycle of the original
        // implementation.
        create();
    }

    /**
     *
     */
    private void initialize() {
        setInfoText(ErlideUIMessages.QuickOutlinePopupDialog_infoTextPressEscToExit);

        fFilterText = null;
        fTreeViewer = null;
        fStringMatcher = null;
        fNamePatternFilter = null;
        fSortAction = null;
        fTreeContentProvider = null;
        fTreeLabelProvider = null;
        // fTreeViewerComparator = null;
        // fTreeViewerDefaultComparator = null;
    }

    @Override
    protected Control createDialogArea(final Composite parent) {
        // Applies only to dialog body - not title. See createTitleControl
        // Create an empty dialog area, if the source page is not defined
        if (fOutlineContentCreator == null || fOutlineSelectionHandler == null) {
            return super.createDialogArea(parent);
        }
        // Create the tree viewer
        createUIWidgetTreeViewer(parent);
        // Add listeners to the tree viewer
        createUIListenersTreeViewer();
        // Create the actions
        createUIActions();
        // Add a dispose listner
        addDisposeListener(this);
        // Return the tree
        return fTreeViewer.getControl();
    }

    /**
     *
     */
    private void createUIActions() {
        // Add sort action to dialog menu
        fSortAction = new SortAction(fTreeViewer,
                ErlideUIMessages.PDEMultiPageContentOutline_SortingAction_tooltip,
                new ErlElementSorter(ErlElementSorter.SORT_ON_NAME),
                new ErlElementSorter(ErlElementSorter.SORT_ON_EXPORT), null, false, null);
    }

    @Override
    protected void fillDialogMenu(final IMenuManager dialogMenu) {
        // Add the sort action
        dialogMenu.add(fSortAction);
        // Separator
        dialogMenu.add(new Separator());
        // Add the default actions
        super.fillDialogMenu(dialogMenu);
    }

    /**
     * @param parent
     */
    private void createUIWidgetTreeViewer(final Composite parent) {

        final int style = SWT.H_SCROLL | SWT.V_SCROLL;
        // Create the tree
        final Tree widget = new Tree(parent, style);
        // Configure the layout
        final GridData data = new GridData(GridData.FILL_BOTH);
        data.heightHint = widget.getItemHeight() * 12;
        widget.setLayoutData(data);
        // Create the tree viewer
        fTreeViewer = new TreeViewer(widget);
        // Add member filter, don't show attributes
        final FilterDescriptor filterDescriptor = FilterDescriptor
                .getFilterDescriptor("attributesFilter");
        fTreeViewer.addFilter(filterDescriptor.getViewerFilter());
        // Add the name pattern filter
        fNamePatternFilter = new QuickOutlineNamePatternFilter();
        fTreeViewer.addFilter(fNamePatternFilter);
        // Set the content provider
        fTreeContentProvider = fOutlineContentCreator.createOutlineContentProvider();
        fTreeViewer.setContentProvider(fTreeContentProvider);
        // Set the label provider
        fTreeLabelProvider = fOutlineContentCreator.createOutlineLabelProvider();
        fTreeViewer.setLabelProvider(fTreeLabelProvider);
        // Create the outline sorter (to be set on the sort action)
        // fTreeViewerComparator = fOutlineContentCreator
        // .createOutlineComparator();
        // Set the comparator to null (sort action will be disabled initially
        // because of this)
        // Create the default outline sorter (Most like this will just return
        // null to indicate sorting disabled
        // fTreeViewerDefaultComparator = fOutlineContentCreator
        // .createDefaultOutlineComparator();
        // fTreeViewer.setComparator(fTreeViewerDefaultComparator);
        fTreeViewer.setAutoExpandLevel(1);
        fTreeViewer.setUseHashlookup(true);
        fTreeViewer.setInput(fOutlineContentCreator.getOutlineInput());
    }

    /**
     *
     */
    private void createUIListenersTreeViewer() {
        // Get the underlying tree widget
        final Tree tree = fTreeViewer.getTree();
        // Handle key events
        tree.addKeyListener(new KeyListener() {
            @Override
            public void keyPressed(final KeyEvent e) {
                if (e.character == 0x1B) {
                    // Dispose on ESC key press
                    dispose();
                }
            }

            @Override
            public void keyReleased(final KeyEvent e) {
                // NO-OP
            }
        });
        // Handle mouse clicks
        tree.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseUp(final MouseEvent e) {
                handleTreeViewerMouseUp(tree, e);
            }
        });
        // Handle mouse move events
        tree.addMouseMoveListener(new QuickOutlineMouseMoveListener(fTreeViewer));
        // Handle widget selection events
        tree.addSelectionListener(new SelectionListener() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                // NO-OP
            }

            @Override
            public void widgetDefaultSelected(final SelectionEvent e) {
                gotoSelectedElement();
            }
        });
    }

    /**
     * @param tree
     * @param e
     */
    void handleTreeViewerMouseUp(final Tree tree, final MouseEvent e) {
        // Ensure a selection was made, the first mouse button was
        // used and the event happened in the tree
        if (tree.getSelectionCount() < 1 || e.button != 1 || !tree.equals(e.getSource())) {
            return;
        }
        // Selection is made in the selection changed listener
        final Object object = tree.getItem(new Point(e.x, e.y));
        final TreeItem selection = tree.getSelection()[0];
        if (selection.equals(object)) {
            gotoSelectedElement();
        }
    }

    /**
     * @return
     */
    private Object getSelectedElement() {
        if (fTreeViewer == null) {
            return null;
        }
        return ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement();
    }

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

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

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

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

    @Override
    public boolean isFocusControl() {
        if (fTreeViewer.getControl().isFocusControl() || fFilterText.isFocusControl()) {
            return true;
        }
        return false;
    }

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

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

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

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

    @Override
    protected Control getFocusControl() {
        return fFilterText;
    }

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

    @Override
    public void setInformation(final String information) {
        // Ignore
        // See IInformationControlExtension2
    }

    @Override
    public void setLocation(final 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() || !getPersistSize() || getDialogSettings() == null) {
            getShell().setLocation(location);
        }
    }

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

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

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

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

    @Override
    public void setInput(final Object input) {
        // Input comes from ErlangSourceInfoProvider.getInformation2()
        // The input should be a model object of some sort
        // Turn it into a structured selection and set the selection in the tree
        if (input != null) {
            fTreeViewer.setSelection(new StructuredSelection(input));
        }
    }

    @Override
    public void widgetDisposed(final DisposeEvent e) {
        // Note: We do not reuse the dialog
        fTreeViewer = null;
        fFilterText = null;
    }

    @Override
    protected Control createTitleControl(final Composite parent) {
        // Applies only to dialog title - not body. See createDialogArea
        // Create the text widget
        createUIWidgetFilterText(parent);
        // Add listeners to the text widget
        createUIListenersFilterText();
        // Return the text widget
        return fFilterText;
    }

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

    /**
     *
     */
    protected void gotoSelectedElement() {
        final Object selectedElement = getSelectedElement();
        ErlLogger.debug("&&>> " + selectedElement);
        if (selectedElement == null) {
            return;
        }
        dispose();
        if (fEditor != null && selectedElement instanceof IErlElement) {
            EditorUtility.revealInEditor(fEditor, (IErlElement) selectedElement);
        }
    }

    /**
     *
     */
    private void createUIListenersFilterText() {
        // Handle key events
        fFilterText.addKeyListener(new KeyListener() {
            @Override
            public void keyPressed(final KeyEvent e) {
                if (e.keyCode == 0x0D) {
                    // Return key was pressed
                    gotoSelectedElement();
                } else if (e.keyCode == SWT.ARROW_DOWN) {
                    // Down key was pressed
                    fTreeViewer.getTree().setFocus();
                } else if (e.keyCode == SWT.ARROW_UP) {
                    // Up key was pressed
                    fTreeViewer.getTree().setFocus();
                } else if (e.character == 0x1B) {
                    // Escape key was pressed
                    dispose();
                }
            }

            @Override
            public void keyReleased(final KeyEvent e) {
                // NO-OP
            }
        });
        // Handle text modify events
        fFilterText.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(final ModifyEvent e) {
                String text = ((Text) e.widget).getText();
                final 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
     */
    void setMatcherString(final String pattern, final boolean update) {
        if (pattern.length() == 0) {
            fStringMatcher = null;
        } else {
            fStringMatcher = new StringMatcher(pattern, true, false);
        }
        // Update the name pattern filter on the tree viewer
        fNamePatternFilter.setStringMatcher(fStringMatcher);
        // Update the tree viewer according to the 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
        fTreeViewer.getControl().setRedraw(false);
        fTreeViewer.refresh();
        fTreeViewer.expandAll();
        selectFirstMatch();
        fTreeViewer.getControl().setRedraw(true);
    }

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

    /**
     * @param items
     * @return
     */
    private Object findFirstMatchToPattern(final TreeItem[] items) {
        // Match the string pattern against labels
        final ILabelProvider labelProvider = (ILabelProvider) fTreeViewer
                .getLabelProvider();
        // Process each item in the tree
        for (int i = 0; i < items.length; i++) {
            Object element = items[i].getData();
            // Return the first element if no pattern is set
            if (fStringMatcher == null) {
                return element;
            }
            // Return the element if it matches the pattern
            if (element != null) {
                final String label = labelProvider.getText(element);
                if (fStringMatcher.match(label)) {
                    return element;
                }
            }
            // Recursively check the elements children for a match
            element = findFirstMatchToPattern(items[i].getItems());
            // Return the child element match if found
            if (element != null) {
                return element;
            }
        }
        // No match found
        return null;
    }

    @Override
    protected Point getInitialSize() {
        return new Point(400, 250);
    }

}
TOP

Related Classes of org.erlide.ui.editors.erl.outline.QuickOutlinePopupDialog

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.