/*
* Created on Apr 6, 2004
*
* The MIT License
* Copyright (c) 2004 Rob Rohan
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cfeclipse.cfml.views.contentoutline;
//import org.eclipse.core.resources.IResource;
import java.util.Iterator;
import org.cfeclipse.cfml.editors.CFMLEditor;
import org.cfeclipse.cfml.editors.ICFDocument;
import org.cfeclipse.cfml.editors.actions.GotoFileAction;
import org.cfeclipse.cfml.parser.CFDocument;
import org.cfeclipse.cfml.parser.CFNodeList;
import org.cfeclipse.cfml.parser.docitems.DocItem;
import org.cfeclipse.cfml.parser.docitems.ScriptItem;
import org.cfeclipse.cfml.parser.docitems.TagItem;
import org.cfeclipse.cfml.util.CFPluginImages;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
/**
* @author Rob
*
* This is the default content outline view
*/
public class CFContentOutlineView extends ContentOutlinePage implements IPartListener, IPropertyListener,
ISelectionListener {
public static final String ID_CONTENTOUTLINE = "org.cfeclipse.cfml.views.contentoutline.cfcontentoutlineview";
private static final int EXPAND_TO_LEVEL= 2;
protected Action jumpAction, selectAction, deleteItem, expandAction, collapseAction, filterOnAction, openAction,
removeFilters;
protected Action filters[];
private boolean fSelectionFromEditor = false;
private static String filter = "";
protected static GotoFileAction gfa = new GotoFileAction();
public void createControl(Composite parent) {
super.createControl(parent);
DocItem root = getRootInput();
TreeViewer viewer = getTreeViewer();
viewer.setContentProvider(new OutlineContentProvider(root));
viewer.setLabelProvider(new OutlineLabelProvider());
viewer.addSelectionChangedListener(this);
// this listener listens to the editor (vs. this outline view)
getSite().getPage().addPostSelectionListener(this);
createActions();
createToolbar();
createContextMenu();
hookGlobalActions();
hookDoubleClickAction();
reload(root);
}
protected void fireSelectionChanged(ISelection selection) {
System.out.println("wee");
}
/**
* Gets a 1 level tree of items in the document of a type. See the parser
* for the filter syntax
*
* @param filter
* @return
*/
public DocItem getItems(String filter) {
DocItem scratch = new TagItem(1, 1, 1, "root");
DocItem rootItem = getRootInput();
CFNodeList nodes = rootItem.selectNodes(filter);
Iterator i = nodes.iterator();
while (i.hasNext()) {
try {
scratch.addChild((DocItem) i.next());
} catch (Exception e) {
System.err.println("Tree item set error ");
e.printStackTrace();
}
}
return scratch;
}
/**
* Gets the root element of the document. Saves information into a local
* cache so if there is an error in the parser it uses the last known good
* outline (which kind of sucks but it looks better)
*
* @return the root directory
*/
public DocItem getRootInput() {
try {
DocItem docRoot = null;
IEditorPart iep = getSite().getPage().getActiveEditor();
iep.addPropertyListener(this);
getSite().getPage().addPartListener(this);
ITextEditor ite = (ITextEditor) iep;
ICFDocument icfd = null;
CFDocument cfd = null;
if (ite.getDocumentProvider().getDocument(iep.getEditorInput()) instanceof ICFDocument) {
icfd = (ICFDocument) ite.getDocumentProvider().getDocument(iep.getEditorInput());
cfd = icfd.getCFDocument();
}
// icfd.clearAllMarkers();
// icfd.parseDocument();
if (cfd != null) {
docRoot = cfd.getDocumentRoot();
// lastDocRoot = docRoot;
}
if (docRoot != null) {
return docRoot;
} else {
return createFakeRoot();
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
// a fake root
return createFakeRoot();
}
private TagItem createFakeRoot() {
TagItem tg = new TagItem(1, 1, 1, "Unk");
return tg;
}
/**
* gets the currently selected item in docitem form or <code>null</code> if
* there is none
*
* @return
*/
private Iterator getSelectedDocItems() {
Iterator selecteditem = null;
// can't do much if nothing is selected
if (getTreeViewer().getSelection().isEmpty()) {
return null;
} else {
IStructuredSelection selection = (IStructuredSelection) getTreeViewer().getSelection();
selecteditem = selection.iterator();
}
return selecteditem;
}
/**
* gets the currently selected item in docitem form or <code>null</code> if
* there is none
*
* @return
*/
private DocItem getSelectedDocItem() {
Iterator selecteditems = getSelectedDocItems();
// can't do much if nothing is selected
if (selecteditems == null || !selecteditems.hasNext()) {
return null;
}
return (DocItem) selecteditems.next();
}
/**
* for recursive parent expansion in setSelectedDocItem
*
* @return
*/
private void expandParentItems(DocItem item) {
// can't do much if nothing is selected
DocItem parentItem = item.getParent();
if (parentItem != null) {
TreeViewer tree = getTreeViewer();
tree.setExpandedState(parentItem, true);
tree.refresh(parentItem, false);
expandParentItems(parentItem);
}
}
/**
* sets the currently selected item in docitem form or <code>null</code> if
* there is none
* @return
*/
private void setSelectedDocItem(DocItem item) {
// can't do much if nothing is selected
if (item != null) {
TreeViewer tree = getTreeViewer();
fSelectionFromEditor = true;
tree.setSelection(new StructuredSelection(item), false);
fSelectionFromEditor = false;
//tree.refresh(item, false);
tree.setExpandedState(item, true);
tree.reveal(item);
// reveal takes care of this. Doh!
//expandParentItems(item);
}
}
/**
* Selects the item(s) within the source code view
*/
protected void selectItem() {
// get a handle to the current editor and assign it to our temp action
IEditorPart iep = getSite().getPage().getActiveEditor();
Iterator selecteditems = getSelectedDocItems();
if (!selecteditems.hasNext())
return;
ITextEditor editor = (ITextEditor) iep;
DocItem firstItem = ((DocItem) selecteditems.next());
int startPos = firstItem.getStartPosition();
int endPos = firstItem.getEndPosition();
if (!selecteditems.hasNext()) {
// select whole tag
if(firstItem.getClass().getName().endsWith("CfmlComment")){
editor.selectAndReveal(firstItem.getStartPosition(), firstItem.getEndPosition() - firstItem.getStartPosition() + 1);
return;
}
if (firstItem instanceof TagItem) {
TagItem cti = (TagItem) firstItem;
if (cti.matchingItem != null) {
if (cti.matchingItem.getStartPosition() < cti.getStartPosition()) {
startPos = cti.matchingItem.getStartPosition();
endPos = cti.getEndPosition();
} else {
startPos = cti.getStartPosition();
endPos = cti.matchingItem.getEndPosition();
}
} else {
startPos = cti.getStartPosition();
endPos = cti.getEndPosition();
}
} else {
ScriptItem csi = (ScriptItem) firstItem;
startPos = csi.getStartPosition();
endPos = csi.getEndPosition();
}
} else {
// otherwise select selected items
while (selecteditems.hasNext()) {
endPos = ((DocItem) selecteditems.next()).getEndPosition();
}
}
editor.selectAndReveal(startPos, endPos - startPos + 1);
}
/**
* Gets the selected item parses it, and adds the defined stuff to the
* editor
*/
protected void jumpToItem() {
// get a handle to the current editor and assign it to our temp action
IEditorPart iep = getSite().getPage().getActiveEditor();
DocItem selecteditem = getSelectedDocItem();
if (selecteditem == null)
return;
ITextEditor editor = (ITextEditor) iep;
// editor.setHighlightRange(selecteditem.getStartPosition(),selecteditem.getMatchingItem().getEndPosition(),true);
editor.setHighlightRange(selecteditem.getStartPosition(), 0, true);
}
/**
* filters the outline on the currenly selected element
*/
protected void filterOnSelected() {
DocItem selecteditem = getSelectedDocItem();
if (selecteditem == null)
return;
filter = "//" + selecteditem.getName();
reload();
}
public void reload(DocItem root) {
// saveExpandedElements();
getTreeViewer().setInput(root);
if (filter.length() == 0) {
removeFilters.setEnabled(false);
} else {
removeFilters.setEnabled(true);
}
getControl().getDisplay().asyncExec(new Runnable() {
public void run() {
Control ctrl= getControl();
if (ctrl != null && !ctrl.isDisposed()) {
getTreeViewer().refresh();
updateTreeExpansion();
}
}
});
}
public void reload() {
if (filter.length() == 0) {
DocItem di = getRootInput();
reload(di);
} else {
reload(getItems(filter));
}
}
private void updateTreeExpansion() {
DocItem root = getRootInput();
if (!getTreeViewer().getExpandedState(root) && root.hasChildren() && root.getFirstChild().getName().compareToIgnoreCase("cfcomponent") == 0) {
getTreeViewer().setExpandedState(root.getFirstChild(), true);
getTreeViewer().refresh(root, false);
}
// boolean wasModelEmpty= fIsModelEmpty;
// fIsModelEmpty= fModel == null || fModel.getProjectNode() == null;
// if (wasModelEmpty && !fIsModelEmpty) {
// getTreeViewer().expandToLevel(EXPAND_TO_LEVEL);
// }
}
/**
* creates all the default actions
*/
protected void createActions() {
jumpAction = new Action("Jump To", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_SHOW)) {
public void run() {
// we only want to jump if the change came from tree viewer
// selection
jumpToItem();
}
};
jumpAction.setToolTipText("Jump to selected tag in the editor");
selectAction = new Action("Select", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_SHOW)) {
public void run() {
selectItem();
}
};
selectAction.setToolTipText("Select tag in the editor");
filterOnAction = new Action("Filter On This", CFPluginImages.getImageRegistry().getDescriptor(
CFPluginImages.ICON_TAG)) {
public void run() {
filterOnSelected();
}
};
filterOnAction.setToolTipText("Filter on selected tag type");
expandAction = new Action("Expand All", AbstractUIPlugin.imageDescriptorFromPlugin("org.eclipse.search",
"$nl$/icons/full/elcl16/expandall.gif")) {
public void run() {
getTreeViewer().expandAll();
}
};
expandAction.setToolTipText("Expand all");
collapseAction = new Action("Collapse All", AbstractUIPlugin.imageDescriptorFromPlugin("org.eclipse.search",
"$nl$/icons/full/elcl16/collapseall.gif")) {
public void run() {
getTreeViewer().collapseAll();
}
};
collapseAction.setToolTipText("Collapse all");
openAction = new Action("Open File", CFPluginImages.getImageRegistry()
.getDescriptor(CFPluginImages.ICON_IMPORT)) {
public void run() {
openFile();
}
};
removeFilters = new Action("Remove Filter", CFPluginImages.getImageRegistry().getDescriptor(
CFPluginImages.ICON_DELETE)) {
public void run() {
filter = "";
reload();
}
};
removeFilters.setToolTipText("Remove Filter");
deleteItem = new Action("Delete Item", CFPluginImages.getImageRegistry().getDescriptor(
CFPluginImages.ICON_DELETE)) {
public void run() {
// this doesn't do nothing
System.out.println("this should delete");
}
};
// /filters
filters = new Action[6];
filters[0] = new Action("Include", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_TAG)) {
public void run() {
filter = "//cfinclude";
reload(getItems(filter));
}
};
filters[1] = new Action("Module", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_TAG)) {
public void run() {
filter = "//cfmodule";
reload(getItems(filter));
}
};
filters[2] = new Action("Query", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_TAG)) {
public void run() {
filter = "//cfquery";
reload(getItems(filter));
}
};
filters[3] = new Action("Set", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_TAG)) {
public void run() {
filter = "//cfset";
reload(getItems(filter));
}
};
filters[4] = new Action("Case", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_TAG)) {
public void run() {
filter = "//cfcase";
reload(getItems(filter));
}
};
filters[5] = new Action("Custom", CFPluginImages.getImageRegistry().getDescriptor(CFPluginImages.ICON_TAG)) {
public void run() {
InputDialog pathdialog = new InputDialog(getSite().getShell(), "CFML Path Filter",
"Filter outline using path (i.e. \"//cfoutput\" for all the cfoutput tags):", "", null);
if (pathdialog.open() == org.eclipse.jface.window.Window.OK) {
String xpath = pathdialog.getValue();
if (xpath.length() > 0) {
filter = xpath;
reload(getItems(filter));
}
}
}
};
}
private void createContextMenu() {
// Create menu manager.
MenuManager menuMgr = new MenuManager();
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager mgr) {
fillContextMenu(mgr);
}
});
// Create menu.
Menu menu = menuMgr.createContextMenu(getTreeViewer().getControl());
getTreeViewer().getControl().setMenu(menu);
}
private void fillContextMenu(IMenuManager mgr) {
mgr.add(selectAction);
mgr.add(new Separator());
mgr.add(filterOnAction);
DocItem di = getSelectedDocItem();
if (di != null) {
String sname = di.getName();
if (filter.length() > 0) {
mgr.add(removeFilters);
}
if (sname.equals("cfinclude") || sname.equals("cfmodule")) {
mgr.add(openAction);
}
}
mgr.add(new Separator());
int flen = filters.length;
for (int i = 0; i < flen; i++) {
mgr.add(filters[i]);
}
}
protected void createToolbar() {
IToolBarManager toolbarManager = super.getSite().getActionBars().getToolBarManager();
toolbarManager.add(expandAction);
toolbarManager.add(collapseAction);
toolbarManager.add(selectAction);
toolbarManager.add(filterOnAction);
toolbarManager.add(removeFilters);
}
/**
* method to open the currently selected docitem if its a include or a
* module item
*/
private void openFile() {
IEditorPart iep = getSite().getPage().getActiveEditor();
DocItem selecteditem = getSelectedDocItem();
if (selecteditem == null)
return;
String si = selecteditem.getName();
if (si.equalsIgnoreCase("cfinclude") || si.equalsIgnoreCase("cfmodule")) {
gfa.setActiveEditor(null, iep);
gfa.run(null);
}
}
private void hookDoubleClickAction() {
getTreeViewer().addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
selectAction.run();
}
});
}
private void hookGlobalActions() {
IActionBars bars = getSite().getActionBars();
bars.setGlobalActionHandler(IWorkbenchActionConstants.DELETE, deleteItem);
getTreeViewer().getControl().addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
if (event.character == SWT.DEL && event.stateMask == 0 && deleteItem.isEnabled()) {
deleteItem.run();
}
}
});
}
public void selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent event) {
// this fires when tree node selection changed
if(!fSelectionFromEditor) {
jumpAction.run();
}
// getTreeViewer().refresh(new
// StructuredSelection(event.getSelection()));
}
public void selectionChanged(IWorkbenchPart workbench, ISelection selection) {
// this fires when editor is changed (thanks to addPostSelection in
// createControl)
if (selection != null && selection instanceof ITextSelection) {
IEditorPart curEditor = workbench.getSite().getWorkbenchWindow().getActivePage().getActiveEditor();
if (curEditor != null && curEditor instanceof CFMLEditor) {
CFMLEditor curDoc = (CFMLEditor) curEditor;
DocItem tagItem = curDoc.getSelectionCursorListener().getSelectedTag();
if (tagItem != null) {
setSelectedDocItem(tagItem);
}
}
}
// if (!getControl().isDisposed()) {
// getControl().getDisplay().asyncExec(new Runnable() {
// public void run() {
// Control ctrl= getControl();
// if (ctrl != null && !ctrl.isDisposed()) {
// reload();
// getTreeViewer().refresh();
// }
// }
// });
// }
}
public void partActivated(IWorkbenchPart arg0) {
// TODO Auto-generated method stub
}
public void partBroughtToTop(IWorkbenchPart arg0) {
// TODO Auto-generated method stub
}
public void partClosed(IWorkbenchPart arg0) {
// TODO Auto-generated method stub
}
public void partDeactivated(IWorkbenchPart arg0) {
// TODO Auto-generated method stub
}
public void partOpened(IWorkbenchPart arg0) {
// TODO Auto-generated method stub
}
public void propertyChanged(Object arg0, int arg1) {
// this generally comes from a save. When we get the reconciler going, shouldn't need it
fSelectionFromEditor = true;
reload();
}
}