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