blob: 83e23a26bdf757f58c8d56f3ef3d13dde5efc956 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018, 2021 1C-Soft LLC.
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Vladimir Piskarev (1C) - initial API and implementation
*******************************************************************************/
package org.eclipse.handly.ui.callhierarchy;
import static org.eclipse.handly.context.Contexts.EMPTY_CONTEXT;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import org.eclipse.handly.context.IContext;
import org.eclipse.handly.internal.ui.Activator;
import org.eclipse.handly.snapshot.StaleSnapshotException;
import org.eclipse.handly.ui.DefaultEditorUtility;
import org.eclipse.handly.ui.EditorOpener;
import org.eclipse.handly.ui.EditorUtility;
import org.eclipse.handly.ui.PartListenerAdapter;
import org.eclipse.handly.ui.action.HistoryDropDownAction;
import org.eclipse.handly.ui.viewer.ColumnDescription;
import org.eclipse.handly.ui.viewer.DelegatingSelectionProvider;
import org.eclipse.handly.ui.viewer.LabelComparator;
import org.eclipse.handly.util.TextRange;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLayoutData;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.OpenAndLinkWithEditorHelper;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.BaseSelectionListenerAction;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.part.ViewPart;
/**
* An abstract base implementation of a call hierarchy view.
*/
public abstract class CallHierarchyViewPart
extends ViewPart
{
/**
* Pop-up menu: name of group for focus actions (value
* <code>"group.focus"</code>).
*/
protected static final String GROUP_FOCUS = "group.focus"; //$NON-NLS-1$
private static final Object[] NO_ELEMENTS = new Object[0];
private static final String KEY_HIERARCHY_KIND =
"org.eclipse.handly.callhierarchy.view.kind"; //$NON-NLS-1$
private static final String KEY_ORIENTATION =
"org.eclipse.handly.callhierarchy.view.orientation"; //$NON-NLS-1$
private static final String KEY_HORIZONTAL_RATIO =
"org.eclipse.handly.callhierarchy.view.horizontalRatio"; //$NON-NLS-1$
private static final String KEY_VERTICAL_RATIO =
"org.eclipse.handly.callhierarchy.view.verticalRatio"; //$NON-NLS-1$
private static final int ORIENTATION_AUTO = SWT.HORIZONTAL | SWT.VERTICAL;
private final EnumSet<CallHierarchyKind> supportedHierarchyKinds;
private CallHierarchyKind hierarchyKind;
private int orientation = ORIENTATION_AUTO;
private boolean orientationAdjusted;
private int horizontalRatio = 500, verticalRatio = 500;
private Object[] inputElements = NO_ELEMENTS;
private boolean pinned;
private PageBook pageBook;
private Control noHierarchyPage;
private SashForm sashForm;
private TreeViewer hierarchyViewer;
private TableViewer locationViewer;
private EditorOpener editorOpener;
private final RefreshAction refreshAction = new RefreshAction();
private final RefreshElementAction refreshElementAction =
new RefreshElementAction();
private final RemoveFromViewAction removeFromViewAction =
new RemoveFromViewAction();
private SetHierarchyKindAction[] setHierarchyKindActions =
new SetHierarchyKindAction[0];
private SetOrientationAction[] setOrientationActions =
new SetOrientationAction[] { new SetOrientationAction(SWT.VERTICAL),
new SetOrientationAction(SWT.HORIZONTAL), new SetOrientationAction(
ORIENTATION_AUTO) };
private final FocusOnSelectionAction focusOnSelectionAction =
new FocusOnSelectionAction();
private HistoryDropDownAction<HistoryEntry> historyDropDownAction;
private final PinAction pinAction = new PinAction();
private final IPartListener partListener = new PartListenerAdapter()
{
@Override
public void partActivated(IWorkbenchPart part)
{
if (part != CallHierarchyViewPart.this)
return;
getViewManager().viewOpenedOrActivated(CallHierarchyViewPart.this);
}
@Override
public void partClosed(IWorkbenchPart part)
{
if (part != CallHierarchyViewPart.this)
return;
getViewManager().viewClosed(CallHierarchyViewPart.this);
}
@Override
public void partOpened(IWorkbenchPart part)
{
if (part != CallHierarchyViewPart.this)
return;
getViewManager().viewOpenedOrActivated(CallHierarchyViewPart.this);
refresh();
}
};
/**
* Constructs a call hierarchy view that supports all of the
* call hierarchy kinds.
*
* @see CallHierarchyKind
* @see #CallHierarchyViewPart(EnumSet)
*/
public CallHierarchyViewPart()
{
this(EnumSet.allOf(CallHierarchyKind.class));
}
/**
* Constructs a call hierarchy view that supports the given
* call hierarchy kinds.
*
* @param supportedHierarchyKinds not <code>null</code> and not empty
*/
public CallHierarchyViewPart(
EnumSet<CallHierarchyKind> supportedHierarchyKinds)
{
if (supportedHierarchyKinds == null)
throw new IllegalArgumentException();
if (supportedHierarchyKinds.isEmpty())
throw new IllegalArgumentException();
this.supportedHierarchyKinds = supportedHierarchyKinds.clone();
this.hierarchyKind = supportedHierarchyKinds.iterator().next();
makeSetHierarchyKindActions();
}
private void makeSetHierarchyKindActions()
{
int size = supportedHierarchyKinds.size();
if (size > 1)
{
setHierarchyKindActions = new SetHierarchyKindAction[size];
int i = 0;
for (CallHierarchyKind kind : supportedHierarchyKinds)
setHierarchyKindActions[i++] = new SetHierarchyKindAction(kind);
setHierarchyKindActions[0].setChecked(true);
}
}
/**
* Returns whether the given elements are possible input elements for this view.
* This method invokes {@link #isPossibleInputElement(Object)} for each of
* the given elements until <code>false</code> is returned for an element
* (in which case this method will return <code>false</code>) or all elements
* have been checked (in which case it will return <code>true</code>).
*
* @param elements may be <code>null</code> or may contain null elements,
* in which case <code>false</code> will be returned; may be empty,
* in which case <code>true</code> will be returned
* @return <code>true</code> if the given elements are possible input elements
* for this view, and <code>false</code> otherwise
*/
public final boolean arePossibleInputElements(Object[] elements)
{
if (elements == null)
return false;
for (Object element : elements)
{
if (!isPossibleInputElement(element))
return false;
}
return true;
}
/**
* Returns whether the given element is a possible input element for this view.
*
* @param element may be <code>null</code>, in which case <code>false</code>
* will be returned
* @return <code>true</code> if the given element is a possible input element
* for this view, and <code>false</code> otherwise
* @see #arePossibleInputElements(Object[])
*/
protected abstract boolean isPossibleInputElement(Object element);
/**
* Sets the current input elements for this view. Clients <b>must not</b>
* modify the given array afterwards.
* <p>
* Default implementation invokes {@link #refresh()} after the input elements
* have been set.
* </p>
*
* @param elements not <code>null</code>, must not contain null elements;
* may be empty
* @throws IllegalArgumentException if {@link #arePossibleInputElements(Object[])}
* returns <code>false</code> for the given elements
*/
public void setInputElements(Object[] elements)
{
if (!arePossibleInputElements(elements))
throw new IllegalArgumentException(Arrays.toString(elements));
inputElements = elements;
if (elements.length > 0)
addHistoryEntry(createHistoryEntry(elements));
refresh();
}
/**
* Returns the current input elements for this view.
*
* @return the current input elements (never <code>null</code>,
* may be empty). Clients <b>must not</b> modify the returned array.
*/
public final Object[] getInputElements()
{
return inputElements;
}
/**
* Returns the current hierarchy kind for this view.
* If the hierarchy kind has not been explicitly set,
* returns the first of the supported hierarchy kinds.
*
* @return the current hierarchy kind (never <code>null</code>)
* @see #setHierarchyKind(CallHierarchyKind)
*/
public final CallHierarchyKind getHierarchyKind()
{
return hierarchyKind;
}
/**
* Sets the current hierarchy kind for this view.
* <p>
* Default implementation invokes {@link #refresh()} if the view
* hierarchy kind has changed; it also adjusts the checked state of the
* 'set hierarchy kind' actions accordingly.
* </p>
*
* @param kind not <code>null</code>
* @throws IllegalArgumentException if the given kind is not {@link
* #supportsHierarchyKind(CallHierarchyKind) supported} by this view
*/
public void setHierarchyKind(CallHierarchyKind kind)
{
if (!supportsHierarchyKind(kind))
throw new IllegalArgumentException();
if (kind == hierarchyKind)
return;
hierarchyKind = kind;
for (SetHierarchyKindAction action : setHierarchyKindActions)
action.setChecked(action.kind == kind);
refresh();
}
/**
* Returns whether this view supports the given hierarchy kind.
*
* @param kind may be <code>null</code>, in which case <code>false</code>
* will be returned
* @return <code>true</code> if this view supports the given kind,
* and <code>false</code> otherwise
*/
public final boolean supportsHierarchyKind(CallHierarchyKind kind)
{
return supportedHierarchyKinds.contains(kind);
}
/**
* Sets the orientation of this view, which may be one of the constants
* {@link SWT#HORIZONTAL} or {@link SWT#VERTICAL}; this method may also
* be called with <code>SWT.HORIZONTAL|SWT.VERTICAL</code> for automatic
* orientation.
*
* @param orientation new orientation
* @throws IllegalArgumentException if the value of orientation is invalid
*/
public void setOrientation(int orientation)
{
if (!supportsOrientation(orientation))
throw new IllegalArgumentException();
if (orientation == this.orientation)
return;
this.orientation = orientation;
for (SetOrientationAction action : setOrientationActions)
action.setChecked(action.orientation == orientation);
adjustOrientation();
}
private static boolean supportsOrientation(int orientation)
{
return (orientation == SWT.HORIZONTAL || orientation == SWT.VERTICAL
|| orientation == ORIENTATION_AUTO);
}
private void adjustOrientation()
{
if (sashForm == null || sashForm.isDisposed())
return;
Point size = sashForm.getParent().getParent().getSize();
if (size.x == 0 || size.y == 0)
return;
int orientation = this.orientation;
if (orientation == ORIENTATION_AUTO)
orientation = (size.x > size.y) ? SWT.HORIZONTAL : SWT.VERTICAL;
if (sashForm.getOrientation() == orientation && orientationAdjusted)
return;
if (sashForm.getOrientation() != orientation)
{
if (orientationAdjusted)
saveSplitterRatio();
sashForm.setOrientation(orientation);
sashForm.layout();
}
restoreSplitterRatio();
orientationAdjusted = true;
}
private void saveSplitterRatio()
{
int[] weights = sashForm.getWeights();
int ratio = (weights[0] * 1000) / (weights[0] + weights[1]);
if (sashForm.getOrientation() == SWT.HORIZONTAL)
horizontalRatio = ratio;
else
verticalRatio = ratio;
}
private void restoreSplitterRatio()
{
int ratio = (sashForm.getOrientation() == SWT.HORIZONTAL)
? horizontalRatio : verticalRatio;
sashForm.setWeights(new int[] { ratio, 1000 - ratio });
}
/**
* Marks this view as pinned.
*
* @param pinned whether the view is pinned
*/
public void setPinned(boolean pinned)
{
this.pinned = pinned;
}
/**
* Returns whether this view is pinned.
*
* @return <code>true</code> if the view is pinned,
* and <code>false</code> otherwise
*/
public final boolean isPinned()
{
return pinned;
}
/**
* Performs a full refresh of the content of this view. This method
* invokes {@link #refresh(IContext)} with an empty context.
*/
public final void refresh()
{
refresh(EMPTY_CONTEXT);
}
/**
* Performs a refresh of the content of this view according to options
* specified in the given context. This method does nothing if the SWT
* controls for this view have not been created or have been disposed.
* Otherwise, it invokes {@link #doRefresh(IContext)}.
*
* @param context the operation context (never <code>null</code>)
*/
protected final void refresh(IContext context)
{
if (hierarchyViewer == null
|| hierarchyViewer.getControl().isDisposed())
return;
doRefresh(context);
}
@Override
public void init(IViewSite site, IMemento memento) throws PartInitException
{
super.init(site, memento);
if (memento != null)
{
if (supportedHierarchyKinds.size() > 1)
{
String value = memento.getString(KEY_HIERARCHY_KIND);
if (value != null)
{
CallHierarchyKind kind = null;
try
{
kind = CallHierarchyKind.valueOf(value);
}
catch (IllegalArgumentException e)
{
}
if (supportsHierarchyKind(kind))
setHierarchyKind(kind);
}
}
Integer integer = memento.getInteger(KEY_ORIENTATION);
if (integer != null)
{
int value = integer.intValue();
if (supportsOrientation(value))
setOrientation(value);
}
integer = memento.getInteger(KEY_HORIZONTAL_RATIO);
if (integer != null)
{
int value = integer.intValue();
if (value > 0 && value < 1000)
horizontalRatio = value;
}
integer = memento.getInteger(KEY_VERTICAL_RATIO);
if (integer != null)
{
int value = integer.intValue();
if (value > 0 && value < 1000)
verticalRatio = value;
}
}
}
@Override
public void saveState(IMemento memento)
{
super.saveState(memento);
if (supportedHierarchyKinds.size() > 1)
memento.putString(KEY_HIERARCHY_KIND, hierarchyKind.name());
memento.putInteger(KEY_ORIENTATION, orientation);
saveSplitterRatio(); // make sure to save the current splitter ratio
memento.putInteger(KEY_HORIZONTAL_RATIO, horizontalRatio);
memento.putInteger(KEY_VERTICAL_RATIO, verticalRatio);
}
@Override
public void createPartControl(Composite parent)
{
getSite().getPage().addPartListener(partListener);
parent.addControlListener(new ControlAdapter()
{
@Override
public void controlResized(ControlEvent e)
{
if (!orientationAdjusted || orientation == ORIENTATION_AUTO)
adjustOrientation();
}
});
pageBook = new PageBook(parent, SWT.NONE);
noHierarchyPage = createNoHierarchyPage(pageBook);
sashForm = createSashForm(pageBook);
hierarchyViewer = createHierarchyViewer(sashForm);
locationViewer = createLocationViewer(sashForm);
configureHierarchyViewer(hierarchyViewer);
configureLocationViewer(locationViewer);
ConvertingSelectionProvider selectionProvider =
new ConvertingSelectionProvider();
selectionProvider.setDelegate(hierarchyViewer);
getSite().setSelectionProvider(selectionProvider);
selectionProvider.addSelectionChangedListener(e -> updateStatusLine(
getViewSite().getActionBars().getStatusLineManager(),
e.getStructuredSelection()));
initContextMenu(hierarchyViewer.getControl(), (IMenuManager manager) ->
{
createHierarchyViewerMenuGroups(manager);
fillHierarchyViewerMenu(manager);
}, getSite().getId(), selectionProvider);
initContextMenu(locationViewer.getControl(), (IMenuManager manager) ->
{
createLocationViewerMenuGroups(manager);
fillLocationViewerMenu(manager);
}, getSite().getId() + ".locationViewerMenu", locationViewer); //$NON-NLS-1$
new OpenEditorHelper(hierarchyViewer);
new OpenEditorHelper(locationViewer);
hierarchyViewer.addSelectionChangedListener(
e -> hierarchySelectionChanged(e.getSelection()));
locationViewer.addSelectionChangedListener(
e -> locationSelectionChanged(e.getSelection()));
editorOpener = createEditorOpener();
selectionProvider.addSelectionChangedListener(focusOnSelectionAction);
addRefreshAction(refreshAction);
getViewSite().getActionBars().setGlobalActionHandler(
ActionFactory.REFRESH.getId(), refreshElementAction);
hierarchyViewer.addSelectionChangedListener(removeFromViewAction);
getViewSite().getActionBars().setGlobalActionHandler(
ActionFactory.DELETE.getId(), removeFromViewAction);
for (SetHierarchyKindAction action : setHierarchyKindActions)
addSetHierarchyKindAction(action, action.kind);
for (SetOrientationAction action : setOrientationActions)
addSetOrientationAction(action, action.orientation);
historyDropDownAction = createHistoryDropDownAction(
new HistoryDropDownAction.History<HistoryEntry>()
{
@Override
public List<HistoryEntry> getHistoryEntries()
{
return getHistory();
}
@Override
public void setHistoryEntries(List<HistoryEntry> entries)
{
List<HistoryEntry> history = getHistory();
history.clear();
history.addAll(entries);
notifyHistoryUpdated();
}
@Override
public HistoryEntry getActiveEntry()
{
Object[] inputElements = getInputElements();
if (inputElements.length == 0)
return null;
return createHistoryEntry(inputElements);
}
@Override
public void setActiveEntry(HistoryEntry entry)
{
setInputElements(entry.getInputElements());
}
@Override
public String getLabel(HistoryEntry entry)
{
return entry.getLabel();
}
@Override
public ImageDescriptor getImageDescriptor(HistoryEntry entry)
{
return entry.getImageDescriptor();
}
});
historyDropDownAction.setEnabled(!getHistory().isEmpty());
addHistoryDropDownAction(historyDropDownAction);
addPinAction(pinAction);
}
@Override
public void dispose()
{
getSite().getPage().removePartListener(partListener);
super.dispose();
}
@Override
public void setFocus()
{
pageBook.setFocus();
ISelection selection = getSite().getSelectionProvider().getSelection();
if (selection instanceof IStructuredSelection)
updateStatusLine(
getViewSite().getActionBars().getStatusLineManager(),
(IStructuredSelection)selection);
}
/**
* Returns a {@link CallHierarchyViewManager} for this view.
* The same manager instance is returned for each call.
*
* @return the view manager (never <code>null</code>)
*/
protected abstract CallHierarchyViewManager getViewManager();
/**
* Returns the root nodes for the current call hierarchy.
* <p>
* Default implementation invokes {@link #createHierarchyRoots(Object[])}
* with the current input elements.
* </p>
*
* @return the root nodes for the current call hierarchy
* (never <code>null</code>, may be empty)
*/
protected ICallHierarchyNode[] getHierarchyRoots()
{
return createHierarchyRoots(inputElements);
}
/**
* Creates and returns the root nodes for a call hierarchy based on the
* given input elements and the current hierarchy kind.
*
* @param inputElements never <code>null</code>, may be empty
* @return the created nodes (not <code>null</code>, may be empty)
*/
protected abstract ICallHierarchyNode[] createHierarchyRoots(
Object[] inputElements);
/**
* Returns a comparator for the hierarchy viewer.
* <p>
* Default implementation returns a {@link LabelComparator} if the current
* hierarchy kind is {@link CallHierarchyKind#CALLER}, and <code>null</code>
* otherwise.
* </p>
*
* @return a {@link ViewerComparator}, or <code>null</code> for no sorting
*/
protected ViewerComparator getHierarchyComparator()
{
if (getHierarchyKind() == CallHierarchyKind.CALLER)
return new LabelComparator(); // sort caller hierarchy alphabetically
return null;
}
/**
* Computes the content description for this view.
*
* @return the computed content description (not <code>null</code>)
* @see #getContentDescription()
*/
protected abstract String computeContentDescription();
/**
* Updates the status line based on the given selection in this view.
* <p>
* Default implementation clears the status line message if the selection
* is empty or if exactly one element is selected; sets a generic message
* of the form "(x) items selected" otherwise. It always clears the error
* message.
* </p>
*
* @param manager the status line manager (never <code>null</code>)
* @param selection the current selection (never <code>null</code>)
*/
protected void updateStatusLine(IStatusLineManager manager,
IStructuredSelection selection)
{
String message = null;
int size = selection.size();
if (size > 1)
{
message = MessageFormat.format(
Messages.CallHierarchyViewPart_0__items_selected, size);
}
manager.setMessage(message);
manager.setErrorMessage(null);
}
/**
* Refreshes the content of this view according to options specified in
* the given context. This method may only be called after the SWT controls
* for this view have been created and before they have been disposed.
*
* @param context the operation context (never <code>null</code>)
*/
protected void doRefresh(IContext context)
{
setContentDescription(computeContentDescription());
hierarchyViewer.setInput(null);
locationViewer.setInput(null);
if (inputElements.length > 0)
{
pageBook.showPage(sashForm);
hierarchyViewer.setComparator(getHierarchyComparator());
ICallHierarchyNode[] roots = getHierarchyRoots();
hierarchyViewer.setInput(new CallHierarchy(hierarchyKind, roots));
if (roots.length > 0)
hierarchyViewer.setSelection(new StructuredSelection(roots[0]),
true);
hierarchyViewer.getTree().setFocus();
refreshAction.setEnabled(true);
refreshElementAction.setEnabled(true);
}
else
{
pageBook.showPage(noHierarchyPage);
pageBook.setFocus();
refreshAction.setEnabled(false);
refreshElementAction.setEnabled(false);
}
}
/**
* Creates and returns a control for the 'no hierarchy' page.
* This method is called once, when the part's control is created.
* <p>
* Default implementation returns a <code>Label</code> telling,
* in general terms, that there is no call hierarchy to display.
* Subclasses may override this method (e.g., to give details
* on what the user needs to do to display a call hierarchy).
* </p>
*
* @param parent the parent composite (never <code>null</code>)
* @return the created control (not <code>null</code>)
*/
protected Control createNoHierarchyPage(Composite parent)
{
Label label = new Label(parent, SWT.LEAD + SWT.WRAP);
label.setText(Messages.CallHierarchyViewPart_No_hierarchy_to_display);
return label;
}
/**
* Returns the parent {@link SashForm} for the hierarchy and location
* viewers.
*
* @return the <code>SashForm</code>,
* or <code>null</code> if it has yet to be created
* @see #createSashForm(Composite)
*/
protected final SashForm getSashForm()
{
return sashForm;
}
/**
* Creates and returns a {@link SashForm} that will be used as the parent
* control for the hierarchy and location viewers. This method only creates
* the control; it does not configure it. This method is called once,
* when the part's control is created.
*
* @param parent the parent composite (never <code>null</code>)
* @return the created control (not <code>null</code>)
*/
protected SashForm createSashForm(Composite parent)
{
return new SashForm(parent, SWT.NONE);
}
/**
* Returns the hierarchy tree viewer.
*
* @return the hierarchy tree viewer,
* or <code>null</code> if it has yet to be created
* @see #createHierarchyViewer(Composite)
*/
protected final TreeViewer getHierarchyViewer()
{
return hierarchyViewer;
}
/**
* Creates and returns a tree viewer control that will be used for
* displaying the call hierarchy. This method only creates the control;
* it does not configure it. This method is called once, when the part's
* control is created.
*
* @param parent the parent composite (never <code>null</code>)
* @return the created control (not <code>null</code>)
* @see #configureHierarchyViewer(TreeViewer)
*/
protected TreeViewer createHierarchyViewer(Composite parent)
{
return new TreeViewer(parent, SWT.MULTI);
}
/**
* Configures the newly created hierarchy viewer. This method has to set
* as least a content provider and a label provider. This method is called
* once, just after the viewer is created.
* <p>
* Default implementation sets a {@link CallHierarchyContentProvider}
* as the content provider, and a {@link CallHierarchyLabelProvider}
* backed by a {@link WorkbenchLabelProvider} as the label provider.
* Subclasses usually need to extend this method and replace the default
* label provider; they may also override this method completely, but
* there is usually no need to.
* </p>
*
* @param viewer the viewer to configure (never <code>null</code>)
*/
protected void configureHierarchyViewer(TreeViewer viewer)
{
viewer.getTree().setLayoutData(new GridData(GridData.FILL_BOTH));
viewer.setUseHashlookup(true);
viewer.setAutoExpandLevel(2);
viewer.setContentProvider(new CallHierarchyContentProvider(this));
viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
new CallHierarchyLabelProvider(new WorkbenchLabelProvider())));
}
/**
* Creates the menu groups for the hierarchy viewer's pop-up menu.
* This method is called each time the pop-up menu is about to show,
* just before {@link #fillHierarchyViewerMenu(IMenuManager)} is called.
* <p>
* Default implementation adds groups named {@link #GROUP_FOCUS} and
* {@link IWorkbenchActionConstants#MB_ADDITIONS}. Subclasses may extend
* or override this method, but should usually keep the default groups.
* <p>
*
* @param manager the menu manager (never <code>null</code>)
*/
protected void createHierarchyViewerMenuGroups(IMenuManager manager)
{
manager.add(new Separator(GROUP_FOCUS));
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
/**
* Fills the pop-menu for the hierarchy viewer using the menu groups
* created by {@link #createHierarchyViewerMenuGroups(IMenuManager)}.
* This method is called each time the pop-up menu is about to show.
* <p>
* Default implementation adds generic actions such as
* 'focus on selection', 'refresh element', and 'remove from view'.
* Subclasses may extend or override this method.
* </p>
*
* @param manager the menu manager (never <code>null</code>)
*/
protected void fillHierarchyViewerMenu(IMenuManager manager)
{
if (focusOnSelectionAction.isEnabled())
manager.appendToGroup(GROUP_FOCUS, focusOnSelectionAction);
manager.appendToGroup(GROUP_FOCUS, refreshElementAction);
if (removeFromViewAction.isEnabled())
manager.appendToGroup(GROUP_FOCUS, removeFromViewAction);
}
/**
* This method is called on each selection change in the hierarchy viewer.
* <p>
* Default implementation changes the input of the location viewer
* accordingly and tries to reveal the selected hierarchy node in an
* open editor with {@link #revealInEditor(Object, boolean, boolean)
* revealInEditor}. Subclasses may extend or even override this method,
* but there is usually no need to.
* </p>
*
* @param selection the new selection (never <code>null</code>)
*/
protected void hierarchySelectionChanged(ISelection selection)
{
Object element = getSelectedElement(selection);
if (!(element instanceof ICallHierarchyNode))
locationViewer.setInput(null);
else
{
ICallLocation[] callLocations =
((ICallHierarchyNode)element).getCallLocations();
locationViewer.setInput(callLocations);
if (callLocations.length > 0)
locationViewer.setSelection(new StructuredSelection(
callLocations[0]), true);
if (hierarchyViewer.getControl().isFocusControl())
{
try
{
revealInEditor(element, false, false);
}
catch (PartInitException e)
{
// cannot happen: may not open a new editor
}
}
}
}
/**
* Returns the location table viewer.
*
* @return the location table viewer,
* or <code>null</code> if it has yet to be created
* @see #createLocationViewer(Composite)
*/
protected final TableViewer getLocationViewer()
{
return locationViewer;
}
/**
* Creates and returns a table viewer control that will be used for
* displaying the call locations. This method only creates the control;
* it does not configure it. This method is called once, when the part's
* control is created.
*
* @param parent the parent composite (never <code>null</code>)
* @return the created control (not <code>null</code>)
* @see #configureLocationViewer(TableViewer)
*/
protected TableViewer createLocationViewer(Composite parent)
{
return new TableViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI
| SWT.FULL_SELECTION);
}
/**
* Configures the newly created location viewer. This method has to set
* as least a content provider and a label provider. This method is called
* once, just after the viewer is created.
* <p>
* Default implementation sets an {@link ArrayContentProvider} as the
* content provider, and a {@link LocationTableLabelProvider} as the
* label provider. Also, it invokes {@link #createLocationColumns(Table)}.
* Subclasses may extend or even override this method, but there is
* usually no need to.
* </p>
*
* @param viewer the viewer to configure (never <code>null</code>)
*/
protected void configureLocationViewer(TableViewer viewer)
{
viewer.setContentProvider(new ArrayContentProvider());
viewer.setLabelProvider(new LocationTableLabelProvider());
Table table = viewer.getTable();
table.setHeaderVisible(true);
createLocationColumns(table);
}
/**
* Creates the table columns in the location viewer.
* <p>
* Default implementation creates the columns based on descriptions
* returned by {@link #getLocationColumnDescriptions()}. Subclasses
* may override this method, but there is usually no need to.
* </p>
*
* @param table the table to create columns in (never <code>null</code>)
*/
protected void createLocationColumns(Table table)
{
TableLayout layout = new TableLayout();
table.setLayout(layout);
ColumnDescription[] columnDescriptions =
getLocationColumnDescriptions();
for (ColumnDescription columnDescription : columnDescriptions)
{
ColumnLayoutData layoutData = columnDescription.getLayoutData();
layout.addColumnData(layoutData);
TableColumn column = new TableColumn(table, SWT.NONE);
column.setResizable(layoutData.resizable);
String header = columnDescription.getHeader();
if (header != null)
column.setText(header);
}
}
/**
* Returns the column descriptions for the call location table.
* <p>
* Default implementation returns descriptions for the 'icon' column,
* the 'line number' column, and the 'call info' column. Subclasses
* may override this method, but there is usually no need to.
* </p>
*
* @return the column descriptions (not <code>null</code>)
*/
protected ColumnDescription[] getLocationColumnDescriptions()
{
ColumnDescription iconColumn = new ColumnDescription(null,
new ColumnPixelData(18, false, true));
ColumnDescription lineColumn = new ColumnDescription(
Messages.CallHierarchyViewPart_Line_column_header,
new ColumnWeightData(60));
ColumnDescription infoColumn = new ColumnDescription(
Messages.CallHierarchyViewPart_Info_column_header,
new ColumnWeightData(300));
return new ColumnDescription[] { iconColumn, lineColumn, infoColumn };
}
/**
* Creates the menu groups for the location viewer's pop-up menu.
* This method is called each time the pop-up menu is about to show,
* just before {@link #fillLocationViewerMenu(IMenuManager)} is called.
* <p>
* Default implementation adds a group named {@link
* IWorkbenchActionConstants#MB_ADDITIONS}. Subclasses may extend
* or override this method.
* <p>
*
* @param manager the menu manager (never <code>null</code>)
*/
protected void createLocationViewerMenuGroups(IMenuManager manager)
{
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
/**
* Fills the pop-menu for the location viewer using the menu groups
* created by {@link #createLocationViewerMenuGroups(IMenuManager)}.
* This method is called each time the pop-up menu is about to show.
* <p>
* Default implementation does nothing. Subclasses may extend or override
* this method.
* </p>
*
* @param manager the menu manager (never <code>null</code>)
*/
protected void fillLocationViewerMenu(IMenuManager manager)
{
}
/**
* This method is called on each selection change in the location viewer.
* <p>
* Default implementation tries to reveal the selected call location in an
* open editor with {@link #revealInEditor(Object, boolean, boolean)
* revealInEditor}. Subclasses may extend or even override this method,
* but there is usually no need to.
* </p>
*
* @param selection the new selection (never <code>null</code>)
*/
protected void locationSelectionChanged(ISelection selection)
{
Object element = getSelectedElement(selection);
if (!(element instanceof ICallLocation))
return;
if (locationViewer.getControl().isFocusControl())
{
try
{
revealInEditor(element, false, false);
}
catch (PartInitException e)
{
// cannot happen: may not open a new editor
}
}
}
/**
* Reveals the given element in an appropriate editor on a best effort basis.
* <p>
* Default implementation uses the {@link #getEditorOpener()
* editor opener} and specifically supports revealing an {@link
* ICallLocation} and an {@link ICallHierarchyNode} (other elements
* are handled generically). To reveal a call location in an open
* editor, it invokes {@link #revealCallLocation(IEditorPart,
* ICallLocation, IContext) revealCallLocation}.
* </p>
*
* @param element not <code>null</code>
* @param activate whether to activate the editor
* @param mayOpenNewEditor whether a new editor may be opened
* when the element cannot be revealed in an existing editor
* @throws PartInitException if a new editor could not be created
* or initialized
*/
protected void revealInEditor(Object element, boolean activate,
boolean mayOpenNewEditor) throws PartInitException
{
ICallLocation callLocation = null;
if (element instanceof ICallLocation)
{
callLocation = (ICallLocation)element;
element = callLocation.getCaller();
}
else if (element instanceof ICallHierarchyNode)
{
ICallHierarchyNode node = (ICallHierarchyNode)element;
ICallLocation[] callLocations = node.getCallLocations();
if (callLocations.length == 0)
element = node.getElement();
else
{
callLocation = callLocations[0];
element = callLocation.getCaller();
}
}
EditorUtility editorUtility = editorOpener.getEditorUtility();
if (editorUtility.getEditorInput(element) == null)
return;
IEditorPart editor = null;
if (mayOpenNewEditor)
editor = editorOpener.open(element, activate, callLocation == null);
else
{
IWorkbenchPage page = editorOpener.getWorkbenchPage();
IEditorReference editorRef = editorUtility.findEditor(page,
element);
if (editorRef != null)
{
editor = editorRef.getEditor(true);
if (editor != null)
{
if (activate)
page.activate(editor);
else
page.bringToTop(editor);
if (callLocation == null)
editorUtility.revealElement(editor, element);
}
}
}
if (editor != null && callLocation != null)
revealCallLocation(editor, callLocation, EMPTY_CONTEXT);
}
/**
* Attempts to reveal the given call location in the given editor.
* <p>
* Default implementation uses the {@link EditorOpener#getEditorUtility()
* editor utility} of the {@link #getEditorOpener() editor opener}
* for revealing the call range in the given editor and invokes {@link
* #handleCannotRevealCallLocation(IEditorPart, ICallLocation, IContext)
* handleCannotRevealCallLocation} if the call location cannot be revealed
* for whatever reason.
* </p>
*
* @param editor never <code>null</code>
* @param callLocation never <code>null</code>
* @param context never <code>null</code>
*/
protected void revealCallLocation(IEditorPart editor,
ICallLocation callLocation, IContext context)
{
TextRange callRange = callLocation.getCallRange();
if (callRange == null)
handleCannotRevealCallLocation(editor, callLocation, context);
else
{
EditorUtility editorUtility = editorOpener.getEditorUtility();
try
{
editorUtility.revealTextRange(editor, callRange.getOffset(),
callRange.getLength(), callLocation.getSnapshot());
}
catch (StaleSnapshotException e)
{
handleCannotRevealCallLocation(editor, callLocation, context);
}
}
}
/**
* Handles the case when a call location cannot be revealed in the editor.
* <p>
* Default implementation displays a generic error message
* on the status line and attempts to reveal the caller element
* in the given editor.
* </p>
*
* @param editor never <code>null</code>
* @param callLocation never <code>null</code>
* @param context never <code>null</code>
*/
protected void handleCannotRevealCallLocation(IEditorPart editor,
ICallLocation callLocation, IContext context)
{
getViewSite().getActionBars().getStatusLineManager().setErrorMessage(
Messages.CallHierarchyViewPart_Cannot_reveal_call_location);
Object element = callLocation.getCaller();
if (element != null)
editorOpener.getEditorUtility().revealElement(editor, element);
}
/**
* Returns the editor opener used by this view.
*
* @return the editor opener,
* or <code>null</code> if it has yet to be created
* @see #createEditorOpener()
*/
protected final EditorOpener getEditorOpener()
{
return editorOpener;
}
/**
* Creates and returns an editor opener for this view.
* This method is called once, when the part's control is created.
* <p>
* Subclasses may override this method if they require a specific
* editor opener.
* </p>
*
* @return the created editor opener (not <code>null</code>)
*/
protected EditorOpener createEditorOpener()
{
return new EditorOpener(getSite().getPage(),
DefaultEditorUtility.INSTANCE);
}
/**
* Returns the 'focus on selection' action used by this view.
*
* @return the 'focus on selection' action
*/
protected final IAction getFocusOnSelectionAction()
{
return focusOnSelectionAction;
}
/**
* Returns the 'refresh element' action used by this view.
*
* @return the 'refresh element' action
*/
protected final IAction getRefreshElementAction()
{
return refreshElementAction;
}
/**
* Returns the 'remove from view' action used by this view.
*
* @return the 'remove from view' action
*/
protected final IAction getRemoveFromViewAction()
{
return removeFromViewAction;
}
/**
* Contributes the 'refresh' action to this view.
* This method is called once, when the part's control is created.
* <p>
* Default implementation adds the given action to the view tool bar.
* Subclasses may extend or override this method.
* </p>
*
* @param action the 'refresh' action (never <code>null</code>)
*/
protected void addRefreshAction(IAction action)
{
getViewSite().getActionBars().getToolBarManager().add(action);
}
/**
* Contributes a 'set hierarchy kind' action to this view. This method
* is called once for each of the 'set hierarchy kind' actions, when the
* part's control is created. If this view supports only one hierarchy kind,
* no 'set hierarchy kind' action is created and this method is not called.
* <p>
* Default implementation adds the given action to the view tool bar
* as well as to the view menu. Subclasses may extend or override
* this method.
* </p>
*
* @param action a 'set hierarchy kind' action
* (never <code>null</code>)
* @param kind the hierarchy kind set by the given action
* (never <code>null</code>)
*/
protected void addSetHierarchyKindAction(IAction action,
CallHierarchyKind kind)
{
IActionBars actionBars = getViewSite().getActionBars();
actionBars.getToolBarManager().add(action);
actionBars.getMenuManager().add(action);
}
/**
* Contributes a 'set orientation' action to this view. This method
* is called once for each of the 'set orientation' actions, when the
* part's control is created.
* <p>
* Default implementation adds the given action to the 'Layout' sub-menu
* of the view menu. The sub-menu is created if necessary. Subclasses may
* extend or override this method.
* </p>
*
* @param action a 'set orientation' action
* (never <code>null</code>)
* @param orientation the orientation set by the given action
*/
protected void addSetOrientationAction(IAction action, int orientation)
{
IActionBars actionBars = getViewSite().getActionBars();
IMenuManager viewMenu = actionBars.getMenuManager();
String id = "layout"; //$NON-NLS-1$
IMenuManager layoutSubMenu = viewMenu.findMenuUsingPath(id);
if (layoutSubMenu == null)
{
viewMenu.add(new Separator());
layoutSubMenu = new MenuManager(
Messages.CallHierarchyViewPart_Layout_menu, id);
viewMenu.add(layoutSubMenu);
}
layoutSubMenu.add(action);
}
/**
* Creates and returns a 'show history list' action for this view.
* This method is called once, when the part's control is created.
* <p>
* Subclasses need to override this method if they extend
* {@link HistoryDropDownAction}.
* </p>
*
* @param history never <code>null</code>
* @return the created action (not <code>null</code>)
*/
protected HistoryDropDownAction<HistoryEntry> createHistoryDropDownAction(
HistoryDropDownAction.History<HistoryEntry> history)
{
return new HistoryDropDownAction<>(history);
}
/**
* Contributes the 'show history list' action to this view.
* This method is called once, when the part's control is created.
* <p>
* Default implementation adds the given action to the view tool bar.
* Subclasses may extend or override this method.
* </p>
*
* @param action the 'show history list' action (never <code>null</code>)
*/
protected void addHistoryDropDownAction(IAction action)
{
getViewSite().getActionBars().getToolBarManager().add(action);
}
/**
* Contributes the 'pin' action to this view.
* This method is called once, when the part's control is created.
* <p>
* Default implementation adds the given action to the view tool bar.
* Subclasses may extend or override this method.
* </p>
*
* @param action the 'pin' action (never <code>null</code>)
*/
protected void addPinAction(IAction action)
{
getViewSite().getActionBars().getToolBarManager().add(action);
}
/**
* Returns the history used by this view; the history is represented by
* a "live" list of history entries.
* <p>
* Default implementation returns a history that is shared between all
* views managed by the same {@link #getViewManager() view manager}.
* </p>
*
* @return the view history (never <code>null</code>)
*/
protected List<HistoryEntry> getHistory()
{
return getViewManager().getViewHistory();
}
/**
* Creates and returns a history entry for the given input elements.
*
* @param inputElements never <code>null</code>; never empty
* @return the created history entry (not <code>null</code>)
*/
protected abstract HistoryEntry createHistoryEntry(Object[] inputElements);
/**
* Notifies that the history has been updated by this view.
* <p>
* Default implementation calls {@link #historyUpdated()} for each view
* managed by the same {@link #getViewManager() view manager}.
* </p>
*
* @see #getHistory()
*/
protected void notifyHistoryUpdated()
{
List<CallHierarchyViewPart> views = getViewManager().getViews();
for (CallHierarchyViewPart view : views)
{
view.historyUpdated();
}
}
/**
* A callback that is invoked when the history has been updated.
* <p>
* Default implementation sets the enabled state of the 'show history list'
* action according to whether the history is empty and, if the history
* is empty, clears the view input.
* </p>
*
* @see #getHistory()
* @see #notifyHistoryUpdated()
*/
protected void historyUpdated()
{
boolean empty = getHistory().isEmpty();
historyDropDownAction.setEnabled(!empty);
if (empty)
setInputElements(NO_ELEMENTS);
}
private void addHistoryEntry(HistoryEntry entry)
{
List<HistoryEntry> history = getHistory();
history.remove(entry);
history.add(0, entry);
notifyHistoryUpdated();
}
private void initContextMenu(Control parent, IMenuListener listener,
String menuId, ISelectionProvider selectionProvider)
{
MenuManager manager = new MenuManager();
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(listener);
Menu menu = manager.createContextMenu(parent);
parent.setMenu(menu);
getSite().registerContextMenu(menuId, manager, selectionProvider);
}
private static Object getSelectedElement(ISelection selection)
{
if (selection instanceof IStructuredSelection)
{
IStructuredSelection ss = (IStructuredSelection)selection;
if (ss.size() == 1)
return ss.getFirstElement();
}
return null;
}
/**
* Represents an entry of the call hierarchy view history list.
*/
protected abstract static class HistoryEntry
{
private final Object[] inputElements;
/**
* Constructs a history entry for the given input elements.
* Clients <b>must not</b> modify the given array afterwards.
*
* @param inputElements never <code>null</code>; never empty
*/
protected HistoryEntry(Object[] inputElements)
{
if (inputElements.length == 0)
throw new AssertionError();
this.inputElements = inputElements;
}
/**
* Returns the input elements for this history entry.
*
* @return the input elements (never <code>null</code>; never empty).
* Clients <b>must not</b> modify the returned array.
*/
public final Object[] getInputElements()
{
return inputElements;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HistoryEntry other = (HistoryEntry)obj;
if (!Arrays.equals(inputElements, other.inputElements))
return false;
return true;
}
@Override
public int hashCode()
{
return Arrays.hashCode(inputElements);
}
/**
* Returns a user-readable text label for this history entry.
* <p>
* Default implementation composes a label based on labels for
* input elements. It invokes {@link #getElementLabel(Object)}
* to obtain a label for an input element.
* </p>
*
* @return the text label of the history entry (never <code>null</code>)
*/
public String getLabel()
{
switch (inputElements.length)
{
case 1:
return MessageFormat.format(
Messages.CallHierarchyViewPart_History_entry_label__0,
getElementLabel(inputElements[0]));
case 2:
return MessageFormat.format(
Messages.CallHierarchyViewPart_History_entry_label__0__1,
getElementLabel(inputElements[0]), getElementLabel(
inputElements[1]));
default:
return MessageFormat.format(
Messages.CallHierarchyViewPart_History_entry_label__0__1_more,
getElementLabel(inputElements[0]), getElementLabel(
inputElements[1]));
}
}
/**
* Returns a user-readable text label for the given element.
*
* @param element the given element
* @return the text label of the element (never <code>null</code>)
*/
protected abstract String getElementLabel(Object element);
/**
* Returns an image descriptor for this history entry.
* <p>
* Default implementation always returns <code>null</code>.
* </p>
*
* @return the image descriptor of the history entry
* (may be <code>null</code>)
*/
public ImageDescriptor getImageDescriptor()
{
return null;
}
}
private class FocusOnSelectionAction
extends BaseSelectionListenerAction
{
FocusOnSelectionAction()
{
super(
Messages.CallHierarchyViewPart_Focus_on_selection_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Focus_on_selection_action_tooltip);
}
@Override
public void run()
{
setInputElements(getStructuredSelection().toArray());
}
@Override
protected boolean updateSelection(IStructuredSelection selection)
{
return !selection.isEmpty() && arePossibleInputElements(
selection.toArray());
}
}
private static class ConvertingSelectionProvider
extends DelegatingSelectionProvider
{
@Override
public ISelection getSelection()
{
return convert(super.getSelection());
}
@Override
protected SelectionChangedEvent newSelectionChangedEvent(
ISelection selection)
{
return super.newSelectionChangedEvent(convert(selection));
}
private ISelection convert(ISelection selection)
{
if (!(selection instanceof IStructuredSelection)
|| selection.isEmpty())
return selection;
Object[] elements = ((IStructuredSelection)selection).toArray();
int length = elements.length;
Object[] converted = new Object[length];
for (int i = 0; i < length; i++)
converted[i] = convert(elements[i]);
return new StructuredSelection(converted);
}
private Object convert(Object element)
{
if (element instanceof ICallHierarchyNode)
return ((ICallHierarchyNode)element).getElement();
return element;
}
}
private class OpenEditorHelper
extends OpenAndLinkWithEditorHelper
{
OpenEditorHelper(StructuredViewer viewer)
{
super(viewer);
}
@Override
protected void activate(ISelection selection)
{
Object element = getSelectedElement(selection);
if (element != null)
{
try
{
revealInEditor(element, true, false);
}
catch (PartInitException e)
{
// cannot happen: may not open a new editor
}
}
}
@Override
protected void open(ISelection selection, boolean activate)
{
Object element = getSelectedElement(selection);
if (element != null)
{
try
{
revealInEditor(element, activate, true);
}
catch (PartInitException e)
{
ErrorDialog.openError(getSite().getShell(),
Messages.CallHierarchyViewPart_Show_call_location,
Messages.CallHierarchyViewPart_Error_opening_editor,
e.getStatus());
}
}
}
}
private class RefreshAction
extends Action
{
RefreshAction()
{
setText(Messages.CallHierarchyViewPart_Refresh_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Refresh_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_REFRESH));
setDisabledImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_DLCL_REFRESH));
}
@Override
public void run()
{
refresh();
}
}
private class RefreshElementAction
extends Action
{
RefreshElementAction()
{
setText(Messages.CallHierarchyViewPart_Refresh_element_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Refresh_element_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_REFRESH));
setActionDefinitionId(IWorkbenchCommandConstants.FILE_REFRESH);
}
@Override
public void run()
{
IStructuredSelection selection =
hierarchyViewer.getStructuredSelection();
if (selection.isEmpty())
refresh();
else
{
Iterator<?> it = selection.iterator();
while (it.hasNext())
{
Object e = it.next();
if (e instanceof ICallHierarchyNode)
((ICallHierarchyNode)e).refresh();
hierarchyViewer.refresh(e);
}
}
}
}
private class RemoveFromViewAction
extends BaseSelectionListenerAction
{
RemoveFromViewAction()
{
super(Messages.CallHierarchyViewPart_Remove_from_view_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Remove_from_view_action_tooltip);
ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
setImageDescriptor(images.getImageDescriptor(
ISharedImages.IMG_ELCL_REMOVE));
setActionDefinitionId(IWorkbenchCommandConstants.EDIT_DELETE);
}
@Override
public void run()
{
Object[] elements = getStructuredSelection().toArray();
hierarchyViewer.setSelection(StructuredSelection.EMPTY);
hierarchyViewer.remove(elements);
}
@Override
protected boolean updateSelection(IStructuredSelection selection)
{
return !selection.isEmpty();
}
}
private class SetHierarchyKindAction
extends Action
{
final CallHierarchyKind kind;
SetHierarchyKindAction(CallHierarchyKind kind)
{
super(null, AS_RADIO_BUTTON);
this.kind = kind;
switch (kind)
{
case CALLER:
setText(
Messages.CallHierarchyViewPart_Show_caller_hierarchy_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Show_caller_hierarchy_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_CH_CALLERS));
break;
case CALLEE:
setText(
Messages.CallHierarchyViewPart_Show_callee_hierarchy_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Show_callee_hierarchy_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_CH_CALLEES));
break;
}
}
@Override
public void run()
{
setHierarchyKind(kind);
}
}
private class SetOrientationAction
extends Action
{
final int orientation;
SetOrientationAction(int orientation)
{
super(null, AS_RADIO_BUTTON);
this.orientation = orientation;
switch (orientation)
{
case SWT.HORIZONTAL:
setText(
Messages.CallHierarchyViewPart_Layout_horizontal_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Layout_horizontal_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_LAYOUT_HORIZONTAL));
break;
case SWT.VERTICAL:
setText(
Messages.CallHierarchyViewPart_Layout_vertical_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Layout_vertical_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_LAYOUT_VERTICAL));
break;
default:
setText(
Messages.CallHierarchyViewPart_Layout_automatic_action_text);
setToolTipText(
Messages.CallHierarchyViewPart_Layout_automatic_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_LAYOUT_AUTOMATIC));
setChecked(true);
}
}
@Override
public void run()
{
setOrientation(orientation);
}
}
private class PinAction
extends Action
{
PinAction()
{
super(Messages.CallHierarchyViewPart_Pin_action_text,
IAction.AS_CHECK_BOX);
setToolTipText(Messages.CallHierarchyViewPart_Pin_action_tooltip);
setImageDescriptor(Activator.getImageDescriptor(
Activator.IMG_ELCL_PIN_VIEW));
}
@Override
public void run()
{
setPinned(isChecked());
}
}
}