org.eclipse.mylyn.wikitext.tasks.ui.editor.MarkupTaskEditorExtension.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.mylyn.wikitext.tasks.ui.editor.MarkupTaskEditorExtension.java

Source

/*******************************************************************************
 * Copyright (c) 2007, 2013 David Green 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:
 *     David Green - initial API and implementation
 *     Frank Becker - improvements for bug 304910
 *     Tasktop Technologies - improvements
 *******************************************************************************/
package org.eclipse.mylyn.wikitext.tasks.ui.editor;

import java.util.Iterator;
import java.util.Map;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.DefaultAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.mylyn.commons.workbench.browser.UrlHyperlink;
import org.eclipse.mylyn.internal.wikitext.tasks.ui.WikiTextTasksUiPlugin;
import org.eclipse.mylyn.internal.wikitext.tasks.ui.util.Util;
import org.eclipse.mylyn.internal.wikitext.ui.WikiTextUiPlugin;
import org.eclipse.mylyn.internal.wikitext.ui.editor.MarkupEditor;
import org.eclipse.mylyn.internal.wikitext.ui.util.PreferenceStoreFacade;
import org.eclipse.mylyn.internal.wikitext.ui.viewer.AnnotationHyperlinkDetector;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.TaskHyperlinkPresenter;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorExtension;
import org.eclipse.mylyn.wikitext.core.parser.markup.AbstractMarkupLanguage;
import org.eclipse.mylyn.wikitext.core.parser.markup.MarkupLanguage;
import org.eclipse.mylyn.wikitext.core.parser.markup.MarkupLanguageConfiguration;
import org.eclipse.mylyn.wikitext.ui.editor.MarkupSourceViewer;
import org.eclipse.mylyn.wikitext.ui.editor.MarkupSourceViewerConfiguration;
import org.eclipse.mylyn.wikitext.ui.editor.ShowInTargetBridge;
import org.eclipse.mylyn.wikitext.ui.viewer.DefaultHyperlinkDetectorDescriptorFilter;
import org.eclipse.mylyn.wikitext.ui.viewer.MarkupViewer;
import org.eclipse.mylyn.wikitext.ui.viewer.MarkupViewerConfiguration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.swt.IFocusService;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;

/**
 * A task editor extension that uses a markup language to parse content. Provides a markup-aware source editor, and a
 * source viewer that displays markup in its intended formatted form.
 * 
 * @author David Green
 * @since 1.0
 */
