/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.views.outline; import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.swing.text.AbstractDocument.ElementEdit; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IContributionManager; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.IElementComparer; import org.eclipse.jface.viewers.IOpenListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; 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.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; 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.CoolBar; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.PatternFilter; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.progress.WorkbenchJob; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.views.contentoutline.ContentOutlinePage; import com.aptana.ide.core.IdeLog; import com.aptana.ide.editors.UnifiedEditorsPlugin; import com.aptana.ide.editors.preferences.IPreferenceConstants; import com.aptana.ide.editors.unified.ContributedOutline; import com.aptana.ide.editors.unified.EditorFileContext; import com.aptana.ide.editors.unified.IFileService; import com.aptana.ide.editors.unified.IFileServiceChangeListener; import com.aptana.ide.editors.unified.UnifiedEditor; import com.aptana.ide.lexer.IRange; import com.aptana.ide.parsing.nodes.IParseNode; /** * @author Paul Colton * @author Kevin Sawicki * @author Kevin Lindsey */ public class UnifiedQuickOutlinePage extends ContentOutlinePage implements IUnifiedOutlinePage, IOpenListener { /** * Close listener. * @author Denis Denisenko */ public interface ICloseListener { /** * Does close. */ void doClose(); } /** * @author Pavel Petrochenko */ private static final class OpenExternalAction extends Action { IResolvableItem item; String editorId; /** * @param item */ OpenExternalAction(IResolvableItem item) { IEditorDescriptor editorDescriptor; try { editorDescriptor = IDE.getEditorDescriptor(item.getEditorInput().getName()); editorId = editorDescriptor.getId(); this.setImageDescriptor(editorDescriptor.getImageDescriptor()); } catch (PartInitException e) { IdeLog.logError(UnifiedEditorsPlugin.getDefault(), e.getMessage()); } this.item = item; this.setToolTipText(Messages.UnifiedOutlinePage_Open); } /** * opens editor on external item * * @see org.eclipse.jface.action.Action#run() */ public void run() { IEditorInput input = item.getEditorInput(); IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); try { IEditorPart openEditor = IDE.openEditor(activePage, input, editorId); if (openEditor instanceof ITextEditor) { ITextEditor ed = (ITextEditor) openEditor; if (item instanceof IRange) { int position = ((IRange) item).getStartingOffset(); ed.selectAndReveal(position, 0); return; } if (item instanceof OutlineItem) { int position = ((OutlineItem) item).getStartingOffset(); ed.selectAndReveal(position, 0); return; } } return; } catch (PartInitException e) { IdeLog.logError(UnifiedEditorsPlugin.getDefault(), e.getMessage()); return; } } } private static final int FILTER_REFRESH_DELAY = 200; private static final int REFRESH_DELAY = 500; private Composite _composite; private SashForm _outlineSash; private SashForm _outlineTabsSash; private CTabFolder _outlineTabs; private UnifiedEditor _editor; private PatternFilter _filter; private String _pattern; private WorkbenchJob _filterRefreshJob; private WorkbenchJob _delayedRefreshJob; private Text _searchBox; private TreeViewer _treeViewer; private IDocumentListener _documentListener; private IFileServiceChangeListener _serviceListener; private IDocument _document; private boolean _hide; private ActionContributionItem _sortItem; private ActionContributionItem _collapseItem; private ActionContributionItem _expandItem; private ActionContributionItem _hidePrivateItem; private ActionContributionItem _splitItem; private HidePrivateAction _hidePrivateAction; private Map<String, ContributedOutline> _outlines; private UnifiedOutlineProvider outlineProvider; private BaseFilter[] _filters; private ActionContributionItem openAction; /** * Close listeners. */ private List<ICloseListener> closeListeners = new ArrayList<ICloseListener>(); private ToolBarManager _toolbarManager; /** * UnifiedOutlinePage * * @param editor */ public UnifiedQuickOutlinePage(UnifiedEditor editor) { this._editor = editor; editor.getViewer().getTextWidget().addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { if (outlineProvider != null) { // notify provider that active editor was changed outlineProvider.pageActivated(UnifiedQuickOutlinePage.this); } } }); editor.getViewer().getTextWidget().addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { if (outlineProvider != null) { outlineProvider.pageClosed(UnifiedQuickOutlinePage.this); } } }); this._outlines = new HashMap<String, ContributedOutline>(); } /** * addOutline * * @param outline * @param name */ public void addOutline(ContributedOutline outline, String name) { this._outlines.put(name, outline); } /** * createComposite * * @param parent * @return Composite */ private Composite createComposite(Composite parent) { GridLayout contentAreaLayout = new GridLayout(); contentAreaLayout.numColumns = 1; contentAreaLayout.makeColumnsEqualWidth = false; contentAreaLayout.marginHeight = 0; contentAreaLayout.marginWidth = 0; contentAreaLayout.verticalSpacing = 0; contentAreaLayout.horizontalSpacing = 0; Composite result = new Composite(parent, SWT.NONE); result.setLayout(contentAreaLayout); result.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); return result; } /** * createContributedOutlines */ private void createContributedOutlines(CTabFolder outlineTabs, SashForm outlineSash) { Iterator<String> iter = this._outlines.keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); ContributedOutline outline = this._outlines.get(key); CTabItem tab = new CTabItem(outlineTabs, SWT.NONE); Composite previewComp = new Composite(outlineSash, SWT.NONE); GridLayout contentAreaLayout = new GridLayout(); contentAreaLayout.numColumns = 1; contentAreaLayout.makeColumnsEqualWidth = true; contentAreaLayout.marginHeight = 0; contentAreaLayout.marginWidth = 0; contentAreaLayout.verticalSpacing = 0; contentAreaLayout.horizontalSpacing = 0; previewComp.setLayout(contentAreaLayout); previewComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label outlineLabel = new Label(previewComp, SWT.NONE); outlineLabel.setText(key); outlineLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); tab.setText(key); SashForm preForm = new SashForm(outlineTabs, SWT.NONE); contentAreaLayout = new GridLayout(); contentAreaLayout.numColumns = 1; contentAreaLayout.makeColumnsEqualWidth = false; contentAreaLayout.marginHeight = 0; contentAreaLayout.marginWidth = 0; contentAreaLayout.verticalSpacing = 0; contentAreaLayout.horizontalSpacing = 0; preForm.setLayout(contentAreaLayout); preForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); outline.createControl(preForm); tab.setControl(preForm); } } /** * @see org.eclipse.ui.views.contentoutline.ContentOutlinePage#createControl(org.eclipse.swt.widgets.Composite) */ public void createControl(Composite parent) { createControl(parent, true); } /** * Creates page control. * @param parent - page parent. * @param createSearchArea - whether to create search area or it would be created separately * through calling {@link UnifiedQuickOutlinePage#createSearchArea(Composite)} by a third party. */ public void createControl(Composite parent, boolean createSearchArea) { // create main container this._composite = createComposite(parent); if (createSearchArea) { // create top strip and search area this.createSearchArea(this._composite, false); } // create tab sash this._outlineSash = this.createSash(this._composite); this.createSourceSash(); // create tabs this._outlineTabsSash = this.createOutlineTabs(this._composite); // create tree view this._treeViewer = this.createTreeViewer(this._outlineTabsSash); this._treeViewer.addSelectionChangedListener(this); // apply tree filters this._treeViewer.addFilter(new UnifiedViewerFilter(this)); this._filter = new PatternFilter() { /** * @see org.eclipse.ui.dialogs.PatternFilter#isLeafMatch(org.eclipse.jface.viewers.Viewer, java.lang.Object) */ protected boolean isLeafMatch(Viewer viewer, Object element) { boolean result = true; String label = null; if (element instanceof OutlineItem) { label = ((OutlineItem) element).getLabel(); } else if (element instanceof IParseNode) { label = UnifiedOutlineProvider.getInstance().getText(element); } if (label != null) { result = this.wordMatches(label); } return result; } }; this._treeViewer.addFilter(this._filter); // add filters for (BaseFilter filter : this._filters) { this._treeViewer.addFilter(filter); } // add listeners this._treeViewer.addOpenListener(this); // create filter refresh job this._filterRefreshJob = this.createRefreshJob(); this._filterRefreshJob.setSystem(true); // create delayed update job this._delayedRefreshJob = this.createDelayedRefreshJob(); this._delayedRefreshJob.setSystem(true); // create document change listener and add to editor this.createDocumentListener(); this.createServiceListener(); this._document = this._editor.getDocumentProvider().getDocument(this._editor.getEditorInput()); this._document.addDocumentListener(this._documentListener); this._editor.addFileServiceChangeListener(new IFileServiceChangeListener() { public void fileServiceChanged(IFileService newService) { if (_document != null) { _document.removeDocumentListener(_documentListener); } _document = _editor.getDocumentProvider().getDocument(_editor.getEditorInput()); if (_document != null) { _document.addDocumentListener(_documentListener); } } }); // refresh tree this.updateProviders(); this.refresh(); } /** * Reveals position. * @param documentPos - document position. */ public void revealPosition(int documentPos) { IStructuredContentProvider provider = createProvider(); final Object[] originalElements = provider.getElements(_treeViewer.getInput()); if (originalElements == null || originalElements.length == 0) { return; } //list of outline elements List<Object> elements = new ArrayList<Object>(); //map from outline elements to the list of its parent elements //we need such a construction due to the fact that OutlineItem has no information about its parent final Map<Object, List<Object>> parents = new IdentityHashMap<Object, List<Object>>(); for (Object el : originalElements) { elements.add(el); } if (provider instanceof ITreeContentProvider) { ITreeContentProvider treeContentProvider = (ITreeContentProvider) provider; for (Object element : originalElements) { expandElement(element, treeContentProvider, elements, parents); } } // TreePath path = new TreePath(new Object[]{elements.get(0), elements.get(4)}); // _treeViewer.setSelection(new TreeSelection(path), true); Object bestElement = null; int bestElementStartingOffset = -1; int bestElementEndingOffset = -1; for (Object element : elements) { int start = -1; int end = -1; if (element instanceof IParseNode) { IParseNode node = (IParseNode) element; start = node.getStartingOffset(); end = node.getEndingOffset(); } else if (element instanceof OutlineItem) { int refNodeStart = ((OutlineItem) element).getReferenceNode().getStartingOffset(); int refNodeEnd = ((OutlineItem) element).getReferenceNode().getEndingOffset(); int elementStart = ((OutlineItem) element).getStartingOffset(); int elementEnd = ((OutlineItem) element).getEndingOffset(); start = refNodeStart < elementStart ? refNodeStart : elementStart; end = refNodeEnd > elementEnd ? refNodeEnd : elementEnd; // start = ((OutlineItem) element).getStartingOffset(); // end = ((OutlineItem) element).getEndingOffset(); } if (start != -1) { if (start <= documentPos && end >= documentPos) { //choosing the node having the least length if (bestElement == null || bestElementEndingOffset - bestElementStartingOffset > end - start) { bestElement = element; bestElementStartingOffset = start; bestElementEndingOffset = end; } } } } final Object toReveal = bestElement; WorkbenchJob job = new WorkbenchJob("Initial reveal") {//$NON-NLS-1$ /** * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus runInUIThread(IProgressMonitor monitor) { if (_treeViewer.getControl().isDisposed()) { return Status.CANCEL_STATUS; } try { // don't want the user to see updates that will be made to the tree _treeViewer.getControl().setRedraw(false); _treeViewer.refresh(true); //_treeViewer.setSelection(new StructuredSelection(toReveal), true); List<Object> path = new ArrayList<Object>(); List<Object> p = parents.get(toReveal); if (p != null) { path.addAll(p); } path.add(toReveal); TreePath treePath = new TreePath(path.toArray()); _treeViewer.setSelection(new TreeSelection(treePath), true); } finally { // done updating the tree - set redraw back to true _treeViewer.getControl().setRedraw(true); } return Status.OK_STATUS; } }; if (bestElement != null) { job.schedule(FILTER_REFRESH_DELAY); } } /** * Creates new outline content provider. * Does not cache provider instance due to the fact that some * of the language providers are not reusable. * @return outline content provider */ private UnifiedOutlineProvider createProvider() { UnifiedOutlineProvider provider = new UnifiedOutlineProvider(); provider.loadExtensions(); provider.setOutlinePage(this); return provider; } /** * createDelayedRefreshJob * * @return workbench job */ private WorkbenchJob createDelayedRefreshJob() { return new WorkbenchJob("Refresh Content") { //$NON-NLS-1$ /** * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus runInUIThread(IProgressMonitor monitor) { try { if (_treeViewer.getControl().isDisposed()) { return Status.CANCEL_STATUS; } // refresh(); updateProviders(); _treeViewer.setSelection(null); _treeViewer.refresh(); } // SWT errors may be thrown here and will show as an error box since this is done on the UI thread // Catch everything and log it so that the dialog doesn't annoy the user since they may be typing into // the editor when this code throws errors and will impact them severely catch (Exception e) { IdeLog.logError(UnifiedEditorsPlugin.getDefault(), Messages.UnifiedOutlinePage_ErrorRefreshingOutline, e); } catch (Error e) { IdeLog.logError(UnifiedEditorsPlugin.getDefault(), Messages.UnifiedOutlinePage_ErrorRefreshingOutline, e); } return Status.OK_STATUS; } }; } /** * createDocumentListener */ private void createDocumentListener() { this._documentListener = new IDocumentListener() { /** * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentAboutToBeChanged(DocumentEvent event) { } /** * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) */ public void documentChanged(DocumentEvent event) { // cancel currently running job first, to prevent unnecessary redraw if (_delayedRefreshJob != null) { _delayedRefreshJob.cancel(); _delayedRefreshJob.schedule(REFRESH_DELAY); } } }; } /** * createOutlineTabs * * @param parent * @return tab folder */ private SashForm createOutlineTabs(Composite parent) { this._outlineTabs = new CTabFolder(parent, SWT.BOTTOM); this._outlineTabs.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); // create source tab SashForm sourceForm = new SashForm(this._outlineTabs, SWT.NONE); sourceForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); CTabItem sourceTab = new CTabItem(this._outlineTabs, SWT.NONE); sourceTab.setText(Messages.UnifiedOutlinePage_Source); sourceTab.setControl(sourceForm); this._outlineTabs.setSelection(sourceTab); // create contributed outline tabs this.createContributedOutlines(this._outlineTabs, this._outlineSash); return sourceForm; } /** * createRefreshJob * * @return Workbench job */ private WorkbenchJob createRefreshJob() { return new WorkbenchJob("Refresh Filter") {//$NON-NLS-1$ /** * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus runInUIThread(IProgressMonitor monitor) { if (_treeViewer.getControl().isDisposed()) { return Status.CANCEL_STATUS; } if (_pattern == null) { return Status.OK_STATUS; } _filter.setPattern(_pattern); try { // don't want the user to see updates that will be made to the tree _treeViewer.getControl().setRedraw(false); _treeViewer.refresh(true); if (_pattern.length() > 0) { /* * Expand elements one at a time. After each is expanded, check to see if the filter text has * been modified. If it has, then cancel the refresh job so the user doesn't have to endure * expansion of all the nodes. */ IStructuredContentProvider provider = (IStructuredContentProvider) _treeViewer .getContentProvider(); Object[] elements = provider.getElements(_treeViewer.getInput()); for (int i = 0; i < elements.length; i++) { if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } _treeViewer.expandToLevel(elements[i], AbstractTreeViewer.ALL_LEVELS); } TreeItem[] items = _treeViewer.getTree().getItems(); if (items.length > 0) { // to prevent scrolling _treeViewer.getTree().showItem(items[0]); } } } finally { // done updating the tree - set redraw back to true _treeViewer.getControl().setRedraw(true); } return Status.OK_STATUS; } }; } /** * createSash * * @param parent * @return sash form */ private SashForm createSash(Composite parent) { // create layout GridLayout contentAreaLayout = new GridLayout(); contentAreaLayout.numColumns = 1; contentAreaLayout.makeColumnsEqualWidth = false; contentAreaLayout.marginHeight = 0; contentAreaLayout.marginWidth = 0; contentAreaLayout.verticalSpacing = 0; contentAreaLayout.horizontalSpacing = 0; // create layout data GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true); gridData.exclude = true; // create form SashForm result = new SashForm(parent, SWT.VERTICAL); // set layout and layout data result.setLayoutData(gridData); result.setLayout(contentAreaLayout); result.setVisible(false); return result; } /** * Create search area * * @param parent - parent * @param embedded - whether to create embedded search area. * @return Composite */ public Composite createSearchArea(Composite parent, boolean embedded) { GridLayout contentAreaLayout = new GridLayout(); contentAreaLayout.numColumns = 1; contentAreaLayout.makeColumnsEqualWidth = false; contentAreaLayout.marginHeight = 3; contentAreaLayout.marginWidth = 0; contentAreaLayout.verticalSpacing = 0; contentAreaLayout.horizontalSpacing = 0; Composite top = new Composite(parent, SWT.NONE); top.setLayout(contentAreaLayout); top.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); // create layout contentAreaLayout = new GridLayout(); contentAreaLayout.numColumns = 3; contentAreaLayout.makeColumnsEqualWidth = false; contentAreaLayout.marginHeight = 0; contentAreaLayout.marginWidth = 0; contentAreaLayout.verticalSpacing = 0; contentAreaLayout.horizontalSpacing = 0; // create layout data GridData data = new GridData(); data.horizontalAlignment = GridData.FILL; data.horizontalSpan = 1; data.grabExcessHorizontalSpace = true; data.grabExcessVerticalSpace = false; // create layout Composite result = new Composite(top, SWT.NONE); // assign layout and layout data result.setLayout(contentAreaLayout); result.setLayoutData(data); // create label if (!embedded) { Label searchLabel = new Label(result, SWT.NONE); searchLabel.setText(Messages.UnifiedOutlinePage_Filter); } // create text box int style = 0; if (embedded) { style = SWT.SINGLE | SWT.FOCUSED; } else { style = SWT.SINGLE | SWT.BORDER; } this._searchBox = new Text(result, style); this._searchBox.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); this._searchBox.setEditable(true); this._searchBox.addModifyListener(new ModifyListener() { /** * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent) */ public void modifyText(ModifyEvent e) { textChanged(); } }); this._searchBox.addKeyListener(new KeyListener(){ public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN) { _treeViewer.getControl().setFocus(); } } public void keyReleased(KeyEvent e) { } }); ToolBar filtersToolBar = new ToolBar(result, SWT.HORIZONTAL); filtersToolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); _toolbarManager = new ToolBarManager(filtersToolBar); final List<BaseFilter> filters = new ArrayList<BaseFilter>(); EditorFileContext fileContext = this._editor.getFileContext(); String language = fileContext.getDefaultLanguage(); for (FilterActionInfo filterInfo : UnifiedOutlineProvider.getInstance().getFilterActionInfos(language)) { final FilterActionInfo info = filterInfo; _toolbarManager.add(new ContributionItemStub() { public String getId() { return info.getName(); } @Override public void fill(ToolBar parent, int index) { final BaseFilter filter = info.getFilter(); ToolItem item = new ToolItem(parent, SWT.CHECK); filters.add(index, filter); item.setImage(info.getImageDescriptor().createImage()); item.setToolTipText(info.getToolTip()); item.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { ToolItem item = (ToolItem) e.widget; filter.setActive(item.getSelection()); _treeViewer.refresh(true); } }); } }); } _toolbarManager.update(false); _toolbarManager.getControl().update(); this._filters = filters.toArray(new BaseFilter[filters.size()]); return result; } /** * createServiceListener */ private void createServiceListener() { this._serviceListener = new IFileServiceChangeListener() { public void fileServiceChanged(IFileService newService) { if (_document != null) { _document.removeDocumentListener(_documentListener); } _document = _editor.getDocumentProvider().getDocument(_editor.getEditorInput()); if (_document != null) { _document.addDocumentListener(_documentListener); } } }; } /** * createSourceSash */ private void createSourceSash() { Composite previewComp = new Composite(this._outlineSash, SWT.NONE); GridLayout contentAreaLayout = new GridLayout(); contentAreaLayout.numColumns = 1; contentAreaLayout.makeColumnsEqualWidth = false; contentAreaLayout.marginHeight = 0; contentAreaLayout.marginWidth = 0; contentAreaLayout.verticalSpacing = 0; contentAreaLayout.horizontalSpacing = 0; previewComp.setLayout(contentAreaLayout); previewComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label outlineLabel = new Label(previewComp, SWT.NONE); outlineLabel.setText(Messages.UnifiedOutlinePage_Source); outlineLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); } /** * createTreeViewer * * @param parent * @return TreeViewer */ private TreeViewer createTreeViewer(Composite parent) { TreeViewer result = new TreeViewer(new Tree(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL)); //outlineProvider = UnifiedOutlineProvider.getInstance(); outlineProvider = createProvider(); result.setLabelProvider(outlineProvider); result.setContentProvider(outlineProvider); result.setInput(this._editor); // add selection changed listener result.addSelectionChangedListener(new ISelectionChangedListener() { /** * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event) { // IStructuredSelection selection = (IStructuredSelection) event.getSelection(); // // if (selection.size() == 1) // { // Object item = selection.getFirstElement(); // // if (item != null && item instanceof OutlineItem) // { // OutlineItem outlineItem = (OutlineItem) selection.getFirstElement(); // // // Only select and reveal in editor if the tree viewer is focused meaning the selection // // originated from a user selection on the tree itself // // // move cursor to start of this item's text // if (_treeViewer != null && _treeViewer.getTree() != null && !_treeViewer.getTree().isDisposed() // && _treeViewer.getTree().isFocusControl()) // { // _editor.selectAndReveal(outlineItem.getStartingOffset(), 0); // } // } // } } }); result.setComparer(new IElementComparer() { /** * @see org.eclipse.jface.viewers.IElementComparer#equals(java.lang.Object, java.lang.Object) */ public boolean equals(Object a, Object b) { boolean result = false; if (a instanceof OutlineItem && b instanceof OutlineItem) { OutlineItem item1 = (OutlineItem) a; OutlineItem item2 = (OutlineItem) b; result = item1.equals(item2); } else if (a instanceof IParseNode && b instanceof IParseNode) { if (a == b) { result = true; } else { IParseNode node1 = (IParseNode) a; IParseNode node2 = (IParseNode) b; String path1 = node1.getUniquePath(); String path2 = node2.getUniquePath(); result = path1.equals(path2); } } else { result = (a == b); } return result; } /** * @see org.eclipse.jface.viewers.IElementComparer#hashCode(java.lang.Object) */ public int hashCode(Object element) { return 0; } }); return result; } /** * @see org.eclipse.ui.part.Page#dispose() */ public void dispose() { super.dispose(); if (this._delayedRefreshJob != null && this._editor != null) { this._editor.getDocumentProvider().getDocument(this._editor.getEditorInput()).removeDocumentListener( this._documentListener); this._editor.removeFileServiceChangeListener(_serviceListener); } } /** * getContributedOutlines * * @return HashMap */ public Map<String, ContributedOutline> getContributedOutlines() { return this._outlines; } /** * @see org.eclipse.ui.views.contentoutline.ContentOutlinePage#getControl() */ public Control getControl() { return this._composite; } /** * getOutlineSash * * @return SashForm */ public SashForm getOutlineSash() { return this._outlineSash; } /** * getOutlineTabs * * @return CTabFolder */ public CTabFolder getOutlineTabs() { return this._outlineTabs; } /** * @see org.eclipse.ui.views.contentoutline.ContentOutlinePage#getTreeViewer() */ public TreeViewer getTreeViewer() { return this._treeViewer; } /** * @return editor */ public UnifiedEditor getUnifiedEditor() { return this._editor; } /** * hidePrivateMembers * * @return boolean */ public boolean hidePrivateMembers() { return this._hide; } /** * refresh */ public void refresh() { if (!_treeViewer.getControl().isDisposed()) { this._treeViewer.refresh(); } } /** * removeOpenActionIfNeeded */ private void removeOpenActionIfNeeded() { if (this.openAction != null) { _toolbarManager.remove(openAction); openAction = null; _toolbarManager.update(false); } } /** * {@inheritDoc} */ public void open(OpenEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); // If a node in the outline view is selected if (selection.size() == 1) { Object element = selection.getFirstElement(); if (element instanceof IResolvableItem) { IResolvableItem item = (IResolvableItem) element; // if item is resolvable and is targeting on external content if (item.isResolvable()) { // selecting it in editor if (item instanceof IParseNode) { int position = ((IParseNode) item).getStartingOffset(); this._editor.selectAndReveal(position, 0); return; } } else { if (item instanceof OutlineItem) { OutlineItem outlineItem = (OutlineItem) selection.getFirstElement(); // Only select and reveal in editor if the tree viewer is focused meaning the selection // originated from a user selection on the tree itself // move cursor to start of this item's text if (_treeViewer != null && _treeViewer.getTree() != null && !_treeViewer.getTree().isDisposed() && _treeViewer.getTree().isFocusControl()) { _editor.selectAndReveal(outlineItem.getStartingOffset(), 0); } } } } if (element instanceof IRange) { int position = ((IRange) element).getStartingOffset(); this._editor.selectAndReveal(position, 0); } notifyCloseListeners(); } else { removeOpenActionIfNeeded(); this._editor.getViewer().removeRangeIndication(); } } // /** // * @see org.eclipse.ui.part.Page#setActionBars(org.eclipse.ui.IActionBars) // */ // public void setActionBars(IActionBars actionBars) // { // // add split action // if (this._outlines.size() > 0) // { // SplitOutlinesAction splitAction = new SplitOutlinesAction(this); // // splitAction.setEnabled(this._composite.isReparentable()); // this._splitItem = new ActionContributionItem(splitAction); // actionBars.getToolBarManager().add(this._splitItem); // } // this._actionBars = actionBars; // // add sort action // SortAction sortAction = new SortAction(this); // IPreferenceStore store = UnifiedEditorsPlugin.getDefault().getPreferenceStore(); // boolean sort = store.getBoolean(IPreferenceConstants.SORT_OUTLINE_ALPHABETICALLY); // sortAction.setChecked(sort); // if (sort) // { // getTreeViewer().setSorter(SortAction.SORTER); // } // this._sortItem = new ActionContributionItem(sortAction); // actionBars.getToolBarManager().add(this._sortItem); // // // add hide private members action // this._hidePrivateAction = new HidePrivateAction(this); // this._hidePrivateItem = new ActionContributionItem(this._hidePrivateAction); // actionBars.getToolBarManager().add(this._hidePrivateItem); // // // add collapse all action // CollapseAction collapseAction = new CollapseAction(this); // this._collapseItem = new ActionContributionItem(collapseAction); // actionBars.getToolBarManager().add(this._collapseItem); // // Action expandAction = new Action(Messages.UnifiedOutlinePage_ExpandAll) // { // public void run() // { // getTreeViewer().expandAll(); // } // }; // expandAction.setImageDescriptor(UnifiedEditorsPlugin.getImageDescriptor("icons/expandall.gif")); //$NON-NLS-1$ // expandAction.setToolTipText(Messages.UnifiedOutlinePage_CollapseAll); // this._expandItem = new ActionContributionItem(expandAction); // actionBars.getToolBarManager().add(this._expandItem); // // super.setActionBars(actionBars); // } /** * Contibutes actions to quick outline menu. * @param manager - menu manager. */ public void contributeToQuickOutlineMenu(IMenuManager manager) { // add split action if (this._outlines.size() > 0) { SplitOutlinesAction splitAction = new SplitOutlinesAction(this); splitAction.setEnabled(this._composite.isReparentable()); this._splitItem = new ActionContributionItem(splitAction); manager.add(this._splitItem); } // add sort action SortAction sortAction = new SortAction(this); IPreferenceStore store = UnifiedEditorsPlugin.getDefault().getPreferenceStore(); boolean sort = store.getBoolean(IPreferenceConstants.SORT_OUTLINE_ALPHABETICALLY); sortAction.setChecked(sort); if (sort) { getTreeViewer().setSorter(SortAction.SORTER); } this._sortItem = new ActionContributionItem(sortAction); manager.add(this._sortItem); // add hide private members action this._hidePrivateAction = new HidePrivateAction(this); this._hidePrivateItem = new ActionContributionItem(this._hidePrivateAction); manager.add(this._hidePrivateItem); // add collapse all action CollapseAction collapseAction = new CollapseAction(this); this._collapseItem = new ActionContributionItem(collapseAction); manager.add(this._collapseItem); Action expandAction = new Action(Messages.UnifiedOutlinePage_ExpandAll) { public void run() { getTreeViewer().expandAll(); } }; expandAction.setImageDescriptor(UnifiedEditorsPlugin.getImageDescriptor("icons/expandall.gif")); //$NON-NLS-1$ expandAction.setToolTipText(Messages.UnifiedOutlinePage_CollapseAll); this._expandItem = new ActionContributionItem(expandAction); manager.add(this._expandItem); } /** * @see org.eclipse.ui.views.contentoutline.ContentOutlinePage#setFocus() */ public void setFocus() { this._treeViewer.getControl().setFocus(); } /** * Gets searhbox. */ public Control getSearchBox() { return _searchBox; } /** * textChanged */ private void textChanged() { // cancel currently running job first, to prevent unnecessary redraw this._filterRefreshJob.cancel(); this._filterRefreshJob.schedule(FILTER_REFRESH_DELAY); // set current filter pattern this._pattern = this._searchBox.getText(); this._filter.setPattern(this._pattern); // update all contributed outline filter patterns Iterator<ContributedOutline> outlineIter = _outlines.values().iterator(); while (outlineIter.hasNext()) { outlineIter.next().setFilterText(this._pattern); } } /** * togglePrivateMemberVisibility */ public void togglePrivateMemberVisibility() { this._hide = (this._hide == false); this.refresh(); } /** * Adds close listener. * @param listener - listener. */ public void addCloseListener(ICloseListener listener) { closeListeners.add(listener); } /** * {@inheritDoc} */ public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection) event.getSelection(); if (openAction != null) { _toolbarManager.remove(openAction); _toolbarManager.update(true); _toolbarManager.getControl().getParent().layout(true, true); } // If a node in the outline view is selected if (selection.size() == 1) { Object element = selection.getFirstElement(); if (element instanceof IResolvableItem) { IResolvableItem item = (IResolvableItem) element; // if item is resolvable and is targeting on external content // removing all item if needed if (item.isResolvable()) { openAction = new ActionContributionItem(new OpenExternalAction(item)); _toolbarManager.add(openAction); _toolbarManager.update(true); _toolbarManager.getControl().getParent().layout(true, true); // while item is not able to be resolved in current file // getting parent item while (!item.stillHighlight()) { item = item.getParentItem(); if (item == null) { return; } } // selecting it in editor if (item instanceof IParseNode) { int position = ((IParseNode) item).getStartingOffset(); this._editor.selectAndReveal(position, 0); return; } } else { // removing item from toolbar removeOpenActionIfNeeded(); } } removeOpenActionIfNeeded(); } else { removeOpenActionIfNeeded(); this._editor.getViewer().removeRangeIndication(); } } /** * Notifies close listeners. */ private void notifyCloseListeners() { for (ICloseListener listener : closeListeners) { listener.doClose(); } } /** * updateProviders */ private void updateProviders() { EditorFileContext fileContext = _editor.getFileContext(); if (fileContext != null) { UnifiedOutlineProvider provider = UnifiedOutlineProvider.getInstance(); provider.setCurrentLanguage(fileContext.getDefaultLanguage()); provider.fireBeforeRefreshEvent(fileContext); } } /** * Expands a tree element, also feels parents map. * @param element - element to expand. * @param treeContentProvider - tree content provider. * @param elements - elements. */ private void expandElement(Object element, ITreeContentProvider treeContentProvider, List<Object> elements, Map<Object, List<Object>> parents) { //getting children Object[] children = treeContentProvider.getChildren(element); List<Object> elementParentsList = parents.get(element); for (Object child : children) { //adding child to the elements list elements.add(child); //filling parents list for the child ArrayList<Object> parentsList = new ArrayList<Object>(); //adding list of parent's parents, if exists if (elementParentsList != null) { parentsList.addAll(elementParentsList); } //adding parent parentsList.add(element); parents.put(child, parentsList); } //expanding children for (Object child : children) { expandElement(child, treeContentProvider, elements, parents); } } }