| /******************************************************************************* |
| * Copyright (c) 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 |
| *******************************************************************************/ |
| package org.eclipse.wst.sse.ui.internal.quickoutline; |
| |
| import org.eclipse.jface.action.IMenuManager; |
| 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.IInformationControlExtension; |
| import org.eclipse.jface.text.IInformationControlExtension2; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ISelection; |
| 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.MouseMoveListener; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.SelectionListener; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Item; |
| 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.ui.IEditorPart; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.texteditor.ITextEditor; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.ui.IContentSelectionProvider; |
| import org.eclipse.wst.sse.ui.internal.SSEUIPlugin; |
| import org.eclipse.wst.sse.ui.internal.quickoutline.StringPatternFilter.StringMatcher; |
| import org.eclipse.wst.sse.ui.quickoutline.AbstractQuickOutlineConfiguration; |
| |
| /** |
| * Popup dialog that contains the filtering input and the outline |
| * view of the editor's input. |
| * |
| * <p> |
| * Based on {@link org.eclipse.jdt.internal.ui.text.AbstractInformationControl} |
| * </p> |
| * |
| */ |
| public class QuickOutlinePopupDialog extends PopupDialog implements IInformationControl, IInformationControlExtension, IInformationControlExtension2, DisposeListener { |
| |
| /** Section used for storing the dialog's size and position settings */ |
| private static final String DIALOG_SECTION = "org.eclipse.wst.sse.ui.quick_outline"; //$NON-NLS-1$ |
| |
| /** Text field for entering filter patterns */ |
| private Text fFilterText; |
| |
| /** Tree for presenting the information outline */ |
| private TreeViewer fTreeViewer; |
| |
| /** The model to be outlined */ |
| private IStructuredModel fModel; |
| |
| private ILabelProvider fLabelProvider; |
| private ITreeContentProvider fContentProvider; |
| |
| private IContentSelectionProvider fSelectionProvider; |
| |
| private StringPatternFilter fFilter; |
| |
| public QuickOutlinePopupDialog(Shell parent, int shellStyle, IStructuredModel model, AbstractQuickOutlineConfiguration configuration) { |
| super(parent, shellStyle, true, true, true, true, true, null, null); |
| fContentProvider = configuration.getContentProvider(); |
| fLabelProvider = configuration.getLabelProvider(); |
| fSelectionProvider = configuration.getContentSelectionProvider(); |
| fModel = model; |
| create(); |
| } |
| |
| protected Control createDialogArea(Composite parent) { |
| createTreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL); |
| addListeners(fTreeViewer.getTree()); |
| |
| installFilter(); |
| return fTreeViewer.getControl(); |
| } |
| |
| protected Control createTitleControl(Composite parent) { |
| createFilterText(parent); |
| |
| return fFilterText; |
| } |
| |
| protected void createTreeViewer(Composite parent, int style) { |
| Tree tree = new Tree(parent, style); |
| GridData gd = new GridData(GridData.FILL_BOTH); |
| gd.heightHint = tree.getItemHeight() * 12; |
| tree.setLayoutData(gd); |
| |
| fTreeViewer = new TreeViewer(tree); |
| fTreeViewer.setContentProvider(fContentProvider); |
| fTreeViewer.setLabelProvider(fLabelProvider); |
| fTreeViewer.setAutoExpandLevel(2); |
| fTreeViewer.setUseHashlookup(true); |
| fTreeViewer.setInput(fModel); |
| } |
| |
| protected void createFilterText(Composite parent) { |
| fFilterText = new Text(parent, SWT.NONE); |
| Dialog.applyDialogFont(fFilterText); |
| |
| GridData data= new GridData(GridData.FILL_HORIZONTAL); |
| data.horizontalAlignment= GridData.FILL; |
| data.verticalAlignment= GridData.CENTER; |
| fFilterText.setLayoutData(data); |
| fFilterText.addKeyListener(new KeyListener() { |
| public void keyPressed(KeyEvent e) { |
| if (e.keyCode == 0x0D) // return |
| gotoSelectedElement(); |
| if (e.keyCode == SWT.ARROW_DOWN) |
| fTreeViewer.getTree().setFocus(); |
| if (e.keyCode == SWT.ARROW_UP) |
| fTreeViewer.getTree().setFocus(); |
| if (e.character == 0x1B) // ESC |
| dispose(); |
| } |
| public void keyReleased(KeyEvent e) { |
| // do nothing |
| } |
| }); |
| } |
| |
| protected void installFilter() { |
| fFilter = new StringPatternFilter(); |
| fTreeViewer.addFilter(fFilter); |
| fFilterText.addModifyListener(new ModifyListener() { |
| public void modifyText(ModifyEvent e) { |
| String text = ((Text) e.widget).getText(); |
| int length = text.length(); |
| if (length > 0 && text.charAt(length -1 ) != '*') { |
| text = text + '*'; |
| } |
| setMatcherString(text, true); |
| } |
| }); |
| } |
| |
| protected void setMatcherString(String pattern, boolean update) { |
| fFilter.updatePattern(pattern); |
| if (update) |
| stringMatcherUpdated(); |
| } |
| /** |
| * The string matcher has been modified. The default implementation |
| * refreshes the view and selects the first matched element |
| */ |
| protected void stringMatcherUpdated() { |
| // refresh viewer to re-filter |
| fTreeViewer.getControl().setRedraw(false); |
| fTreeViewer.refresh(); |
| fTreeViewer.expandAll(); |
| selectFirstMatch(); |
| fTreeViewer.getControl().setRedraw(true); |
| } |
| |
| private void selectFirstMatch() { |
| TreeItem item = findItem(fTreeViewer.getTree().getItems(), fFilter.getStringMatcher()); |
| if (item != null) { |
| fTreeViewer.getTree().setSelection(item); |
| } |
| else { |
| fTreeViewer.setSelection(StructuredSelection.EMPTY); |
| } |
| } |
| |
| private TreeItem findItem(TreeItem[] items, StringMatcher matcher) { |
| if (matcher == null) |
| return items.length > 0 ? items[0] : null; |
| int length = items.length; |
| TreeItem result = null; |
| for (int i = 0; i < length; i++) { |
| if (matcher.match(items[i].getText())) |
| return items[i]; |
| if (items[i].getItemCount() > 0) { |
| result = findItem(items[i].getItems(), matcher); |
| if (result != null) |
| return result; |
| } |
| |
| } |
| return result; |
| } |
| |
| protected IDialogSettings getDialogSettings() { |
| IDialogSettings settings= SSEUIPlugin.getDefault().getDialogSettings().getSection(DIALOG_SECTION); |
| if (settings == null) |
| settings = SSEUIPlugin.getDefault().getDialogSettings().addNewSection(DIALOG_SECTION); |
| |
| return settings; |
| } |
| |
| private void gotoSelectedElement() { |
| Object element = getSelectedElement(); |
| dispose(); |
| ITextEditor editor = getActiveTextEditor(); |
| if (editor != null) { |
| editor.selectAndReveal(((IndexedRegion) element).getStartOffset(), ((IndexedRegion) element).getEndOffset() - ((IndexedRegion) element).getStartOffset()); |
| } |
| } |
| |
| private ITextEditor getActiveTextEditor() { |
| IWorkbench wb = PlatformUI.getWorkbench(); |
| ITextEditor editor = null; |
| if (wb != null) { |
| IWorkbenchWindow ww = wb.getActiveWorkbenchWindow(); |
| IWorkbenchPage page = ww.getActivePage(); |
| if (page != null) { |
| IEditorPart part = page.getActiveEditor(); |
| if (part instanceof ITextEditor) |
| editor = (ITextEditor) part; |
| else |
| editor = part != null ? (ITextEditor) part.getAdapter(ITextEditor.class) : null; |
| } |
| } |
| return editor; |
| } |
| |
| private Object getSelectedElement() { |
| if (fTreeViewer == null) |
| return null; |
| |
| return ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement(); |
| } |
| |
| private void addListeners(final Tree tree) { |
| tree.addKeyListener(new KeyListener() { |
| public void keyPressed(KeyEvent e) { |
| if (e.character == 0x1B) // ESC |
| dispose(); |
| } |
| public void keyReleased(KeyEvent e) { |
| // do nothing |
| } |
| }); |
| |
| tree.addSelectionListener(new SelectionListener() { |
| public void widgetSelected(SelectionEvent e) { |
| // do nothing |
| } |
| public void widgetDefaultSelected(SelectionEvent e) { |
| gotoSelectedElement(); |
| } |
| }); |
| |
| /* Mouse hover */ |
| tree.addMouseMoveListener(new MouseMoveListener() { |
| TreeItem fLastItem= null; |
| public void mouseMove(MouseEvent e) { |
| if (tree.equals(e.getSource())) { |
| Object o= tree.getItem(new Point(e.x, e.y)); |
| if (o instanceof TreeItem) { |
| Rectangle clientArea = tree.getClientArea(); |
| if (!o.equals(fLastItem)) { |
| fLastItem= (TreeItem)o; |
| tree.setSelection(new TreeItem[] { fLastItem }); |
| } else if (e.y - clientArea.y < tree.getItemHeight() / 4) { |
| // Scroll up |
| Point p= tree.toDisplay(e.x, e.y); |
| Item item= fTreeViewer.scrollUp(p.x, p.y); |
| if (item instanceof TreeItem) { |
| fLastItem= (TreeItem)item; |
| tree.setSelection(new TreeItem[] { fLastItem }); |
| } |
| } else if (clientArea.y + clientArea.height - e.y < tree.getItemHeight() / 4) { |
| // Scroll down |
| Point p= tree.toDisplay(e.x, e.y); |
| Item item= fTreeViewer.scrollDown(p.x, p.y); |
| if (item instanceof TreeItem) { |
| fLastItem= (TreeItem)item; |
| tree.setSelection(new TreeItem[] { fLastItem }); |
| } |
| } |
| } |
| } |
| } |
| }); |
| |
| tree.addMouseListener(new MouseAdapter() { |
| public void mouseUp(MouseEvent e) { |
| |
| if (tree.getSelectionCount() < 1) |
| return; |
| |
| if (e.button != 1) |
| return; |
| |
| if (tree.equals(e.getSource())) { |
| Object o= tree.getItem(new Point(e.x, e.y)); |
| TreeItem selection= tree.getSelection()[0]; |
| if (selection.equals(o)) |
| gotoSelectedElement(); |
| } |
| } |
| }); |
| } |
| public void addDisposeListener(DisposeListener listener) { |
| getShell().addDisposeListener(listener); |
| } |
| |
| public void addFocusListener(FocusListener listener) { |
| getShell().addFocusListener(listener); |
| } |
| |
| public Point computeSizeHint() { |
| return getShell().getSize(); |
| } |
| |
| public void dispose() { |
| close(); |
| } |
| |
| public boolean isFocusControl() { |
| return getShell().getDisplay().getActiveShell() == getShell(); |
| } |
| |
| 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(); |
| fFilterText.setFocus(); |
| } |
| |
| public void setForegroundColor(Color foreground) { |
| applyForegroundColor(foreground, getContents()); |
| } |
| |
| public void setInformation(String information) { |
| // nothing to do |
| } |
| |
| 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() || getDialogSettings() == null) |
| getShell().setLocation(location); |
| } |
| |
| public void setSize(int width, int height) { |
| getShell().setSize(width, height); |
| } |
| |
| public void setSizeConstraints(int maxWidth, int maxHeight) { |
| } |
| |
| public void setVisible(boolean visible) { |
| if (visible) { |
| open(); |
| } else { |
| saveDialogBounds(getShell()); |
| getShell().setVisible(false); |
| } |
| } |
| |
| public boolean hasContents() { |
| return fTreeViewer != null && fTreeViewer.getInput() != null; |
| } |
| |
| public void setInput(Object input) { |
| if (!(input instanceof ISelection)) { |
| fTreeViewer.setSelection(new StructuredSelection(input)); |
| } |
| else { |
| if (fSelectionProvider != null) { |
| ISelection selection = fSelectionProvider.getSelection(fTreeViewer, (ISelection) input); |
| fTreeViewer.setSelection(selection); |
| } |
| else { |
| fTreeViewer.setSelection((ISelection) input); |
| } |
| } |
| } |
| |
| public void widgetDisposed(DisposeEvent e) { |
| fTreeViewer = null; |
| fFilterText = null; |
| fModel = null; |
| } |
| |
| protected void fillDialogMenu(IMenuManager dialogMenu) { |
| // Add custom actions |
| super.fillDialogMenu(dialogMenu); |
| } |
| |
| } |