public class MarkupTaskEditorExtension<MarkupLanguageType extends MarkupLanguage>
        extends AbstractTaskEditorExtension implements IAdaptable {

    private static final String MARKUP_SOURCE_CONTEXT_ID = "org.eclipse.mylyn.wikitext.tasks.ui.markupSourceContext"; //$NON-NLS-1$

    private static final String MARKUP_VIEWER = "org.eclipse.mylyn.wikitext.tasks.ui.markupViewer"; //$NON-NLS-1$

    /**
     * Provide a means to disable WikiWord linking. This feature is experimental and may be removed in a future release.
     * To enable this feature, set the system property <tt>MarkupTaskEditorExtension.wikiWordDisabled</tt> to
     * <tt>true</tt>. eg, <tt>-DMarkupTaskEditorExtension.wikiWordDisabled=true</tt>
     */
    private static final boolean DISABLE_WIKI_WORD = Boolean
            .getBoolean(MarkupTaskEditorExtension.class.getSimpleName() + ".wikiWordDisabled"); //$NON-NLS-1$

    private static final String ID_CONTEXT_EDITOR_TASK = "org.eclipse.mylyn.tasks.ui.TaskEditor"; //$NON-NLS-1$

    private static final String ID_CONTEXT_EDITOR_TEXT = "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$

    private MarkupLanguageType markupLanguage;

    public MarkupLanguageType getMarkupLanguage() {
        return markupLanguage;
    }

    public void setMarkupLanguage(MarkupLanguageType markupLanguage) {
        this.markupLanguage = markupLanguage;
    }

    @Override
    public String getEditorContextId() {
        return MARKUP_SOURCE_CONTEXT_ID;
    }

    @Deprecated
    @Override
    public SourceViewer createViewer(TaskRepository taskRepository, Composite parent, int style) {
        return createViewer(taskRepository, parent, style, null);
    }

    @Override
    public SourceViewer createViewer(TaskRepository taskRepository, Composite parent, int style,
            IAdaptable context) {
        if (markupLanguage == null) {
            throw new IllegalStateException();
        }
        MarkupViewer markupViewer = new MarkupViewer(parent, null, style | SWT.FLAT | SWT.WRAP);
        MarkupLanguageType markupLanguageCopy = createRepositoryMarkupLanguage(taskRepository);
        configureMarkupLanguage(taskRepository, markupLanguageCopy);

        markupViewer.setMarkupLanguage(markupLanguageCopy);

        MarkupViewerConfiguration configuration = createViewerConfiguration(taskRepository, markupViewer, context);
        configuration.setDisableHyperlinkModifiers(true);
        configuration.setEnableSelfContainedIncrementalFind(true);

        if (markupLanguageCopy instanceof AbstractMarkupLanguage
                && ((AbstractMarkupLanguage) markupLanguageCopy).isDetectingRawHyperlinks()) {
            // bug 264612 don't detect hyperlinks twice
            configuration.addHyperlinkDetectorDescriptorFilter(new DefaultHyperlinkDetectorDescriptorFilter(
                    "org.eclipse.mylyn.tasks.ui.hyperlinks.detectors.url")); //$NON-NLS-1$
        }
        markupViewer.configure(configuration);

        markupViewer.setEditable(false);
        markupViewer.getTextWidget().setCaret(null);

        if (JFaceResources.getFontRegistry().hasValueFor(WikiTextTasksUiPlugin.FONT_REGISTRY_KEY_DEFAULT_FONT)) {
            markupViewer.getTextWidget().setFont(
                    JFaceResources.getFontRegistry().get(WikiTextTasksUiPlugin.FONT_REGISTRY_KEY_DEFAULT_FONT));
        }
        if (JFaceResources.getFontRegistry().hasValueFor(WikiTextTasksUiPlugin.FONT_REGISTRY_KEY_MONOSPACE_FONT)) {
            markupViewer.setDefaultMonospaceFont(
                    JFaceResources.getFontRegistry().get(WikiTextTasksUiPlugin.FONT_REGISTRY_KEY_MONOSPACE_FONT));
        }

        markupViewer.setStylesheet(WikiTextUiPlugin.getDefault().getPreferences().getStylesheet());

        IFocusService focusService = (IFocusService) PlatformUI.getWorkbench().getService(IFocusService.class);
        if (focusService != null) {
            focusService.addFocusTracker(markupViewer.getTextWidget(), MARKUP_VIEWER);
        }
        markupViewer.getTextWidget().setData(ISourceViewer.class.getName(), markupViewer);

        return markupViewer;
    }

    @SuppressWarnings("unchecked")
    private MarkupLanguageType createRepositoryMarkupLanguage(TaskRepository taskRepository) {
        MarkupLanguageType copy = (MarkupLanguageType) markupLanguage.clone();
        MarkupLanguageConfiguration configuration = createMarkupLanguageConfiguration(taskRepository);
        copy.configure(configuration);
        return copy;
    }

    /**
     * @since 1.3
     */
    protected MarkupLanguageConfiguration createMarkupLanguageConfiguration(TaskRepository taskRepository) {
        MarkupLanguageConfiguration configuration = Util.create(taskRepository.getConnectorKind());
        if (DISABLE_WIKI_WORD) {
            configuration.setWikiWordLinking(false);
        }
        return configuration;
    }

    protected TaskMarkupViewerConfiguration createViewerConfiguration(TaskRepository taskRepository,
            MarkupViewer markupViewer) {
        return createViewerConfiguration(taskRepository, markupViewer, null);
    }

    /**
     * @since 1.3
     */
    protected TaskMarkupViewerConfiguration createViewerConfiguration(TaskRepository taskRepository,
            MarkupViewer markupViewer, IAdaptable context) {
        return new TaskMarkupViewerConfiguration(markupViewer, taskRepository, context);
    }

    protected TaskMarkupSourceViewerConfiguration createSourceViewerConfiguration(TaskRepository taskRepository,
            SourceViewer viewer) {
        return createSourceViewerConfiguration(taskRepository, viewer, null);
    }

    /**
     * @since 1.3
     */
    protected TaskMarkupSourceViewerConfiguration createSourceViewerConfiguration(TaskRepository taskRepository,
            SourceViewer viewer, IAdaptable context) {
        IPreferenceStore preferenceStore = EditorsUI.getPreferenceStore();
        return new TaskMarkupSourceViewerConfiguration(preferenceStore, taskRepository, context);
    }

    @Deprecated
    @Override
    public SourceViewer createEditor(TaskRepository taskRepository, Composite parent, int style) {
        return createEditor(taskRepository, parent, style, null);
    }

    @SuppressWarnings("unchecked")
    @Override
    public SourceViewer createEditor(TaskRepository taskRepository, Composite parent, int style,
            IAdaptable context) {
        final MarkupLanguageType markupLanguageCopy = createRepositoryMarkupLanguage(taskRepository);
        configureMarkupLanguage(taskRepository, markupLanguageCopy);

        SourceViewer viewer = new MarkupSourceViewer(parent, null, style | SWT.WRAP, markupLanguageCopy);

        // configure the viewer
        MarkupSourceViewerConfiguration configuration = createSourceViewerConfiguration(taskRepository, viewer,
                context);

        configuration.setEnableSelfContainedIncrementalFind(true);
        configuration.setMarkupLanguage(markupLanguageCopy);
        configuration.setShowInTarget(new ShowInTargetBridge(viewer));
        viewer.configure(configuration);

        // we want the viewer to show annotations
        viewer.showAnnotations(true);

        DefaultMarkerAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
        MarkerAnnotationPreferences annotationPreferences = new MarkerAnnotationPreferences();

        // configure viewer annotation/decoration support
        final SourceViewerDecorationSupport support = new SourceViewerDecorationSupport(viewer, null,
                annotationAccess, EditorsUI.getSharedTextColors());

        // hook the support up to the preference store
        Iterator<AnnotationPreference> e = annotationPreferences.getAnnotationPreferences().iterator();
        while (e.hasNext()) {
            AnnotationPreference preference = e.next();
            support.setAnnotationPreference(preference);
        }
        support.setCursorLinePainterPreferenceKeys(
                AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE,
                AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR);
        support.setMarginPainterPreferenceKeys(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN,
                AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLOR,
                AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN);

        support.install(new EditorExtensionPreferenceStore(EditorsUI.getPreferenceStore(), viewer.getControl()));
        viewer.getControl().addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                support.dispose();
            }
        });

        IFocusService focusService = (IFocusService) PlatformUI.getWorkbench().getService(IFocusService.class);
        if (focusService != null) {
            focusService.addFocusTracker(viewer.getTextWidget(), MarkupEditor.EDITOR_SOURCE_VIEWER);
        }

        viewer.getTextWidget().setData(MarkupLanguage.class.getName(), markupLanguageCopy);
        viewer.getTextWidget().setData(ISourceViewer.class.getName(), viewer);

        return viewer;
    }

    /**
     * Configures the markup language with settings from the task repository. Subclasses may override this method, but
     * should call <code>super.configureMarkupLanguage(taskRepository,markupLanguage)</code>.
     * 
     * @param taskRepository
     *            the repository from which settings should be used
     * @param markupLanguage
     *            the markup language to configure
     * @see #configureDefaultInternalLinkPattern(TaskRepository, MarkupLanguage)
     */
    protected void configureMarkupLanguage(TaskRepository taskRepository, MarkupLanguageType markupLanguage) {
        String internalLinkPattern = taskRepository
                .getProperty(AbstractTaskEditorExtension.INTERNAL_WIKI_LINK_PATTERN);
        if (internalLinkPattern != null && internalLinkPattern.trim().length() > 0) {
            ((AbstractMarkupLanguage) markupLanguage).setInternalLinkPattern(internalLinkPattern.trim());
        } else {
            configureDefaultInternalLinkPattern(taskRepository, markupLanguage);
        }
    }

    /**
     * Overriding methods should set the {@link MarkupLanguage#getInternalLinkPattern() internal hyperlink pattern} of
     * the given markup language based on some default rules applied to the task repository URL. The default
     * implementation does nothing.
     * 
     * @param taskRepository
     *            the task repository from which settings may be obtained
     * @param markupLanguage
     *            the markup language to configure
     */
    protected void configureDefaultInternalLinkPattern(TaskRepository taskRepository,
            MarkupLanguageType markupLanguage) {
        // nothing to do
    }

    protected static class TaskMarkupSourceViewerConfiguration extends MarkupSourceViewerConfiguration {

        private final TaskRepository taskRepository;

        private final IAdaptable context;

        public TaskMarkupSourceViewerConfiguration(IPreferenceStore preferenceStore,
                TaskRepository taskRepository) {
            this(preferenceStore, taskRepository, null);
            setEnableHippieContentAssist(false);
        }

        /**
         * @since 1.3
         */
        public TaskMarkupSourceViewerConfiguration(IPreferenceStore preferenceStore, TaskRepository taskRepository,
                IAdaptable context) {
            super(preferenceStore, WikiTextTasksUiPlugin.FONT_REGISTRY_KEY_DEFAULT_FONT,
                    WikiTextTasksUiPlugin.FONT_REGISTRY_KEY_MONOSPACE_FONT);
            this.taskRepository = taskRepository;
            if (context == null) {
                this.context = createDefaultHyperlinkDetectorContext(taskRepository);
            } else {
                this.context = context;
            }
            // filter out the platform URL hyperlink detector since Mylyn contributes one as well.
            addHyperlinkDetectorDescriptorFilter(new DefaultHyperlinkDetectorDescriptorFilter(
                    "org.eclipse.ui.internal.editors.text.URLHyperlinkDetector")); //$NON-NLS-1$
        }

        @Override
        protected IContentAssistProcessor[] createContentAssistProcessors() {
            IContentAssistProcessor processor = TasksUi.getUiFactory()
                    .createTaskContentAssistProcessor(taskRepository);
            return processor == null ? null : new IContentAssistProcessor[] { processor };
        }

        @Override
        public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
            return new DefaultAnnotationHover() {
                @Override
                protected boolean isIncluded(Annotation annotation) {
                    return annotation.getType().startsWith("org.eclipse.wikitext") || super.isIncluded(annotation); //$NON-NLS-1$
                }
            };
        }

        @SuppressWarnings("rawtypes")
        @Override
        protected Map getHyperlinkDetectorTargets(ISourceViewer sourceViewer) {
            Map hyperlinkDetectorTargets = super.getHyperlinkDetectorTargets(sourceViewer);
            addRepositoryHyperlinkDetectorTargets(context, hyperlinkDetectorTargets);
            return hyperlinkDetectorTargets;
        }

        @Override
        public IReconciler getReconciler(ISourceViewer sourceViewer) {
            if (sourceViewer.isEditable()) {
                return super.getReconciler(sourceViewer);
            } else {
                return null;
            }
        }

        @Override
        public int getHyperlinkStateMask(ISourceViewer sourceViewer) {
            return SWT.NONE;
        }

        @Override
        public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) {
            if (fPreferenceStore == null) {
                return new TaskHyperlinkPresenter(new RGB(0, 0, 255));
            }
            return new TaskHyperlinkPresenter(fPreferenceStore);
        }
    }

    protected static class TaskMarkupViewerConfiguration extends MarkupViewerConfiguration {

        private final IAdaptable context;

        public TaskMarkupViewerConfiguration(MarkupViewer viewer, TaskRepository taskRepository) {
            this(viewer, taskRepository, null);
        }

        /**
         * @since 1.3
         */
        public TaskMarkupViewerConfiguration(MarkupViewer viewer, TaskRepository taskRepository,
                IAdaptable context) {
            super(viewer);
            if (context == null) {
                this.context = createDefaultHyperlinkDetectorContext(taskRepository);
            } else {
                this.context = context;
            }
            markupHyperlinksFirst = false;
        }

        @SuppressWarnings("rawtypes")
        @Override
        protected Map getHyperlinkDetectorTargets(ISourceViewer sourceViewer) {
            Map hyperlinkDetectorTargets = super.getHyperlinkDetectorTargets(sourceViewer);
            addRepositoryHyperlinkDetectorTargets(context, hyperlinkDetectorTargets);
            return hyperlinkDetectorTargets;
        }

        /**
         * @noreference This method is not intended to be referenced by clients.
         */
        @Override
        protected AnnotationHyperlinkDetector createAnnotationHyperlinkDetector() {
            return new PlatformUrlAnnotationHyperlinkDetector();
        }

        @Override
        public IHyperlinkPresenter getHyperlinkPresenter(ISourceViewer sourceViewer) {
            if (fPreferenceStore == null) {
                return new TaskHyperlinkPresenter(new RGB(0, 0, 255));
            }
            return new TaskHyperlinkPresenter(fPreferenceStore);
        }
    }

    private static class PlatformUrlAnnotationHyperlinkDetector extends AnnotationHyperlinkDetector {
        @Override
        protected IHyperlink createUrlHyperlink(IRegion region, String href) {
            return new UrlHyperlink(region, href);
        }
    }

    @SuppressWarnings({ "rawtypes" })
    private static IAdaptable createDefaultHyperlinkDetectorContext(final TaskRepository repository) {
        return new IAdaptable() {
            public Object getAdapter(Class adapter) {
                if (adapter == TaskRepository.class) {
                    return repository;
                }
                return null;
            }
        };
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static void addRepositoryHyperlinkDetectorTargets(IAdaptable context, Map hyperlinkDetectorTargets) {
        hyperlinkDetectorTargets.put(ID_CONTEXT_EDITOR_TEXT, context);
        hyperlinkDetectorTargets.put(ID_CONTEXT_EDITOR_TASK, context);
    }

    /**
     * bug 251657: wrap preferences so that we can alter the current line highlight based on the focus state of the
     * provided control. bug 273528: override workspace preferences to eliminate print margin in the task editor
     * 
     * @author David Green
     */
    private static class EditorExtensionPreferenceStore extends PreferenceStoreFacade {

        // track separately from isFocusControl() since isFocusControl() is not accurate while processing a focus event
        boolean controlFocused;

        public EditorExtensionPreferenceStore(IPreferenceStore preferenceStore, Control control) {
            super(preferenceStore);
            controlFocused = control.isFocusControl();
            control.addFocusListener(new FocusListener() {
                public void focusGained(FocusEvent e) {
                    controlFocused = true;
                    focusChanged();
                }

                public void focusLost(FocusEvent e) {
                    controlFocused = false;
                    focusChanged();
                }
            });
        }

        protected void focusChanged() {
            if (!getCurrentLineHighlightPreference()) {
                return;
            }
            boolean newValue = getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE);
            firePropertyChangeEvent(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE, !newValue,
                    newValue);
        }

        private boolean getCurrentLineHighlightPreference() {
            return TasksUiUtil.getHighlightCurrentLine();
        }

        @Override
        public boolean getBoolean(String name) {
            if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE.equals(name)) {
                if (!controlFocused) {
                    return false;
                }
                return getCurrentLineHighlightPreference();
            }
            if (AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN.equals(name)) {
                return false;
            }
            return super.getBoolean(name);
        }
    }

    /**
     * @since 1.6
     */
    @SuppressWarnings("rawtypes")
    public Object getAdapter(Class adapter) {
        if (MarkupLanguage.class == adapter) {
            return getMarkupLanguage();
        }
        return Platform.getAdapterManager().getAdapter(this, adapter);
    }
}