/**
* 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);
}
}
}