| /******************************************************************************* |
| * 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 java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.FileLocator; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.dltk.core.DLTKLanguageManager; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IMember; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.ScriptUtils; |
| import org.eclipse.dltk.internal.ui.editor.EditorUtility; |
| import org.eclipse.dltk.internal.ui.editor.ScriptEditor; |
| import org.eclipse.dltk.internal.ui.text.HTMLTextPresenter; |
| import org.eclipse.dltk.ui.DLTKUIPlugin; |
| import org.eclipse.dltk.ui.PreferenceConstants; |
| import org.eclipse.dltk.ui.ScriptElementLabels; |
| import org.eclipse.dltk.ui.documentation.ScriptDocumentationAccess; |
| import org.eclipse.dltk.ui.text.completion.HTMLPrinter; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.dialogs.MessageDialogWithToggle; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.text.DefaultInformationControl; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.TextPresentation; |
| import org.eclipse.jface.text.TextSelection; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTError; |
| import org.eclipse.swt.browser.Browser; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.FontData; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds; |
| import org.osgi.framework.Bundle; |
| |
| /** |
| * Abstract view which shows documentation for a given model element. |
| */ |
| public abstract class AbstractDocumentationView extends AbstractInfoView { |
| /** |
| * Preference key for the preference whether to show a dialog when the SWT |
| * Browser widget is not available. |
| * |
| * |
| */ |
| private static final String DO_NOT_WARN_PREFERENCE_KEY = "AbstractDocumentationView.error.doNotWarn"; //$NON-NLS-1$ |
| // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=73558 |
| private static final boolean WARNING_DIALOG_ENABLED = false; |
| /** Flags used to render a label in the text widget. */ |
| private static final long LABEL_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.T_TYPE_PARAMETERS; |
| /** The HTML widget. */ |
| private Browser fBrowser; |
| /** The text widget. */ |
| private StyledText fText; |
| /** The information presenter. */ |
| private DefaultInformationControl.IInformationPresenter fPresenter; |
| /** The text presentation. */ |
| private TextPresentation fPresentation = new TextPresentation(); |
| /** The select all action */ |
| private SelectAllAction fSelectAllAction; |
| /** The style sheet (css) */ |
| private static String fgStyleSheet; |
| /** The Browser widget */ |
| private boolean fIsUsingBrowserWidget; |
| private RGB fBackgroundColorRGB, fForegroundColorRGB; |
| |
| /** |
| * The Javadoc view's select all action. |
| */ |
| private class SelectAllAction extends Action { |
| /** The control. */ |
| private Control fControl; |
| /** The selection provider. */ |
| private SelectionProvider fSelectionProvider; |
| |
| /** |
| * Creates the action. |
| * |
| * @param control |
| * the widget |
| * @param selectionProvider |
| * the selection provider |
| */ |
| public SelectAllAction(Control control, |
| SelectionProvider selectionProvider) { |
| super("selectAll"); //$NON-NLS-1$ |
| fControl = control; |
| fSelectionProvider = selectionProvider; |
| // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022 |
| setEnabled(!fIsUsingBrowserWidget); |
| setText(InfoViewMessages.SelectAllAction_label); |
| setToolTipText(InfoViewMessages.SelectAllAction_tooltip); |
| setDescription(InfoViewMessages.SelectAllAction_description); |
| PlatformUI.getWorkbench().getHelpSystem().setHelp(this, |
| IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION); |
| } |
| |
| /** |
| * Selects all in the view. |
| */ |
| @Override |
| public void run() { |
| if (fControl instanceof StyledText) |
| ((StyledText) fControl).selectAll(); |
| else { |
| // FIXME: see |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022 |
| // ((Browser)fControl).selectAll(); |
| if (fSelectionProvider != null) |
| fSelectionProvider.fireSelectionChanged(); |
| } |
| } |
| } |
| |
| /** |
| * The Javadoc view's selection provider. |
| */ |
| private static class SelectionProvider implements ISelectionProvider { |
| /** The selection changed listeners. */ |
| private ListenerList<ISelectionChangedListener> fListeners = new ListenerList<>( |
| ListenerList.IDENTITY); |
| /** The widget. */ |
| private Control fControl; |
| |
| /** |
| * Creates a new selection provider. |
| * |
| * @param control |
| * the widget |
| */ |
| public SelectionProvider(Control control) { |
| fControl = control; |
| if (fControl instanceof StyledText) { |
| ((StyledText) fControl) |
| .addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| fireSelectionChanged(); |
| } |
| }); |
| } else { |
| // FIXME: see |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022 |
| // ((Browser)fControl).addSelectionListener(new |
| // SelectionAdapter() { |
| // public void widgetSelected(SelectionEvent e) { |
| // fireSelectionChanged(); |
| // } |
| // }); |
| } |
| } |
| |
| /** |
| * Sends a selection changed event to all listeners. |
| */ |
| public void fireSelectionChanged() { |
| ISelection selection = getSelection(); |
| SelectionChangedEvent event = new SelectionChangedEvent(this, |
| selection); |
| for (ISelectionChangedListener listener : fListeners) |
| listener.selectionChanged(event); |
| } |
| |
| @Override |
| public void addSelectionChangedListener( |
| ISelectionChangedListener listener) { |
| fListeners.add(listener); |
| } |
| |
| @Override |
| public ISelection getSelection() { |
| if (fControl instanceof StyledText) { |
| IDocument document = new Document( |
| ((StyledText) fControl).getSelectionText()); |
| return new TextSelection(document, 0, document.getLength()); |
| } else { |
| // FIXME: see |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022 |
| return StructuredSelection.EMPTY; |
| } |
| } |
| |
| @Override |
| public void removeSelectionChangedListener( |
| ISelectionChangedListener listener) { |
| fListeners.remove(listener); |
| } |
| |
| @Override |
| public void setSelection(ISelection selection) { |
| // not supported |
| } |
| } |
| |
| @Override |
| protected void internalCreatePartControl(Composite parent) { |
| try { |
| fBrowser = new Browser(parent, SWT.NONE); |
| fIsUsingBrowserWidget = true; |
| } catch (SWTError er) { |
| /* |
| * The Browser widget throws an SWTError if it fails to instantiate |
| * properly. Application code should catch this SWTError and disable |
| * any feature requiring the Browser widget. Platform requirements |
| * for the SWT Browser widget are available from the SWT FAQ web |
| * site. |
| */ |
| IPreferenceStore store = this.getPreferenceStore();// JavaPlugin.getDefault().getPreferenceStore(); |
| boolean doNotWarn = store.getBoolean(DO_NOT_WARN_PREFERENCE_KEY); |
| if (WARNING_DIALOG_ENABLED && !doNotWarn) { |
| String title = InfoViewMessages.ScriptdocView_error_noBrowser_title; |
| String message = InfoViewMessages.ScriptdocView_error_noBrowser_message; |
| String toggleMessage = InfoViewMessages.ScriptdocView_error_noBrowser_doNotWarn; |
| MessageDialogWithToggle dialog = MessageDialogWithToggle |
| .openError(parent.getShell(), title, message, |
| toggleMessage, false, null, null); |
| if (dialog.getReturnCode() == Window.OK) |
| store.setValue(DO_NOT_WARN_PREFERENCE_KEY, |
| dialog.getToggleState()); |
| } |
| fIsUsingBrowserWidget = false; |
| } |
| if (!fIsUsingBrowserWidget) { |
| fText = new StyledText(parent, SWT.V_SCROLL | SWT.H_SCROLL); |
| fText.setEditable(false); |
| fPresenter = new HTMLTextPresenter(false); |
| fText.addControlListener(new ControlAdapter() { |
| @Override |
| public void controlResized(ControlEvent e) { |
| setInput(fText.getText()); |
| } |
| }); |
| } |
| initStyleSheet(); |
| getViewSite().setSelectionProvider(new SelectionProvider(getControl())); |
| } |
| |
| protected abstract IPreferenceStore getPreferenceStore(); |
| |
| protected abstract String getNature(); |
| |
| private static void initStyleSheet() { |
| Bundle bundle = Platform.getBundle(DLTKUIPlugin.getPluginId()); |
| URL styleSheetURL = bundle.getEntry("/DocumentationViewStyleSheet.css"); //$NON-NLS-1$ |
| if (styleSheetURL == null) |
| return; |
| try { |
| styleSheetURL = FileLocator.toFileURL(styleSheetURL); |
| BufferedReader reader = new BufferedReader( |
| new InputStreamReader(styleSheetURL.openStream())); |
| StringBuffer buffer = new StringBuffer(200); |
| String line = reader.readLine(); |
| while (line != null) { |
| buffer.append(line); |
| buffer.append('\n'); |
| line = reader.readLine(); |
| } |
| FontData fontData = JFaceResources.getFontRegistry().getFontData( |
| PreferenceConstants.APPEARANCE_DOCUMENTATION_FONT)[0]; |
| fgStyleSheet = org.eclipse.dltk.ui.text.completion.HTMLPrinter |
| .convertTopLevelFont(buffer.toString(), fontData); |
| } catch (IOException ex) { |
| DLTKUIPlugin.log(ex); |
| } |
| } |
| |
| @Override |
| protected void createActions() { |
| super.createActions(); |
| fSelectAllAction = new SelectAllAction(getControl(), |
| (SelectionProvider) getSelectionProvider()); |
| } |
| |
| @Override |
| protected IAction getSelectAllAction() { |
| // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022 |
| if (fIsUsingBrowserWidget) |
| return null; |
| return fSelectAllAction; |
| } |
| |
| @Override |
| protected IAction getCopyToClipboardAction() { |
| // FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022 |
| if (fIsUsingBrowserWidget) |
| return null; |
| return super.getCopyToClipboardAction(); |
| } |
| |
| @Override |
| protected void setForeground(Color color) { |
| getControl().setForeground(color); |
| fForegroundColorRGB = color.getRGB(); |
| } |
| |
| @Override |
| protected void setBackground(Color color) { |
| getControl().setBackground(color); |
| // Apply style sheet |
| fBackgroundColorRGB = color.getRGB(); |
| if (getInput() == null) { |
| StringBuffer buffer = new StringBuffer(); |
| HTMLPrinter.insertPageProlog(buffer, 0, fBackgroundColorRGB, |
| fForegroundColorRGB, fgStyleSheet); |
| setInput(buffer.toString()); |
| } else { |
| setInput(computeInput(getInput())); |
| } |
| } |
| |
| @Override |
| protected String getBackgroundColorKey() { |
| return "org.eclipse.dltk.ui.ScriptdocView.backgroundColor"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| protected void internalDispose() { |
| fText = null; |
| fBrowser = null; |
| } |
| |
| @Override |
| public void setFocus() { |
| getControl().setFocus(); |
| } |
| |
| @Override |
| protected Object computeInput(Object input) { |
| if (getControl() != null) { |
| if (input instanceof KeywordInput) { |
| return getScriptdocHtml((KeywordInput) input); |
| } else if (input instanceof ModelElementArray) { |
| final ModelElementArray array = (ModelElementArray) input; |
| return getScriptdocHtmlDetailed(array.getElements()); |
| } else if (input instanceof IModelElement) { |
| final IModelElement je = (IModelElement) input; |
| switch (je.getElementType()) { |
| case IModelElement.SOURCE_MODULE: |
| try { |
| final ISourceModule module = (ISourceModule) je; |
| return getScriptdocHtmlList(module.getChildren()); |
| } catch (ModelException ex) { |
| return null; |
| } |
| default: |
| return getScriptdocHtml(je); |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| protected void setInput(Object input) { |
| String javadocHtml = (String) input; |
| if (fIsUsingBrowserWidget) { |
| if (javadocHtml != null && javadocHtml.length() > 0) { |
| boolean RTL = (getSite().getShell().getStyle() |
| & SWT.RIGHT_TO_LEFT) != 0; |
| if (RTL) { |
| StringBuffer buffer = new StringBuffer(javadocHtml); |
| HTMLPrinter.insertStyles(buffer, |
| new String[] { "direction:rtl" }); //$NON-NLS-1$ |
| javadocHtml = buffer.toString(); |
| } |
| } |
| fBrowser.setText(javadocHtml); |
| } else { |
| fPresentation.clear(); |
| Rectangle size = fText.getClientArea(); |
| try { |
| javadocHtml = ((DefaultInformationControl.IInformationPresenterExtension) fPresenter) |
| .updatePresentation(getSite().getShell(), javadocHtml, |
| fPresentation, size.width, size.height); |
| } catch (IllegalArgumentException ex) { |
| // the javadoc might no longer be valid |
| return; |
| } |
| fText.setText(javadocHtml); |
| TextPresentation.applyTextPresentation(fPresentation, fText); |
| } |
| } |
| |
| /** |
| * Returns the doc in HTML format. |
| * |
| * @param result |
| * the Script elements for which to get the Javadoc |
| * @return a string with the Javadoc in HTML format. |
| */ |
| private String getScriptdocHtml(KeywordInput keyword) { |
| StringBuffer buffer = new StringBuffer(); |
| try { |
| Reader reader = ScriptDocumentationAccess.getKeywordDocumentation( |
| getNature(), keyword.getContext(), keyword.getValue()); |
| if (reader != null) { |
| HTMLPrinter.addParagraph(buffer, reader); |
| } |
| } catch (ModelException ex) { |
| DLTKUIPlugin.log(ex); |
| return null; |
| } |
| if (buffer.length() > 0) { |
| HTMLPrinter.insertPageProlog(buffer, 0, fgStyleSheet); |
| HTMLPrinter.addPageEpilog(buffer); |
| return buffer.toString(); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the Javadoc in HTML format. |
| * |
| * @param result |
| * the Script elements for which to get the Javadoc |
| * @return a string with the Javadoc in HTML format. |
| */ |
| private String getScriptdocHtmlDetailed(Object[] result) { |
| final StringBuffer buffer = new StringBuffer(); |
| final List<String> nodocs = new ArrayList<String>(); |
| for (int i = 0; i < result.length; i++) { |
| final Object member = result[i]; |
| Reader reader = ScriptDocumentationAccess |
| .getHTMLContentReader(getNature(), member, true, true); |
| if (reader != null) { |
| buffer.append("<b>"); //$NON-NLS-1$ |
| buffer.append(getInfoText(member)); |
| buffer.append("</b>"); //$NON-NLS-1$ |
| HTMLPrinter.addParagraph(buffer, reader); |
| } else { |
| if (member instanceof IModelElement) { |
| nodocs.add(ScriptElementLabels.getDefault().getElementLabel( |
| (IModelElement) member, |
| LABEL_FLAGS | ScriptElementLabels.APPEND_FILE)); |
| } else { |
| // TODO |
| } |
| } |
| } |
| if (!nodocs.isEmpty()) { |
| Collections.sort(nodocs); |
| HTMLPrinter.addParagraph(buffer, |
| InfoViewMessages.ScriptdocView_noAttachedInformationHeader); |
| HTMLPrinter.startBulletList(buffer); |
| for (String s : nodocs) { |
| HTMLPrinter.addBullet(buffer, s); |
| } |
| HTMLPrinter.endBulletList(buffer); |
| } |
| return addPrologeEpilog(buffer); |
| } |
| |
| /** |
| * Returns the Javadoc in HTML format. |
| * |
| * @param result |
| * the Script elements for which to get the Javadoc |
| * @return a string with the Javadoc in HTML format. |
| */ |
| private String getScriptdocHtmlList(IModelElement[] result) { |
| final StringBuffer buffer = new StringBuffer(); |
| HTMLPrinter.startBulletList(buffer); |
| for (int i = 0; i < result.length; i++) { |
| final IModelElement curr = result[i]; |
| if (curr instanceof IMember) { |
| final IMember member = (IMember) curr; |
| HTMLPrinter.addBullet(buffer, getInfoText(member)); |
| } |
| } |
| HTMLPrinter.endBulletList(buffer); |
| return addPrologeEpilog(buffer); |
| } |
| |
| /** |
| * Returns the Javadoc in HTML format. |
| * |
| * @param result |
| * the Script elements for which to get the Javadoc |
| * @return a string with the Javadoc in HTML format. |
| */ |
| private String getScriptdocHtml(IModelElement curr) { |
| StringBuffer buffer = new StringBuffer(); |
| if (curr instanceof IMember) { |
| IMember member = (IMember) curr; |
| // HTMLPrinter.addSmallHeader(buffer, getInfoText(member)); |
| Reader reader = ScriptDocumentationAccess |
| .getHTMLContentReader(getNature(), member, true, true); |
| if (reader != null) { |
| HTMLPrinter.addParagraph(buffer, reader); |
| } else { |
| // Provide hint why there's no Javadoc |
| HTMLPrinter.addParagraph(buffer, |
| InfoViewMessages.ScriptdocView_noAttachedInformation); |
| } |
| } |
| return addPrologeEpilog(buffer); |
| } |
| |
| private String addPrologeEpilog(StringBuffer buffer) { |
| if (buffer.length() > 0) { |
| HTMLPrinter.insertPageProlog(buffer, 0, fBackgroundColorRGB, |
| fForegroundColorRGB, fgStyleSheet); |
| HTMLPrinter.addPageEpilog(buffer); |
| return buffer.toString(); |
| } |
| return null; |
| } |
| |
| /** |
| * The method is overridden to compare {@link ScriptEditor} nature with the |
| * nature of this view. |
| * |
| * @see org.eclipse.dltk.ui.infoviews.AbstractInfoView#isValidWorkbenchPart(org.eclipse.ui.IWorkbenchPart) |
| */ |
| @Override |
| protected boolean isValidWorkbenchPart(IWorkbenchPart part) { |
| if (part instanceof IEditorPart) { |
| final IEditorPart editor = (IEditorPart) part; |
| final String editorNature = ScriptUtils.getNatureId(editor); |
| if (editorNature != null) { |
| return editorNature.equals(getNature()); |
| } |
| ISourceModule sourceModule = EditorUtility |
| .getEditorInputModelElement(editor, false); |
| if (sourceModule != null) { |
| final IDLTKLanguageToolkit toolkit = DLTKLanguageManager |
| .getLanguageToolkit(sourceModule); |
| return toolkit != null |
| && toolkit.getNatureId().equals(getNature()); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Gets the label for the given member. |
| * |
| * @param member |
| * the Script member |
| * @return a string containing the member's label |
| */ |
| private String getInfoText(Object member) { |
| if (member instanceof IModelElement) { |
| return ScriptElementLabels.getDefault() |
| .getElementLabel((IModelElement) member, LABEL_FLAGS); |
| } else { |
| // TODO |
| return null; |
| } |
| } |
| |
| @Override |
| protected Control getControl() { |
| if (fIsUsingBrowserWidget) |
| return fBrowser; |
| else |
| return fText; |
| } |
| |
| @Override |
| protected String getHelpContextId() { |
| // return IJavaHelpContextIds.JAVADOC_VIEW; |
| // TODO: add help support here |
| return ""; //$NON-NLS-1$ |
| } |
| } |