| /******************************************************************************* |
| * 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()); |
| } |
| } |
| } |