blob: 7a7a8935c6e8318bd8cbb25698f1fa012b1c7721 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2003 GEBIT Gesellschaft fuer EDV-Beratung
* und Informatik-Technologien mbH,
* Berlin, Duesseldorf, Frankfurt (Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* GEBIT Gesellschaft fuer EDV-Beratung und Informatik-Technologien mbH - initial API and implementation
* IBM Corporation - bug fixes
*******************************************************************************/
package org.eclipse.ant.internal.ui.editor.outline;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.ant.internal.ui.editor.xml.IAntEditorConstants;
import org.eclipse.ant.internal.ui.editor.xml.XmlAttribute;
import org.eclipse.ant.internal.ui.editor.xml.XmlElement;
import org.eclipse.ant.internal.ui.model.AntImageDescriptor;
import org.eclipse.ant.internal.ui.model.AntUIImages;
import org.eclipse.ant.internal.ui.model.AntUIPlugin;
import org.eclipse.ant.internal.ui.model.AntUtil;
import org.eclipse.ant.internal.ui.model.IAntUIConstants;
import org.eclipse.ant.internal.ui.model.IAntUIPreferenceConstants;
import org.eclipse.ant.internal.ui.views.actions.AntOpenWithMenu;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
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.resource.ImageDescriptor;
import org.eclipse.jface.util.ListenerList;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
/**
* Content outline page for the Ant Editor.
*/
public class AntEditorContentOutlinePage extends ContentOutlinePage implements IShowInSource, IAdaptable {
private static final int EXPAND_TO_LEVEL= 2;
private Menu menu;
private AntOpenWithMenu openWithMenu;
private IDocumentModelListener fListener;
private AntModel fModel;
private XMLCore fCore;
private ListenerList fPostSelectionChangedListeners= new ListenerList();
private boolean fIsModelEmpty= true;
private boolean fFilterInternalTargets;
private boolean fSort;
private static ViewerSorter fSorter;
private class AntOutlineSorter extends ViewerSorter {
/**
* @see org.eclipse.jface.viewers.ViewerSorter#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
public int compare(Viewer viewer, Object e1, Object e2) {
if (!(e1 instanceof XmlElement && e2 instanceof XmlElement)) {
return super.compare(viewer, e1, e2);
}
return ((XmlElement) e1).getDisplayName().compareToIgnoreCase(((XmlElement) e2).getDisplayName());
}
}
/**
* The content provider for the objects shown in the outline view.
*/
private class ContentProvider implements ITreeContentProvider {
/**
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
public void dispose() {
}
/**
* do nothing
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, Object, Object)
*/
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(Object)
*/
public Object[] getChildren(Object parentNode) {
XmlElement tempParentElement = (XmlElement)parentNode;
List tempChilds = new ArrayList();
tempChilds.addAll(tempParentElement.getChildNodes());
if (isFilterInternalTargets()) {
// Filter out private targets
ListIterator iter= tempChilds.listIterator();
while (iter.hasNext()) {
XmlElement element = (XmlElement) iter.next();
if ("target".equals(element.getName()) && element.getAttributeNamed(IAntEditorConstants.ATTR_DESCRIPTION) == null) { //$NON-NLS-1$
iter.remove();
}
}
}
Object[] tempChildObjects = new Object[tempChilds.size()];
for(int i=0; i<tempChilds.size(); i++) {
tempChildObjects[i] = tempChilds.get(i);
}
return tempChildObjects;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(Object)
*/
public Object getParent(Object aNode) {
XmlElement tempElement = (XmlElement)aNode;
return tempElement.getParentNode();
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(Object)
*/
public boolean hasChildren(Object aNode) {
return ((XmlElement)aNode).getChildNodes().size() > 0;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object)
*/
public Object[] getElements(Object anInputElement) {
return ((AntModel) anInputElement).getRootElements();
}
}
/**
* The label provider for the objects shown in the outline view.
*/
private class LabelProvider implements ILabelProvider, IColorProvider {
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(ILabelProviderListener)
*/
public void addListener(ILabelProviderListener listener) {
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
*/
public void dispose() {
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(Object, String)
*/
public boolean isLabelProperty(Object element, String property) {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(ILabelProviderListener)
*/
public void removeListener(ILabelProviderListener listener) {
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ILabelProvider#getImage(Object)
*/
public Image getImage(Object anElement) {
XmlElement tempElement = (XmlElement)anElement;
if("target".equals(tempElement.getName())) { //$NON-NLS-1$
ImageDescriptor base = null;
int flags = 0;
if (tempElement.isErrorNode()) {
flags = flags | AntImageDescriptor.HAS_ERRORS;
}
if (isDefaultTargetNode(tempElement)) {
flags = flags | AntImageDescriptor.DEFAULT_TARGET;
base = AntUIImages.getImageDescriptor(IAntUIConstants.IMG_ANT_DEFAULT_TARGET);
} else if (tempElement.getAttributeNamed(IAntEditorConstants.ATTR_DESCRIPTION) == null) {
base = AntUIImages.getImageDescriptor(IAntUIConstants.IMG_ANT_TARGET_INTERNAL);
} else {
base = AntUIImages.getImageDescriptor(IAntUIConstants.IMG_ANT_TARGET);
}
return AntUIImages.getImage(new AntImageDescriptor(base, flags));
}
if("project".equals(tempElement.getName())) { //$NON-NLS-1$
return getProjectImage(tempElement);
}
if("property".equals(tempElement.getName())) { //$NON-NLS-1$
return AntUIImages.getImage(IAntUIConstants.IMG_PROPERTY);
}
XmlAttribute attribute= tempElement.getAttributeNamed(IAntEditorConstants.ATTR_TYPE);
if (attribute != null && IAntEditorConstants.TYPE_EXTERNAL.equals(attribute.getValue())) {
return getProjectImage(tempElement);
}
if (attribute != null && IAntEditorConstants.TYPE_UNKNOWN.equals(attribute.getValue())) {
int flags= 0;
ImageDescriptor base= AntUIImages.getImageDescriptor(IAntUIConstants.IMG_TASK_PROPOSAL);
if (tempElement.isErrorNode()) {
flags |= AntImageDescriptor.HAS_ERRORS;
}
return AntUIImages.getImage(new AntImageDescriptor(base, flags));
}
if (tempElement.isErrorNode()) {
return AntUIImages.getImage(IAntUIConstants.IMG_ANT_TARGET_ERROR);
}
return AntUIImages.getImage(IAntUIConstants.IMG_TASK_PROPOSAL);
}
private Image getProjectImage(XmlElement tempElement) {
int flags = 0;
ImageDescriptor base = AntUIImages.getImageDescriptor(IAntUIConstants.IMG_ANT_PROJECT);
if (tempElement.isErrorNode()) {
flags = flags | AntImageDescriptor.HAS_ERRORS;
}
return AntUIImages.getImage(new AntImageDescriptor(base, flags));
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ILabelProvider#getText(Object)
*/
public String getText(Object aNode) {
XmlElement element= (XmlElement) aNode;
StringBuffer displayName= new StringBuffer(element.getDisplayName());
if (element.isExternal() && (!element.isRootExternal() || (element.getParentNode() != null && element.getParentNode().isExternal()))) {
displayName.append(AntOutlineMessages.getString("AntEditorContentOutlinePage._[external]_1")); //$NON-NLS-1$
}
return displayName.toString();
}
public Color getForeground(Object element) {
if (isDefaultTargetNode((XmlElement) element)) {
return Display.getDefault().getSystemColor(SWT.COLOR_BLUE);
}
return null;
}
public Color getBackground(Object element) {
return null;
}
}
/**
* Sets whether internal targets should be filtered out of the outline.
*
* @param filter whether or not internal targets should be filtered out
*/
protected void setFilterInternalTargets(boolean filter) {
fFilterInternalTargets= filter;
AntUIPlugin.getDefault().getPreferenceStore().setValue(IAntUIPreferenceConstants.ANTEDITOR_FILTER_INTERNAL_TARGETS, filter);
getTreeViewer().refresh();
}
/**
* Returns whether internal targets are currently being filtered out of
* the outline.
*
* @return whether or not internal targets are being filtered out
*/
protected boolean isFilterInternalTargets() {
return fFilterInternalTargets;
}
/**
* Sets whether elements should be sorted in the outline.
*
* @param sort whether or not elements should be sorted
*/
protected void setSort(boolean sort) {
fSort= sort;
if (sort) {
if (fSorter == null) {
fSorter= new AntOutlineSorter();
}
getTreeViewer().setSorter(fSorter);
} else {
getTreeViewer().setSorter(null);
}
AntUIPlugin.getDefault().getPreferenceStore().setValue(IAntUIPreferenceConstants.ANTEDITOR_SORT, sort);
}
/**
* Returns whether elements are currently being sorted.
*
* @return whether elements are currently being sorted
*/
protected boolean isSort() {
return fSort;
}
/**
* Returns whether the given element represents a project's default target
*
* @param node the node to examine
* @return whether the given node is a default target
*/
private boolean isDefaultTargetNode(XmlElement node) {
XmlAttribute type= node.getAttributeNamed(IAntEditorConstants.ATTR_TYPE);
if (type == null || !type.getValue().equals(IAntEditorConstants.TYPE_TARGET)) {
return false;
}
XmlElement parent= node.getParentNode();
if (parent != null) {
type= parent.getAttributeNamed(IAntEditorConstants.ATTR_TYPE);
while (parent != null && (type == null || !type.getValue().equals(IAntEditorConstants.TYPE_PROJECT))) {
parent= parent.getParentNode();
if (parent != null) {
type= parent.getAttributeNamed(IAntEditorConstants.ATTR_TYPE);
}
}
}
if (parent == null) {
return false;
}
XmlAttribute defaultTarget= parent.getAttributeNamed(IAntEditorConstants.ATTR_DEFAULT);
XmlAttribute nameAttribute= node.getAttributeNamed(IAntEditorConstants.ATTR_NAME);
return defaultTarget != null && nameAttribute != null && defaultTarget.getValue().equals(nameAttribute.getValue());
}
/**
* Creates a new AntEditorContentOutlinePage.
*/
public AntEditorContentOutlinePage(XMLCore core) {
super();
fCore= core;
fFilterInternalTargets= AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.ANTEDITOR_FILTER_INTERNAL_TARGETS);
fSort= AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.ANTEDITOR_SORT);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#dispose()
*/
public void dispose() {
if (menu != null) {
menu.dispose();
}
if (openWithMenu != null) {
openWithMenu.dispose();
}
if (fListener != null) {
fCore.removeDocumentModelListener(fListener);
fListener= null;
}
}
/**
* Creates the control (outline view) for this page
*/
public void createControl(Composite parent) {
super.createControl(parent);
TreeViewer viewer = getTreeViewer();
/*
* We might want to implement our own content provider.
* This content provider should be able to work on a dom like tree
* structure that resembles the file contents.
*/
viewer.setContentProvider(new ContentProvider());
setSort(fSort);
/*
* We probably also need our own label provider.
*/
viewer.setLabelProvider(new LabelProvider());
if (fModel != null) {
setViewerInput(fModel);
}
MenuManager manager= new MenuManager("#PopUp"); //$NON-NLS-1$
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager menuManager) {
contextMenuAboutToShow(menuManager);
}
});
menu= manager.createContextMenu(viewer.getTree());
viewer.getTree().setMenu(menu);
IPageSite site= getSite();
site.registerContextMenu(IAntUIConstants.PLUGIN_ID + ".antEditorOutline", manager, viewer); //$NON-NLS-1$
IToolBarManager tbm= site.getActionBars().getToolBarManager();
tbm.add(new ToggleSortAntOutlineAction(this));
tbm.add(new FilterInternalTargetsAction(this));
openWithMenu= new AntOpenWithMenu(this.getSite().getPage());
viewer.addPostSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
postSelectionChanged(event);
}
});
}
private void setViewerInput(Object newInput) {
TreeViewer tree= getTreeViewer();
Object oldInput= tree.getInput();
boolean isAntModel= (newInput instanceof AntModel);
boolean wasAntModel= (oldInput instanceof AntModel);
if (isAntModel && !wasAntModel) {
if (fListener == null) {
fListener= createAntModelChangeListener();
}
fCore.addDocumentModelListener(fListener);
} else if (!isAntModel && wasAntModel && fListener != null) {
fCore.removeDocumentModelListener(fListener);
fListener= null;
}
tree.setInput(newInput);
if (isAntModel) {
updateTreeExpansion();
}
}
public void setPageInput(AntModel xmlModel) {
fModel= xmlModel;
if (getTreeViewer() != null) {
setViewerInput(fModel);
}
}
private IDocumentModelListener createAntModelChangeListener() {
return new IDocumentModelListener() {
public void documentModelChanged(final DocumentModelChangeEvent event) {
if (event.getModel() == fModel && !getControl().isDisposed()) {
getControl().getDisplay().asyncExec(new Runnable() {
public void run() {
Control ctrl= getControl();
if (ctrl != null && !ctrl.isDisposed()) {
getTreeViewer().refresh();
updateTreeExpansion();
}
}
});
}
}
};
}
public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
fPostSelectionChangedListeners.add(listener);
}
public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
fPostSelectionChangedListeners.remove(listener);
}
private void updateTreeExpansion() {
boolean wasModelEmpty= fIsModelEmpty;
fIsModelEmpty= fModel == null || fModel.getRootElements() == null || fModel.getRootElements().length == 0;
if (wasModelEmpty && !fIsModelEmpty) {
getTreeViewer().expandToLevel(EXPAND_TO_LEVEL);
}
}
public void postSelectionChanged(SelectionChangedEvent event) {
firePostSelectionChanged(event.getSelection());
}
private void firePostSelectionChanged(ISelection selection) {
// create an event
SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
// fire the event
Object[] listeners= fPostSelectionChangedListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ISelectionChangedListener) listeners[i]).selectionChanged(event);
}
}
private void contextMenuAboutToShow(IMenuManager menuManager) {
if (shouldAddOpenWithMenu()) {
addOpenWithMenu(menuManager);
}
menuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void addOpenWithMenu(IMenuManager menuManager) {
IStructuredSelection selection= (IStructuredSelection)getSelection();
XmlElement element= (XmlElement)selection.getFirstElement();
String path = getElementPath(element);
if (path != null) {
IPath resourcePath= new Path(path);
IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
IResource resource= root.getFileForLocation(resourcePath);
if (resource != null && resource.getType() == IResource.FILE && resource.exists()) {
menuManager.add(new Separator("group.open")); //$NON-NLS-1$
IMenuManager submenu= new MenuManager(AntOutlineMessages.getString("AntEditorContentOutlinePage.Open_With_1")); //$NON-NLS-1$
openWithMenu.setFile(resource);
submenu.add(openWithMenu);
menuManager.appendToGroup("group.open", submenu); //$NON-NLS-1$
}
}
}
private String getElementPath(XmlElement element) {
String path= element.getFilePath();
if (element.isRootExternal()){
List children= element.getChildNodes();
if (!children.isEmpty()) {
XmlElement child= (XmlElement)children.get(0);
path= child.getFilePath();
}
}
return path;
}
private boolean shouldAddOpenWithMenu() {
ISelection iselection= getSelection();
if (iselection instanceof IStructuredSelection) {
IStructuredSelection selection= (IStructuredSelection)iselection;
if (selection.size() == 1) {
Object selected= selection.getFirstElement();
if (selected instanceof XmlElement) {
XmlElement element= (XmlElement)selected;
if (element.isExternal()) {
String path = getElementPath(element);
if (path.length() == 0) {
return false;
}
XmlElement parent= element.getParentNode();
while (parent != null) {
String parentPath= getElementPath(parent);
if (path != null && !path.equals(parentPath)) {
return true;
}
parent= parent.getParentNode();
}
}
}
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class key) {
if (key == IShowInSource.class) {
return this;
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IShowInSource#getShowInContext()
*/
public ShowInContext getShowInContext() {
if (fModel != null) {
ILocationProvider locationProvider= fModel.getLocationProvider();
IFile file= AntUtil.getFile(locationProvider.getLocation().toOSString());
ISelection selection= new StructuredSelection(file);
return new ShowInContext(null, selection);
}
return null;
}
}