/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.outline;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.ListResourceBundle;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.ViewerFilter;
import org.python.pydev.core.bundle.ImageCache;
import org.python.pydev.core.docutils.PySelection;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.IPyEditListener;
import org.python.pydev.editor.IPyEditListener2;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.parser.visitors.scope.ASTEntryWithChildren;
import org.python.pydev.ui.UIConstants;
/**
* This action keeps the outline synched with the text selected in the text
* editor.
*
* Design notes:
* It's linked on the constructor and unlinked in the destructor.
*
* It considers that it's always linked, even if the action is inactive, but before executing it, a
* check is done to see if it's active or not.
*
* @author Fabio
*/
public class OutlineLinkWithEditorAction extends AbstractOutlineFilterAction implements IPyEditListener,
IPyEditListener2 {
private static final String PREF_LINK_WITH_EDITOR = "org.python.pydev.PREF_LINK_WITH_EDITOR";
private WeakReference<PyEdit> pyEdit;
public OutlineLinkWithEditorAction(PyOutlinePage page, ImageCache imageCache) {
super("Link With Editor", page, imageCache, PREF_LINK_WITH_EDITOR, UIConstants.SYNC_WITH_EDITOR);
pyEdit = new WeakReference<PyEdit>(page.editorView);
relink();
}
/**
* When called, it STOPS hearing notifications to update the outline when the cursor changes positions.
*/
public void unlink() {
PyEdit edit = pyEdit.get();
if (edit != null) {
edit.removePyeditListener(this);
}
}
/**
* When called, it STARTS hearing notifications to update the outline when the cursor changes positions.
*/
public void relink() {
PyEdit edit = pyEdit.get();
if (edit != null) {
edit.addPyeditListener(this);
}
}
public void dispose() {
unlink();
}
@Override
protected ViewerFilter createFilter() {
throw new RuntimeException(
"Not implemented: as setActionEnabled is overriden, this action is not needed (as this is not a filter action).");
}
/**
* Overridden to enable the linking with the editor instead of having a filter created.
*/
@Override
protected void setActionEnabled(boolean enableAction) {
PyOutlinePage p = this.page.get();
if (p != null) {
p.getStore().setValue(PREF_LINK_WITH_EDITOR, enableAction);
if (enableAction && pyEdit != null) {
PyEdit edit = pyEdit.get();
if (edit != null) {
handleCursorPositionChanged(edit, new PySelection(edit));
}
}
}
}
public void onCreateActions(ListResourceBundle resources, PyEdit edit, IProgressMonitor monitor) {
}
public void onDispose(PyEdit edit, IProgressMonitor monitor) {
}
public void onSave(PyEdit edit, IProgressMonitor monitor) {
}
public void onSetDocument(IDocument document, PyEdit edit, IProgressMonitor monitor) {
}
/**
* Hear mouse selections to update the selection in the outline
*/
public void handleCursorPositionChanged(PyEdit edit, PySelection ps) {
PyOutlinePage p = this.page.get();
if (p != null && edit != null) {
if (isChecked()) {
doLinkOutlinePosition(edit, p, ps);
}
}
}
/**
* Keeps the outline linked with the editor.
*/
protected void doLinkOutlinePosition(PyEdit edit, PyOutlinePage p, PySelection ps) {
ITextSelection t = ps.getTextSelection();
IOutlineModel outlineModel = p.model;
if (outlineModel != null) {
StructuredSelection sel = getSelectionPosition(outlineModel.getRoot(), t);
if (sel != null) {
// we don't want to hear our own selections
p.unlinkAll();
try {
p.setSelection(sel);
} finally {
p.relinkAll();
}
}
}
}
/**
* Convert the text selection to a model node in the outline (parsed item tree path).
*/
private StructuredSelection getSelectionPosition(ParsedItem r, ITextSelection t) {
try {
ArrayList<ParsedItem> sel = new ArrayList<ParsedItem>();
if (r != null) {
do {
ParsedItem item = findSel(r, t.getStartLine() + 1);
if (item != null) {
sel.add(item);
}
r = item;
} while (r != null);
}
TreePath treePath = null;
if (sel != null && sel.size() > 0) {
treePath = new TreePath(sel.toArray());
}
if (treePath != null) {
return new TreeSelection(treePath);
}
} catch (Exception e) {
Log.log(e);
}
return null;
}
/**
* @return The parsed item that should be selected given the startLine passed.
*/
private ParsedItem findSel(ParsedItem r, int startLine) {
ParsedItem prev = null;
ParsedItem[] children = r.getChildren();
if (children != null) {
for (ParsedItem i : children) {
ASTEntryWithChildren astThis = i.getAstThis();
if (astThis != null && astThis.node != null) {
if (astThis.node.beginLine == startLine) {
prev = i;
break;
}
if (astThis.node.beginLine > startLine) {
break;
}
}
prev = i;
}
}
return prev;
}
}