package net.sf.colorer.eclipse.outline;
import java.util.Vector;
import net.sf.colorer.HRCParser;
import net.sf.colorer.eclipse.ColorerPlugin;
import net.sf.colorer.eclipse.ImageStore;
import net.sf.colorer.eclipse.Messages;
import net.sf.colorer.eclipse.editors.ColorerEditor;
import net.sf.colorer.editor.BaseEditor;
import net.sf.colorer.editor.OutlineListener;
import net.sf.colorer.impl.Logger;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.model.WorkbenchViewerSorter;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
/**
* Content outline page for the Colorer editor.
* Allows to view General Outline, Error and Internal Parse Tree for
* currently selected editor window.
*/
public class ColorerContentOutlinePage extends ContentOutlinePage
implements OutlineListener
{
Action fLinkToEditorAction = new Action("Link", Action.AS_CHECK_BOX){
public void run() {
prefStore.setValue("outline.link", isChecked());
}
};
ISelectionListener thisSelectionListener = new ISelectionListener (){
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
ISelection sel = selection;
if (Logger.TRACE){
Logger.trace("ColorerContentOutlinePage", "selectionChanged:"+selection);
}
if (!fLinkToEditorAction.isChecked() || sel == null || !(sel instanceof ITextSelection))
return;
int line = ((ITextSelection)sel).getStartLine();
Object element = fActiveOutlineSource.getItemByLine(line);
if (element == null)
return;
fProgrammaticChange = true;
getTreeViewer().expandToLevel(element, 0);
getTreeViewer().setSelection(new StructuredSelection(element), true);
if (Logger.TRACE){
Logger.trace("ColorerContentOutlinePage", "selected:"+element);
}
}
};
class TreeAction extends Action {
public TreeAction() {
super(Messages.get("outline.tree"), ImageStore.getID("outline-tree"));
setToolTipText(Messages.get("outline.tree.tooltip"));
setHoverImageDescriptor(ImageStore.getID("outline-tree-hover"));
setChecked(ColorerPlugin.getDefault().getPreferenceStore().getBoolean(
"Outline.Hierarchy"));
};
public void run() {
setChecked(isChecked());
ColorerPlugin.getDefault().getPreferenceStore().setValue("Outline.Hierarchy",
isChecked());
fActiveOutlineSource.setHierarchy(isChecked());
}
}
class SortAction extends Action {
private ViewerSorter sorter = new WorkbenchViewerSorter();
public SortAction() {
super(Messages.get("outline.sort"), ImageStore.getID("outline-sort"));
setToolTipText(Messages.get("outline.sort.tooltip"));
setHoverImageDescriptor(ImageStore.getID("outline-sort-hover"));
setChecked(ColorerPlugin.getDefault().getPreferenceStore().getBoolean("Outline.Sort"));
getTreeViewer().setSorter(isChecked() ? sorter : null);
};
public void run() {
setChecked(isChecked());
BusyIndicator.showWhile(getControl().getDisplay(), new Runnable(){
public void run() {
getTreeViewer().setSorter(isChecked() ? sorter : null);
}
});
ColorerPlugin.getDefault().getPreferenceStore().setValue("Outline.Sort", isChecked());
fActiveOutlineSource.setSorting(isChecked());
}
}
class OutlineModeAction extends Action {
String id;
IWorkbenchOutlineSource outliner;
OutlineModeAction(String id, IWorkbenchOutlineSource outliner) {
this.id = id;
this.outliner = outliner;
this.setText(Messages.get("outline.options."+id));
this.setImageDescriptor(ImageStore.getID("outline-options-"+id));
setChecked(fActiveOutlineSource == outliner);
}
public void run() {
if (fActiveOutlineSource != outliner){
setActiveOutliner(outliner, this);
update();
}
};
}
IWorkbenchOutlineSource fStructureOutline, fParseTreeOutline;
IWorkbenchOutlineSource fActiveOutlineSource;
OutlineModeAction structureModeAction, parseTreeModeAction;
OutlineModeAction activeAction;
private Vector selectionListeners = new Vector();
Thread backgroundUpdater = null;
boolean outlineModified = true;
long prevTime = 0;
int UPDATE_DELTA = 2000;
boolean fProgrammaticChange = false;
IPreferenceStore prefStore;
BaseEditor fBaseEditor;
ColorerEditor fEditor;
public ColorerContentOutlinePage() {
super();
prefStore = ColorerPlugin.getDefault().getPreferenceStore();
}
public void attach(ColorerEditor editor){
detach();
fEditor = editor;
fBaseEditor = (BaseEditor)editor.getAdapter(BaseEditor.class);
HRCParser hp = fBaseEditor.getParserFactory().getHRCParser();
fStructureOutline = new WorkbenchOutliner(hp.getRegion("def:Outlined"));
fParseTreeOutline = new ParseTreeOutliner();
structureModeAction = new OutlineModeAction("Structure", fStructureOutline);
parseTreeModeAction = new OutlineModeAction("ParseTree", fParseTreeOutline);
fLinkToEditorAction.setImageDescriptor(ImageStore.getID("regions-tree-link"));
fLinkToEditorAction.setToolTipText(Messages.get("outline.link"));
fLinkToEditorAction.setChecked(prefStore.getBoolean("outline.link"));
setActiveOutliner(fStructureOutline, structureModeAction);
fActiveOutlineSource.setHierarchy(ColorerPlugin.getDefault().getPreferenceStore().getBoolean(
"Outline.Hierarchy"));
fEditor.getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(thisSelectionListener);
notifyUpdate();
backgroundUpdater = new Thread("backgroundUpdater"){
Display rootDisplay = Display.getCurrent();
public void run() {
while (true) {
try {
sleep(UPDATE_DELTA);
} catch (InterruptedException e) {
break;
};
if (Thread.currentThread() != backgroundUpdater)
break;
rootDisplay.syncExec(new Runnable(){
public void run() {
updateIfChanged();
}
});
};
};
};
backgroundUpdater.start();
}
public void detach() {
if (fEditor != null){
fEditor.getSite().getWorkbenchWindow().getSelectionService().removePostSelectionListener(thisSelectionListener);
}
setActiveOutliner(null, null);
backgroundUpdater = null;
fBaseEditor = null;
fEditor = null;
fStructureOutline = null;
fParseTreeOutline = null;
};
public void dispose() {
detach();
super.dispose();
}
void setActiveOutliner(IWorkbenchOutlineSource newOutliner, OutlineModeAction action){
if (fActiveOutlineSource != null){
fActiveOutlineSource.removeUpdateListener(this);
if (fBaseEditor != null){
fActiveOutlineSource.detachOutliner(fBaseEditor);
}
if (activeAction != null){
activeAction.setChecked(false);
}
}
fActiveOutlineSource = newOutliner;
activeAction = action;
if (fActiveOutlineSource != null){
fActiveOutlineSource.addUpdateListener(this);
fActiveOutlineSource.clear();
if (fBaseEditor != null){
fActiveOutlineSource.attachOutliner(fBaseEditor);
}
// Sync UI
activeAction.setChecked(true);
// invalidate whole syntax to collect new outline
fEditor.invalidateSyntax();
}
}
void update() {
if (getControl() == null) { return; }
if (getControl().isDisposed()) { return; }
getControl().setRedraw(false);
int hpos = getTreeViewer().getTree().getHorizontalBar().getSelection();
int vpos = getTreeViewer().getTree().getVerticalBar().getSelection();
getTreeViewer().setInput(fActiveOutlineSource);
getTreeViewer().expandAll();
getControl().setRedraw(true);
getTreeViewer().getTree().getHorizontalBar().setSelection(hpos);
getTreeViewer().getTree().getVerticalBar().setSelection(vpos);
}
public void updateIfChanged() {
if (outlineModified) {
update();
outlineModified = false;
};
}
public void notifyUpdate() {
outlineModified = true;
long cTime = System.currentTimeMillis();
if (cTime - prevTime > UPDATE_DELTA && fActiveOutlineSource != null) {
updateIfChanged();
prevTime = System.currentTimeMillis();
};
}
public void addSelectionChangedListener(ISelectionChangedListener listener) {
selectionListeners.add(listener);
}
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
selectionListeners.remove(listener);
}
public void createControl(Composite parent) {
super.createControl(parent);
TreeViewer viewer = getTreeViewer();
getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent event) {
if (fProgrammaticChange) {
fProgrammaticChange = false;
return;
}
Object[] list = selectionListeners.toArray();
for(int idx = list.length-1; idx >=0; idx--) {
((ISelectionChangedListener)list[idx]).selectionChanged(event);
}
}
});
IToolBarManager toolBarManager = getSite().getActionBars().getToolBarManager();
if (toolBarManager != null) {
toolBarManager.add(new TreeAction());
toolBarManager.add(new SortAction());
toolBarManager.add(fLinkToEditorAction);
};
IMenuManager menuManager = getSite().getActionBars().getMenuManager();
if (menuManager != null) {
menuManager.add(structureModeAction);
menuManager.add(parseTreeModeAction);
}
viewer.setContentProvider(new WorkbenchContentProvider());
viewer.setLabelProvider(new WorkbenchLabelProvider());
viewer.setInput(fActiveOutlineSource);
}
}
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Colorer Library
*
* The Initial Developer of the Original Code is
* Igor Russkih <irusskih at gmail dot com>.
* Portions created by the Initial Developer are Copyright (C) 1999-2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */