blob: 61267b615b83f191eb85a6e2f98524683050046a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* xored software, Inc. - initial API and Implementation (Andrei Sobolev)
* xored software, Inc. - Patch 228846 (Alex Panchenko <alex@xored.com>)
*******************************************************************************/
package org.eclipse.dltk.ui.infoviews;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.ui.editor.EditorUtility;
import org.eclipse.dltk.internal.ui.text.ScriptWordFinder;
import org.eclipse.dltk.internal.ui.util.SelectionUtil;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.dltk.ui.IContextMenuConstants;
import org.eclipse.dltk.ui.ScriptElementLabels;
import org.eclipse.dltk.ui.actions.SelectionDispatchAction;
import org.eclipse.jface.action.IAction;
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.ColorRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
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.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
/**
* Abstract class for views which show information for a given element.
*
*/
public abstract class AbstractInfoView extends ViewPart
implements ISelectionListener, IMenuListener, IPropertyChangeListener {
/**
* @since 2.0
*/
protected static class KeywordInput {
private final String value;
private final IModelElement context;
public KeywordInput(String value, IModelElement context) {
this.value = value;
this.context = context;
}
public String getValue() {
return value;
}
public IModelElement getContext() {
return context;
}
}
/** ScriptElementLabels flags used for the title */
private final long TITLE_FLAGS = ScriptElementLabels.ALL_FULLY_QUALIFIED
| ScriptElementLabels.M_APP_RETURNTYPE
| ScriptElementLabels.F_APP_TYPE_SIGNATURE
| ScriptElementLabels.M_PARAMETER_TYPES
| ScriptElementLabels.M_PARAMETER_NAMES
| ScriptElementLabels.M_EXCEPTIONS
| ScriptElementLabels.F_PRE_TYPE_SIGNATURE
| ScriptElementLabels.M_PRE_TYPE_PARAMETERS
| ScriptElementLabels.T_TYPE_PARAMETERS
| ScriptElementLabels.USE_RESOLVED;
// private final long LOCAL_VARIABLE_TITLE_FLAGS = TITLE_FLAGS &
// ~ScriptElementLabels.F_FULLY_QUALIFIED |
// ScriptElementLabels.F_POST_QUALIFIED;
/** ScriptElementLabels flags used for the tool tip text */
private static final long TOOLTIP_LABEL_FLAGS = ScriptElementLabels.DEFAULT_QUALIFIED
| ScriptElementLabels.ROOT_POST_QUALIFIED
| ScriptElementLabels.APPEND_ROOT_PATH
| ScriptElementLabels.M_PARAMETER_TYPES
| ScriptElementLabels.M_PARAMETER_NAMES
| ScriptElementLabels.M_APP_RETURNTYPE
| ScriptElementLabels.M_EXCEPTIONS
| ScriptElementLabels.F_APP_TYPE_SIGNATURE
| ScriptElementLabels.M_APP_RETURNTYPE
| ScriptElementLabels.T_TYPE_PARAMETERS;
/*
* @see IPartListener2
*/
private IPartListener2 fPartListener = new IPartListener2() {
@Override
public void partVisible(IWorkbenchPartReference ref) {
if (ref.getId().equals(getSite().getId())) {
IWorkbenchPart activePart = ref.getPage().getActivePart();
if (activePart != null)
selectionChanged(activePart, ref.getPage().getSelection());
startListeningForSelectionChanges();
}
}
@Override
public void partHidden(IWorkbenchPartReference ref) {
if (ref.getId().equals(getSite().getId()))
stopListeningForSelectionChanges();
}
@Override
public void partInputChanged(IWorkbenchPartReference ref) {
if (!ref.getId().equals(getSite().getId()))
computeAndSetInput(ref.getPart(false));
}
@Override
public void partActivated(IWorkbenchPartReference ref) {
}
@Override
public void partBroughtToTop(IWorkbenchPartReference ref) {
}
@Override
public void partClosed(IWorkbenchPartReference ref) {
}
@Override
public void partDeactivated(IWorkbenchPartReference ref) {
}
@Override
public void partOpened(IWorkbenchPartReference ref) {
}
};
/** The current input. */
protected Object fCurrentViewInput;
/** The copy to clipboard action. */
private SelectionDispatchAction fCopyToClipboardAction;
/** The goto input action. */
private GotoInputAction fGotoInputAction;
/** Counts the number of background computation requests. */
private volatile int fComputeCount;
private IContextActivation fContextActivation;
/**
* Background color.
*
*
*/
private Color fBackgroundColor;
private RGB fBackgroundColorRGB;
/**
* Set the input of this view.
*
* @param input
* the input object
*/
abstract protected void setInput(Object input);
/**
* Computes the input for this view based on the given element.
*
* @param element
* the element from which to compute the input
* @return the input or <code>null</code> if the input was not computed
* successfully
*/
abstract protected Object computeInput(Object element);
/**
* Create the part control.
*
* @param parent
* the parent control
* @see IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
abstract protected void internalCreatePartControl(Composite parent);
/**
* Set the view's foreground color.
*
* @param color
* the SWT color
*/
abstract protected void setForeground(Color color);
/**
* Set the view's background color.
*
* @param color
* the SWT color
*/
abstract protected void setBackground(Color color);
/**
* Returns the view's primary control.
*
* @return the primary control
*/
protected abstract Control getControl();
/**
* Returns the context ID for the Help system
*
* @return the string used as ID for the Help context
*
*/
abstract protected String getHelpContextId();
/*
* @see IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public final void createPartControl(Composite parent) {
internalCreatePartControl(parent);
inititalizeColors();
getSite().getWorkbenchWindow().getPartService()
.addPartListener(fPartListener);
createActions();
createContextMenu();
fillActionBars(getViewSite().getActionBars());
IContextService ctxService = getSite()
.getService(IContextService.class);
if (ctxService != null) {
fContextActivation = ctxService
.activateContext(DLTKUIPlugin.CONTEXT_VIEWS);
}
PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(),
getHelpContextId());
}
/**
* Creates the actions and action groups for this view.
*/
protected void createActions() {
fGotoInputAction = new GotoInputAction(this);
fGotoInputAction.setEnabled(false);
fCopyToClipboardAction = new CopyToClipboardAction(getViewSite());
ISelectionProvider provider = getSelectionProvider();
if (provider != null)
provider.addSelectionChangedListener(fCopyToClipboardAction);
}
/**
* Creates the context menu for this view.
*/
protected void createContextMenu() {
MenuManager menuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
menuManager.setRemoveAllWhenShown(true);
menuManager.addMenuListener(this);
Menu contextMenu = menuManager.createContextMenu(getControl());
getControl().setMenu(contextMenu);
getSite().registerContextMenu(menuManager, getSelectionProvider());
}
@Override
public void menuAboutToShow(IMenuManager menu) {
menu.add(new Separator(IContextMenuConstants.GROUP_OPEN));
menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
menu.add(new Separator(IContextMenuConstants.GROUP_ADDITIONS));
IAction action;
action = getCopyToClipboardAction();
if (action != null)
menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, action);
action = getSelectAllAction();
if (action != null)
menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, action);
menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, fGotoInputAction);
}
protected IAction getSelectAllAction() {
return null;
}
protected IAction getCopyToClipboardAction() {
return fCopyToClipboardAction;
}
/**
* Returns the input of this view.
*
* @return input the input object or <code>null</code> if not input is set
*/
protected Object getInput() {
return fCurrentViewInput;
}
// Helper method
ISelectionProvider getSelectionProvider() {
return getViewSite().getSelectionProvider();
}
/**
* Fills the actions bars.
* <p>
* Subclasses may extend.
*
* @param actionBars
* the action bars
*/
protected void fillActionBars(IActionBars actionBars) {
IToolBarManager toolBar = actionBars.getToolBarManager();
fillToolBar(toolBar);
IAction action;
action = getCopyToClipboardAction();
if (action != null)
actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(),
action);
action = getSelectAllAction();
if (action != null)
actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
action);
}
/**
* Fills the tool bar.
* <p>
* Default is to do nothing.
* </p>
*
* @param tbm
* the tool bar manager
*/
protected void fillToolBar(IToolBarManager tbm) {
tbm.add(fGotoInputAction);
}
private void inititalizeColors() {
if (getSite().getShell().isDisposed())
return;
Display display = getSite().getShell().getDisplay();
if (display == null || display.isDisposed())
return;
setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
ColorRegistry registry = JFaceResources.getColorRegistry();
registry.addListener(this);
fBackgroundColorRGB = registry.getRGB(getBackgroundColorKey());
Color bgColor;
if (fBackgroundColorRGB == null) {
bgColor = display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
fBackgroundColorRGB = bgColor.getRGB();
} else {
bgColor = new Color(display, fBackgroundColorRGB);
fBackgroundColor = bgColor;
}
setBackground(bgColor);
}
/**
* The preference key for the background color.
*
* @return the background color key
*
*/
abstract protected String getBackgroundColorKey();
@Override
public void propertyChange(PropertyChangeEvent event) {
if (getBackgroundColorKey().equals(event.getProperty()))
inititalizeColors();
}
/**
* Start to listen for selection changes.
*/
protected void startListeningForSelectionChanges() {
getSite().getPage().addPostSelectionListener(this);
}
/**
* Stop to listen for selection changes.
*/
protected void stopListeningForSelectionChanges() {
getSite().getPage().removePostSelectionListener(this);
}
@Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if (part.equals(this))
return;
computeAndSetInput(part);
}
/**
* Tells whether the new input should be ignored if the current input is the
* same.
*
* @param je
* the new input
* @param selection
* the current selection from the part that provides the input
* @return <code>true</code> if the new input should be ignored
*/
protected boolean isIgnoringNewInput(Object je, IWorkbenchPart part,
ISelection selection) {
return fCurrentViewInput != null && fCurrentViewInput.equals(je)
&& je != null;
}
/**
* Finds and returns the Script element selected in the given part.
*
* @param part
* the workbench part for which to find the selected Script
* element
* @param selection
* the selection
* @return the selected Script element
*/
protected Object findSelectedModelElement(IWorkbenchPart part,
ISelection selection) {
try {
if (isValidWorkbenchPart(part)
&& selection instanceof ITextSelection) {
final IEditorPart editor = (IEditorPart) part;
IModelElement[] elements = TextSelectionConverter
.codeResolve(editor, (ITextSelection) selection);
if (elements != null && elements.length > 0) {
return elements.length == 1 ? elements[0]
: (Object) new ModelElementArray(elements);
}
} else if (selection instanceof IStructuredSelection) {
Object element = SelectionUtil.getSingleElement(selection);
return findModelElement(element);
}
} catch (ModelException e) {
DLTKUIPlugin.log(e);
}
return null;
}
/**
* Checks that specified {@link IWorkbenchPart} is an {@link ITextEditor}
* and is suitable to retrieve selected {@link IModelElement}s
*
* @param part
* @return
*/
protected boolean isValidWorkbenchPart(IWorkbenchPart part) {
return part instanceof IEditorPart;
}
/**
* Tries to get a Script element out of the given element.
*
* @param element
* an object
* @return the Script element represented by the given element or
* <code>null</code>
*/
private IModelElement findModelElement(Object element) {
if (element != null && element instanceof IAdaptable) {
return ((IAdaptable) element).getAdapter(IModelElement.class);
}
return null;
}
/*
* @see IWorkbenchPart#dispose()
*/
@Override
final public void dispose() {
// cancel possible running computation
fComputeCount++;
if (fContextActivation != null) {
IContextService ctxService = getSite()
.getService(IContextService.class);
if (ctxService != null) {
ctxService.deactivateContext(fContextActivation);
}
}
getSite().getWorkbenchWindow().getPartService()
.removePartListener(fPartListener);
ISelectionProvider provider = getSelectionProvider();
if (provider != null)
provider.removeSelectionChangedListener(fCopyToClipboardAction);
JFaceResources.getColorRegistry().removeListener(this);
fBackgroundColorRGB = null;
if (fBackgroundColor != null) {
fBackgroundColor.dispose();
fBackgroundColor = null;
}
internalDispose();
}
/*
* @see IWorkbenchPart#dispose()
*/
abstract protected void internalDispose();
/**
* Determines all necessary details and delegates the computation into a
* background thread.
*
* @param part
* the workbench part
*/
private void computeAndSetInput(final IWorkbenchPart part) {
final int currentCount = ++fComputeCount;
ISelectionProvider provider = part.getSite().getSelectionProvider();
if (provider == null)
return;
final ISelection selection = provider.getSelection();
if (selection == null || selection.isEmpty())
return;
Thread thread = new Thread("Info view input computer") { //$NON-NLS-1$
@Override
public void run() {
if (currentCount != fComputeCount)
return;
Object je = findSelectedModelElement(part, selection);
Object tmp = null;
if (je != null) {
tmp = je;
} else {
if (part instanceof ITextEditor
&& selection instanceof ITextSelection) {
ITextEditor edit = (ITextEditor) part;
IDocument document = edit.getDocumentProvider()
.getDocument(edit.getEditorInput());
IRegion reg = ScriptWordFinder.findWord(document,
((ITextSelection) selection).getOffset());
if (reg != null) {
try {
tmp = new KeywordInput(
document.get(reg.getOffset(),
reg.getLength()),
EditorUtility
.getEditorInputModelElement(
edit, false));
} catch (BadLocationException e) {
tmp = null;
}
}
}
}
final Object src = tmp;
if (isIgnoringNewInput(src, part, selection))
return;
// The actual computation
final Object input = computeInput(src);
if (input == null)
return;
Shell shell = getSite().getShell();
if (shell.isDisposed())
return;
Display display = shell.getDisplay();
if (display.isDisposed())
return;
display.asyncExec(() -> {
if (fComputeCount != currentCount
|| getViewSite().getShell().isDisposed())
return;
fCurrentViewInput = src;
doSetInput(input);
});
}
};
thread.setDaemon(true);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
private void doSetInput(Object input) {
setInput(input);
Object in = getInput();
fGotoInputAction.setEnabled(in instanceof IModelElement);
if (in instanceof IModelElement) {
IModelElement me = (IModelElement) in;
setContentDescription(ScriptElementLabels.getDefault()
.getElementLabel(me, TITLE_FLAGS));
setTitleToolTip(ScriptElementLabels.getDefault().getElementLabel(me,
TOOLTIP_LABEL_FLAGS));
} else if (in instanceof ModelElementArray) {
final ModelElementArray array = (ModelElementArray) in;
setContentDescription(array.getContentDescription());
setTitleToolTip(array.getTitleTooltip());
} else if (in instanceof KeywordInput) {
final KeywordInput keyword = (KeywordInput) in;
setContentDescription(keyword.getValue());
setTitleToolTip(keyword.getValue());
} else {
setContentDescription(in.toString());
setTitleToolTip(in.toString());
}
}
}