blob: c745959ee41522df01cc477d1d1c171df2420bf7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2016 GEBIT Gesellschaft fuer EDV-Beratung
* und Informatik-Technologien mbH,
* Berlin, Duesseldorf, Frankfurt (Germany) and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* GEBIT Gesellschaft fuer EDV-Beratung und Informatik-Technologien mbH - initial API and implementation
* IBM Corporation - bug fixes
* John-Mason P. Shackelford (john-mason.shackelford@pearson.com) - bug 49380, bug 34548, bug 53547
*******************************************************************************/
package org.eclipse.ant.internal.ui.editor.outline;
import java.util.List;
import org.eclipse.ant.internal.ui.AntUIPlugin;
import org.eclipse.ant.internal.ui.IAntUIConstants;
import org.eclipse.ant.internal.ui.IAntUIPreferenceConstants;
import org.eclipse.ant.internal.ui.editor.AntEditor;
import org.eclipse.ant.internal.ui.editor.actions.TogglePresentationAction;
import org.eclipse.ant.internal.ui.model.AntElementNode;
import org.eclipse.ant.internal.ui.model.AntImportNode;
import org.eclipse.ant.internal.ui.model.AntModel;
import org.eclipse.ant.internal.ui.model.AntModelChangeEvent;
import org.eclipse.ant.internal.ui.model.AntModelContentProvider;
import org.eclipse.ant.internal.ui.model.AntModelCore;
import org.eclipse.ant.internal.ui.model.AntModelLabelProvider;
import org.eclipse.ant.internal.ui.model.AntProjectNode;
import org.eclipse.ant.internal.ui.model.AntPropertyNode;
import org.eclipse.ant.internal.ui.model.AntTargetNode;
import org.eclipse.ant.internal.ui.model.AntTaskNode;
import org.eclipse.ant.internal.ui.model.IAntModel;
import org.eclipse.ant.internal.ui.model.IAntModelListener;
import org.eclipse.ant.internal.ui.views.actions.AntOpenWithMenu;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.ListenerList;
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.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
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.ViewerComparator;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.texteditor.ITextEditorActionDefinitionIds;
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 fMenu;
private AntOpenWithMenu fOpenWithMenu;
private IAntModelListener fListener;
private IAntModel fModel;
private AntModelCore fCore;
private ListenerList<ISelectionChangedListener> fPostSelectionChangedListeners = new ListenerList<>();
private boolean fIsModelEmpty = true;
private boolean fFilterInternalTargets;
private boolean fFilterImportedElements;
private boolean fFilterProperties;
private boolean fFilterTopLevel;
private boolean fSort;
private ViewerComparator fComparator;
private AntEditor fEditor;
private TogglePresentationAction fTogglePresentation;
/**
* A viewer filter for the Ant Content Outline
*/
private class AntOutlineFilter extends ViewerFilter {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (element instanceof AntElementNode) {
AntElementNode node = (AntElementNode) element;
if (fFilterTopLevel && (node instanceof AntTaskNode && parentElement instanceof AntProjectNode)) {
return false;
}
if (fFilterImportedElements && (node.getImportNode() != null || node.isExternal())) {
if (node instanceof AntTargetNode && ((AntTargetNode) node).isDefaultTarget()) {
return true;
}
return false;
}
if (fFilterInternalTargets && node instanceof AntTargetNode) {
return !((AntTargetNode) node).isInternal();
}
if (fFilterProperties && node instanceof AntPropertyNode) {
return false;
}
if (!node.isStructuralNode()) {
return false;
}
}
return true;
}
}
private class AntOutlineComparator extends ViewerComparator {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
if (!(e1 instanceof AntElementNode && e2 instanceof AntElementNode)) {
return super.compare(viewer, e1, e2);
}
String name1 = ((AntElementNode) e1).getLabel();
String name2 = ((AntElementNode) e2).getLabel();
return getComparator().compare(name1, name2);
}
}
/**
* 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;
setFilter(filter, IAntUIPreferenceConstants.ANTEDITOR_FILTER_INTERNAL_TARGETS);
}
/**
* Sets whether imported elements should be filtered out of the outline.
*
* @param filter
* whether or not imported elements should be filtered out
*/
protected void setFilterImportedElements(boolean filter) {
fFilterImportedElements = filter;
setFilter(filter, IAntUIPreferenceConstants.ANTEDITOR_FILTER_IMPORTED_ELEMENTS);
}
private void setFilter(boolean filter, String name) {
if (name != null) {
AntUIPlugin.getDefault().getPreferenceStore().setValue(name, filter);
}
// filter has been changed
getTreeViewer().refresh();
}
/**
* Sets whether properties should be filtered out of the outline.
*
* @param filter
* whether or not properties should be filtered out
*/
protected void setFilterProperties(boolean filter) {
fFilterProperties = filter;
setFilter(filter, IAntUIPreferenceConstants.ANTEDITOR_FILTER_PROPERTIES);
}
/**
* Sets whether internal targets should be filtered out of the outline.
*
* @param filter
* whether or not internal targets should be filtered out
*/
protected void setFilterTopLevel(boolean filter) {
fFilterTopLevel = filter;
setFilter(filter, IAntUIPreferenceConstants.ANTEDITOR_FILTER_TOP_LEVEL);
}
/**
* Returns whether internal targets are currently being filtered out of the outline.
*
* @return whether or not internal targets are being filtered out
*/
protected boolean filterInternalTargets() {
return fFilterInternalTargets;
}
/**
* Returns whether imported elements are currently being filtered out of the outline.
*
* @return whether or not imported elements are being filtered out
*/
protected boolean filterImportedElements() {
return fFilterImportedElements;
}
/**
* Returns whether properties are currently being filtered out of the outline.
*
* @return whether or not properties are being filtered out
*/
protected boolean filterProperties() {
return fFilterProperties;
}
/**
* Returns whether top level tasks/types are currently being filtered out of the outline.
*
* @return whether or not top level tasks/types are being filtered out
*/
protected boolean filterTopLevel() {
return fFilterTopLevel;
}
/**
* 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 (fComparator == null) {
fComparator = new AntOutlineComparator();
}
getTreeViewer().setComparator(fComparator);
} else {
getTreeViewer().setComparator(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;
}
/**
* Creates a new AntEditorContentOutlinePage.
*/
public AntEditorContentOutlinePage(AntModelCore core, AntEditor editor) {
super();
fCore = core;
fFilterInternalTargets = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.ANTEDITOR_FILTER_INTERNAL_TARGETS);
fFilterImportedElements = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.ANTEDITOR_FILTER_IMPORTED_ELEMENTS);
fFilterProperties = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.ANTEDITOR_FILTER_PROPERTIES);
fFilterTopLevel = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.ANTEDITOR_FILTER_TOP_LEVEL);
fSort = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(IAntUIPreferenceConstants.ANTEDITOR_SORT);
fEditor = editor;
fTogglePresentation = new TogglePresentationAction();
fTogglePresentation.setEditor(editor);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.part.IPage#dispose()
*/
@Override
public void dispose() {
if (fMenu != null) {
fMenu.dispose();
}
if (fOpenWithMenu != null) {
fOpenWithMenu.dispose();
}
if (fListener != null) {
fCore.removeAntModelListener(fListener);
fListener = null;
}
fTogglePresentation.setEditor(null);
super.dispose();
}
/**
* Creates the control (outline view) for this page
*/
@Override
public void createControl(Composite parent) {
super.createControl(parent);
TreeViewer viewer = getTreeViewer();
viewer.setContentProvider(new AntModelContentProvider());
setSort(fSort);
viewer.setLabelProvider(new AntModelLabelProvider());
viewer.addFilter(new AntOutlineFilter());
if (fModel != null) {
setViewerInput(fModel);
}
MenuManager manager = new MenuManager("#PopUp"); //$NON-NLS-1$
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager menuManager) {
contextMenuAboutToShow(menuManager);
}
});
fMenu = manager.createContextMenu(viewer.getTree());
viewer.getTree().setMenu(fMenu);
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));
tbm.add(new FilterPropertiesAction(this));
tbm.add(new FilterImportedElementsAction(this));
tbm.add(new FilterTopLevelAction(this));
IMenuManager viewMenu = site.getActionBars().getMenuManager();
viewMenu.add(new ToggleLinkWithEditorAction(fEditor));
fOpenWithMenu = new AntOpenWithMenu(this.getSite().getPage());
viewer.addPostSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
firePostSelectionChanged(event.getSelection());
}
});
site.getActionBars().setGlobalActionHandler(ITextEditorActionDefinitionIds.TOGGLE_SHOW_SELECTED_ELEMENT_ONLY, fTogglePresentation);
}
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.addAntModelListener(fListener);
} else if (!isAntModel && wasAntModel && fListener != null) {
fCore.removeAntModelListener(fListener);
fListener = null;
}
tree.setInput(newInput);
if (isAntModel) {
updateTreeExpansion();
}
}
public void setPageInput(AntModel xmlModel) {
fModel = xmlModel;
if (getTreeViewer() != null) {
setViewerInput(fModel);
}
}
private IAntModelListener createAntModelChangeListener() {
return new IAntModelListener() {
@Override
public void antModelChanged(final AntModelChangeEvent event) {
if (event.getModel() == fModel && !getControl().isDisposed()) {
getControl().getDisplay().asyncExec(() -> {
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.getProjectNode() == null;
if (wasModelEmpty && !fIsModelEmpty) {
getTreeViewer().expandToLevel(EXPAND_TO_LEVEL);
}
}
private void firePostSelectionChanged(ISelection selection) {
// create an event
SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
// fire the event
for (ISelectionChangedListener iSelectionChangedListener : fPostSelectionChangedListeners) {
iSelectionChangedListener.selectionChanged(event);
}
}
private void contextMenuAboutToShow(IMenuManager menuManager) {
if (shouldAddOpenWithMenu()) {
addOpenWithMenu(menuManager);
}
menuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void addOpenWithMenu(IMenuManager menuManager) {
AntElementNode element = getSelectedNode();
IFile file = null;
if (element != null) {
file = element.getIFile();
}
if (file != null) {
menuManager.add(new Separator("group.open")); //$NON-NLS-1$
IMenuManager submenu = new MenuManager(AntOutlineMessages.AntEditorContentOutlinePage_Open_With_1);
fOpenWithMenu.setNode(element);
submenu.add(fOpenWithMenu);
menuManager.appendToGroup("group.open", submenu); //$NON-NLS-1$
}
}
private boolean shouldAddOpenWithMenu() {
AntElementNode node = getSelectedNode();
if (node instanceof AntImportNode) {
return true;
}
if (node != null && node.isExternal()) {
String path = node.getFilePath();
if (path != null && path.length() > 0) {
return true;
}
}
return false;
}
private AntElementNode getSelectedNode() {
ISelection iselection = getSelection();
if (iselection instanceof IStructuredSelection) {
IStructuredSelection selection = (IStructuredSelection) iselection;
if (selection.size() == 1) {
Object selected = selection.getFirstElement();
if (selected instanceof AntElementNode) {
return (AntElementNode) selected;
}
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> key) {
if (key == IShowInSource.class) {
return (T) this;
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.part.IShowInSource#getShowInContext()
*/
@Override
public ShowInContext getShowInContext() {
IFile file = null;
if (fModel != null) {
AntElementNode node = getSelectedNode();
if (node != null) {
file = node.getIFile();
}
}
if (file != null) {
ISelection selection = new StructuredSelection(file);
return new ShowInContext(null, selection);
}
return null;
}
public void select(AntElementNode node) {
if (getTreeViewer() != null) {
ISelection s = getTreeViewer().getSelection();
if (s instanceof IStructuredSelection) {
IStructuredSelection ss = (IStructuredSelection) s;
List<?> nodes = ss.toList();
if (!nodes.contains(node)) {
s = (node == null ? StructuredSelection.EMPTY : new StructuredSelection(node));
getTreeViewer().setSelection(s, true);
}
}
}
}
}