### Eclipse Workspace Patch 1.0 #P org.eclipse.mylyn.commons.ui Index: src/org/eclipse/mylyn/internal/provisional/commons/ui/CommonTextSupport.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.commons.ui/src/org/eclipse/mylyn/internal/provisional/commons/ui/CommonTextSupport.java,v retrieving revision 1.1 diff -u -r1.1 CommonTextSupport.java --- src/org/eclipse/mylyn/internal/provisional/commons/ui/CommonTextSupport.java 13 Jan 2009 08:43:27 -0000 1.1 +++ src/org/eclipse/mylyn/internal/provisional/commons/ui/CommonTextSupport.java 27 May 2009 07:30:28 -0000 @@ -25,6 +25,7 @@ import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.util.Assert; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; @@ -208,6 +209,7 @@ private ISelectionChangedListener selectionChangedListener; public CommonTextSupport(IHandlerService handlerService) { + Assert.isNotNull(handlerService); this.handlerService = handlerService; } #P org.eclipse.mylyn.tasks.ui Index: src/org/eclipse/mylyn/tasks/ui/wizards/TaskAttachmentPage.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/wizards/TaskAttachmentPage.java,v retrieving revision 1.21 diff -u -r1.21 TaskAttachmentPage.java --- src/org/eclipse/mylyn/tasks/ui/wizards/TaskAttachmentPage.java 8 Apr 2009 04:54:10 -0000 1.21 +++ src/org/eclipse/mylyn/tasks/ui/wizards/TaskAttachmentPage.java 27 May 2009 07:30:29 -0000 @@ -20,11 +20,15 @@ import org.eclipse.jface.wizard.WizardPage; import org.eclipse.mylyn.context.core.ContextCore; import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonTextSupport; import org.eclipse.mylyn.internal.tasks.core.data.FileTaskAttachmentSource; +import org.eclipse.mylyn.internal.tasks.ui.editors.RichTextEditor; +import org.eclipse.mylyn.internal.tasks.ui.editors.TaskEditorExtensions; import org.eclipse.mylyn.internal.tasks.ui.wizards.Messages; import org.eclipse.mylyn.tasks.core.data.TaskAttachmentMapper; import org.eclipse.mylyn.tasks.core.data.TaskAttachmentModel; import org.eclipse.mylyn.tasks.ui.TasksUiImages; +import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorExtension; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; @@ -38,6 +42,10 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.handlers.IHandlerService; /** * A wizard page to enter details of a new attachment. @@ -64,7 +72,7 @@ private Button attachContextButton; - private Text commentText; + private RichTextEditor commentEditor; private Text descriptionText; @@ -82,6 +90,12 @@ private boolean first = true; + private IContextService contextService; + + private IContextActivation commentContext; + + private CommonTextSupport textSupport; + public TaskAttachmentPage(TaskAttachmentModel model) { super("AttachmentDetails"); //$NON-NLS-1$ this.model = model; @@ -120,14 +134,30 @@ Label label = new Label(composite, SWT.NONE); label.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, false)); label.setText(Messages.TaskAttachmentPage_Comment); - commentText = new Text(composite, SWT.V_SCROLL | SWT.BORDER | SWT.WRAP); - commentText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); - commentText.addModifyListener(new ModifyListener() { - public void modifyText(ModifyEvent e) { - apply(); + AbstractTaskEditorExtension extension = TaskEditorExtensions.getTaskEditorExtension(model.getTaskRepository()); + String contextId = extension.getEditorContextId(); + if (contextId != null) { + contextService = (IContextService) PlatformUI.getWorkbench().getService(IContextService.class); + if (contextService != null) { + commentContext = contextService.activateContext(contextId); } + } - }); + commentEditor = new RichTextEditor(getModel().getTaskRepository(), SWT.V_SCROLL | SWT.BORDER | SWT.WRAP, + contextService, extension) { + @Override + protected void valueChanged(String value) { + apply(); + }; + }; + commentEditor.createControl(composite, null); + commentEditor.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + + IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); + if (handlerService != null) { + textSupport = new CommonTextSupport(handlerService); + textSupport.install(commentEditor.getViewer(), true); + } new Label(composite, SWT.NONE).setText(Messages.TaskAttachmentPage_Content_Type);// .setBackground(parent.getBackground()); @@ -223,7 +253,7 @@ if (descriptionText != null) { descriptionText.setFocus(); } else { - commentText.setFocus(); + commentEditor.getControl().setFocus(); } Dialog.applyDialogFont(composite); @@ -249,7 +279,7 @@ private void apply() { taskAttachment.applyTo(model.getAttribute()); - model.setComment(commentText.getText()); + model.setComment(commentEditor.getText()); model.setAttachContext(attachContextButton.getSelection()); model.setContentType(taskAttachment.getContentType()); } @@ -305,10 +335,22 @@ if (descriptionText != null) { descriptionText.setFocus(); } else { - commentText.setFocus(); + commentEditor.getControl().setFocus(); } first = false; } } + @Override + public void dispose() { + super.dispose(); + if (contextService != null && commentContext != null) { + contextService.deactivateContext(commentContext); + commentContext = null; + } + if (textSupport != null) { + textSupport.dispose(); + } + } + } Index: src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextAttributeEditor.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextAttributeEditor.java,v retrieving revision 1.37 diff -u -r1.37 RichTextAttributeEditor.java --- src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextAttributeEditor.java 30 Apr 2009 04:03:35 -0000 1.37 +++ src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextAttributeEditor.java 27 May 2009 07:30:29 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2008 Tasktop Technologies and others. + * Copyright (c) 2004, 2008 Tasktop Technologies 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 @@ -7,31 +7,14 @@ * * Contributors: * Tasktop Technologies - initial API and implementation - * Raphael Ackermann - spell checking support on bug 195514 - * Jingwen Ou - extensibility improvements - * David Green - fix for bug 256702 *******************************************************************************/ package org.eclipse.mylyn.internal.tasks.ui.editors; -import java.util.Iterator; - import org.eclipse.core.runtime.Assert; -import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.text.Document; -import org.eclipse.jface.text.ITextListener; -import org.eclipse.jface.text.TextEvent; -import org.eclipse.jface.text.source.AnnotationModel; -import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.SourceViewer; -import org.eclipse.mylyn.internal.provisional.commons.ui.CommonFormUtil; -import org.eclipse.mylyn.internal.provisional.commons.ui.CommonTextSupport; -import org.eclipse.mylyn.internal.provisional.commons.ui.CommonThemes; -import org.eclipse.mylyn.internal.tasks.ui.commands.ViewSourceHandler; import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTextViewerConfiguration.Mode; -import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.core.data.TaskAttribute; import org.eclipse.mylyn.tasks.core.data.TaskDataModel; @@ -42,93 +25,23 @@ import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.ColumnSpan; import org.eclipse.mylyn.tasks.ui.editors.LayoutHint.RowSpan; import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.StackLayout; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; -import org.eclipse.swt.events.FocusListener; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Menu; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.contexts.IContextActivation; import org.eclipse.ui.contexts.IContextService; -import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.forms.widgets.FormToolkit; -import org.eclipse.ui.texteditor.AnnotationPreference; -import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; -import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; -import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; -import org.eclipse.ui.themes.IThemeManager; /** * A text attribute editor that can switch between a editor, preview and source view. * - * @author Raphael Ackermann * @author Steffen Pingel - * @author Jingwen Ou + * @see RichTextEditor */ public class RichTextAttributeEditor extends AbstractAttributeEditor { - private IContextActivation contextActivation; - - private final IContextService contextService; - - private SourceViewer defaultViewer; - - private Composite editorComposite; - - private StackLayout editorLayout; - - private final AbstractTaskEditorExtension extension; - - private SourceViewer editorViewer; - - private SourceViewer previewViewer; - - private final TaskRepository taskRepository; - - private FormToolkit toolkit; - - public class ViewSourceAction extends Action { - - public ViewSourceAction() { - super(Messages.RichTextAttributeEditor_Viewer_Source, SWT.TOGGLE); - setChecked(false); - setEnabled(false); - } - - @Override - public void run() { - if (isChecked()) { - showDefault(); - } else { - showEditor(); - } - if (editorLayout != null) { - EditorUtil.reflow(editorLayout.topControl); - } - ViewSourceHandler.setChecked(isChecked()); - } - - } - - private final IAction viewSourceAction = new ViewSourceAction(); - - private boolean spellCheckingEnabled; - - private final int style; + private final RichTextEditor editor; private Mode mode; - private AbstractRenderingEngine renderingEngine; - - private BrowserPreviewViewer browserViewer; + protected boolean ignoreNotification; public RichTextAttributeEditor(TaskDataModel manager, TaskRepository taskRepository, TaskAttribute taskAttribute) { this(manager, taskRepository, taskAttribute, SWT.MULTI); @@ -142,397 +55,116 @@ public RichTextAttributeEditor(TaskDataModel manager, TaskRepository taskRepository, TaskAttribute taskAttribute, int style, IContextService contextService, AbstractTaskEditorExtension extension) { super(manager, taskAttribute); - this.taskRepository = taskRepository; - this.style = style; - this.contextService = contextService; - this.extension = extension; + this.editor = new RichTextEditor(taskRepository, style, contextService, extension) { + @Override + public void valueChanged(String value) { + if (!ignoreNotification) { + RichTextAttributeEditor.this.setValue(value); + } + }; + }; + this.editor.setReadOnly(isReadOnly()); if ((style & SWT.MULTI) != 0) { setLayoutHint(new LayoutHint(RowSpan.MULTIPLE, ColumnSpan.MULTIPLE)); } else { setLayoutHint(new LayoutHint(RowSpan.SINGLE, ColumnSpan.MULTIPLE)); } setMode(Mode.DEFAULT); - } - - public Mode getMode() { - return mode; - } - - public void setMode(Mode mode) { - Assert.isNotNull(mode); - this.mode = mode; - } - - private void installListeners(final SourceViewer viewer) { - viewer.addTextListener(new ITextListener() { - public void textChanged(TextEvent event) { - // filter out events caused by text presentation changes, e.g. annotation drawing - String value = viewer.getTextWidget().getText(); - if (!getValue().equals(value)) { - setValue(value); - CommonFormUtil.ensureVisible(viewer.getTextWidget()); - } - } - }); - // ensure that tab traverses to next control instead of inserting a tab character unless editing multi-line text - if ((style & SWT.MULTI) != 0 && mode != Mode.DEFAULT) { - viewer.getTextWidget().addListener(SWT.Traverse, new Listener() { - public void handleEvent(Event event) { - switch (event.detail) { - case SWT.TRAVERSE_TAB_NEXT: - case SWT.TRAVERSE_TAB_PREVIOUS: - event.doit = true; - break; - } - } - }); - } - } - - public String getValue() { - return getAttributeMapper().getValue(getTaskAttribute()); - } - - public boolean isSpellCheckingEnabled() { - return spellCheckingEnabled; - } - - public void setSpellCheckingEnabled(boolean spellCheckingEnabled) { - this.spellCheckingEnabled = spellCheckingEnabled; - } - - public void setValue(String value) { - getAttributeMapper().setValue(getTaskAttribute(), value); - attributeChanged(); - } - - /** Configures annotation model for spell checking. */ - private void configureAsEditor(SourceViewer viewer, Document document) { - AnnotationModel annotationModel = new AnnotationModel(); - viewer.showAnnotations(false); - viewer.showAnnotationsOverview(false); - IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess(); - final SourceViewerDecorationSupport support = new SourceViewerDecorationSupport(viewer, null, annotationAccess, - EditorsUI.getSharedTextColors()); - Iterator e = new MarkerAnnotationPreferences().getAnnotationPreferences().iterator(); - while (e.hasNext()) { - support.setAnnotationPreference((AnnotationPreference) e.next()); - } - support.install(EditorsUI.getPreferenceStore()); - viewer.getTextWidget().addDisposeListener(new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - support.uninstall(); - } - }); - //viewer.getTextWidget().setIndent(2); - viewer.setDocument(document, annotationModel); - } - - private RepositoryTextViewerConfiguration installHyperlinkPresenter(SourceViewer viewer) { - RepositoryTextViewerConfiguration configuration = new RepositoryTextViewerConfiguration(taskRepository, false); - configuration.setMode(getMode()); - - // do not configure viewer, this has already been done in extension - - AbstractHyperlinkTextPresentationManager manager; - if (getMode() == Mode.DEFAULT) { - manager = new HighlightingHyperlinkTextPresentationManager(); - manager.setHyperlinkDetectors(configuration.getDefaultHyperlinkDetectors(viewer, null)); - manager.install(viewer); - - manager = new TaskHyperlinkTextPresentationManager(); - manager.setHyperlinkDetectors(configuration.getDefaultHyperlinkDetectors(viewer, Mode.TASK)); - manager.install(viewer); - } else if (getMode() == Mode.TASK_RELATION) { - manager = new TaskHyperlinkTextPresentationManager(); - manager.setHyperlinkDetectors(configuration.getDefaultHyperlinkDetectors(viewer, Mode.TASK_RELATION)); - manager.install(viewer); - } - - return configuration; - } - - private SourceViewer configure(final SourceViewer viewer, boolean readOnly) { - // do this before setting the document to not require invalidating the presentation - installHyperlinkPresenter(viewer); - - Document document = new Document(getValue()); - if (readOnly) { - viewer.setDocument(document); - if (extension != null) { - // setting view source action - viewer.getControl().setData(ViewSourceHandler.VIEW_SOURCE_ACTION, viewSourceAction); - viewer.getControl().addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - ViewSourceHandler.setChecked(getViewer() == defaultViewer); - } - }); - } - } else { - configureAsEditor(viewer, document); - installListeners(viewer); - viewer.getControl().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); - } - - // enable cut/copy/paste - CommonTextSupport.setTextViewer(viewer.getTextWidget(), viewer); - viewer.setEditable(!readOnly); - viewer.getTextWidget().setFont(getFont()); - toolkit.adapt(viewer.getControl(), false, false); - - return viewer; + refresh(); } @Override public void createControl(Composite parent, FormToolkit toolkit) { - this.toolkit = toolkit; - - int style = this.style; - if (!isReadOnly() && (style & TasksUiInternal.SWT_NO_SCROLL) == 0) { - style |= SWT.V_SCROLL; - } - - if (extension != null || renderingEngine != null) { - editorComposite = new Composite(parent, SWT.NULL); - editorLayout = new StackLayout() { - @Override - protected Point computeSize(Composite composite, int hint, int hint2, boolean flushCache) { - return topControl.computeSize(hint, hint2, flushCache); - } - }; - editorComposite.setLayout(editorLayout); - setControl(editorComposite); - - if (extension != null) { - if (isReadOnly()) { - editorViewer = extension.createViewer(taskRepository, editorComposite, style); - } else { - editorViewer = extension.createEditor(taskRepository, editorComposite, style); - editorViewer.getTextWidget().addFocusListener(new FocusListener() { - public void focusGained(FocusEvent e) { - setContext(); - } - - public void focusLost(FocusEvent e) { - unsetContext(); - } - }); - editorViewer.getTextWidget().addDisposeListener(new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - unsetContext(); - } - }); - } - configure(editorViewer, isReadOnly()); - show(editorViewer); - } else { - defaultViewer = createDefaultEditor(editorComposite, style); - configure(defaultViewer, isReadOnly()); - show(defaultViewer); - } - - if (!isReadOnly() && (style & TasksUiInternal.SWT_NO_SCROLL) == 0) { - editorComposite.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); - } - - viewSourceAction.setEnabled(true); - } else { - defaultViewer = createDefaultEditor(parent, style); - configure(defaultViewer, isReadOnly()); - setControl(defaultViewer.getControl()); - - viewSourceAction.setEnabled(false); - } + editor.createControl(parent, toolkit); + setControl(editor.getControl()); } - private SourceViewer createDefaultEditor(Composite parent, int styles) { - SourceViewer defaultEditor = new SourceViewer(parent, null, styles | SWT.WRAP); - - RepositoryTextViewerConfiguration viewerConfig = new RepositoryTextViewerConfiguration(taskRepository, - isSpellCheckingEnabled() && !isReadOnly()); - viewerConfig.setMode(getMode()); - defaultEditor.configure(viewerConfig); - - return defaultEditor; - } - - public SourceViewer getDefaultViewer() { - if (defaultViewer == null) { - defaultViewer = createDefaultEditor(editorComposite, style); - configure(defaultViewer, isReadOnly()); - - // fixed font size - defaultViewer.getTextWidget().setFont(JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT)); - // adapt maximize action - defaultViewer.getControl().setData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION, - editorViewer.getControl().getData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION)); - // adapt menu to the new viewer - installMenu(defaultViewer.getControl(), editorViewer.getControl().getMenu()); - } - return defaultViewer; - } - - private void installMenu(final Control control, Menu menu) { - if (menu != null) { - control.setMenu(menu); - control.addDisposeListener(new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - control.setMenu(null); - } - }); - } + public SourceViewer getEditorViewer() { + return editor.getEditorViewer(); } - private Font getFont() { - IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); - Font font = themeManager.getCurrentTheme().getFontRegistry().get(CommonThemes.FONT_EDITOR_COMMENT); - return font; + public Mode getMode() { + return mode; } - private SourceViewer getPreviewViewer() { - if (extension == null) { - return null; - } - - // construct as needed - if (previewViewer == null) { - // previewer should always have a vertical scroll bar if it's editable - int previewViewerStyle = style; - if (getEditorViewer() != null) { - previewViewerStyle |= SWT.V_SCROLL; - } - previewViewer = extension.createViewer(taskRepository, editorComposite, previewViewerStyle); - configure(previewViewer, true); - // adapt maximize action - previewViewer.getControl().setData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION, - editorViewer.getControl().getData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION)); - } - Document document = new Document(editorViewer.getDocument().get()); - previewViewer.setDocument(document); - return previewViewer; + public AbstractRenderingEngine getRenderingEngine() { + return editor.getRenderingEngine(); } - public SourceViewer getEditorViewer() { - return editorViewer; + public String getValue() { + return getAttributeMapper().getValue(getTaskAttribute()); } public SourceViewer getViewer() { - if (editorLayout == null) { - return defaultViewer; - } - if (defaultViewer != null && editorLayout.topControl == defaultViewer.getControl()) { - return defaultViewer; - } else if (previewViewer != null && editorLayout.topControl == previewViewer.getControl()) { - return previewViewer; - } else { - return editorViewer; - } + return editor.getViewer(); } - private void setContext() { - if (contextService == null) { - return; - } - if (contextActivation != null) { - contextService.deactivateContext(contextActivation); - contextActivation = null; - } - if (contextService != null && extension.getEditorContextId() != null) { - contextActivation = contextService.activateContext(extension.getEditorContextId()); - } + public IAction getViewSourceAction() { + return editor.getViewSourceAction(); } - /** - * Brings viewer to top. - */ - private void show(SourceViewer viewer) { - show(viewer.getControl()); + public boolean hasBrowser() { + return editor.hasBrowser(); } - /** - * Brings control to top. - */ - private void show(Control control) { - // no extension is available - if (editorComposite == null) { - return; - } - - editorLayout.topControl = control; - if (editorComposite.getParent().getLayout() instanceof FillWidthLayout) { - ((FillWidthLayout) editorComposite.getParent().getLayout()).flush(); - } - editorComposite.layout(); - control.setFocus(); + public boolean hasPreview() { + return editor.hasPreview(); } - public void showDefault() { - show(getDefaultViewer()); + public boolean isSpellCheckingEnabled() { + return editor.isSpellCheckingEnabled(); } - public void showPreview() { - if (!isReadOnly()) { - show(getPreviewViewer()); - } + public void setMode(Mode mode) { + Assert.isNotNull(mode); + this.mode = mode; } - public void showEditor() { - if (getEditorViewer() != null) { - show(getEditorViewer()); - } else { - show(getDefaultViewer()); - } + public void setRenderingEngine(AbstractRenderingEngine renderingEngine) { + editor.setRenderingEngine(renderingEngine); } - private void unsetContext() { - if (contextService == null) { - return; - } - if (contextActivation != null) { - contextService.deactivateContext(contextActivation); - contextActivation = null; - } + public void setSpellCheckingEnabled(boolean spellCheckingEnabled) { + editor.setSpellCheckingEnabled(spellCheckingEnabled); } - public boolean hasPreview() { - return extension != null && !isReadOnly(); + public void setValue(String value) { + getAttributeMapper().setValue(getTaskAttribute(), value); + attributeChanged(); } - public boolean hasBrowser() { - return renderingEngine != null; + public void showBrowser() { + editor.showBrowser(); } - private BrowserPreviewViewer getBrowserViewer() { - if (editorComposite == null || renderingEngine == null) { - return null; - } - - if (browserViewer == null) { - browserViewer = new BrowserPreviewViewer(getModel().getTaskRepository(), renderingEngine); - browserViewer.createControl(editorComposite, toolkit); - } - return browserViewer; + public void showDefault() { + editor.showDefault(); } - public AbstractRenderingEngine getRenderingEngine() { - return renderingEngine; + public void showEditor() { + editor.showEditor(); } - public void setRenderingEngine(AbstractRenderingEngine renderingEngine) { - this.renderingEngine = renderingEngine; + public void showPreview() { + editor.showPreview(); } - public void showBrowser() { - BrowserPreviewViewer viewer = getBrowserViewer(); - viewer.update(getValue()); - if (viewer != null) { - show(viewer.getControl()); + @Override + public void setReadOnly(boolean readOnly) { + super.setReadOnly(readOnly); + if (editor != null) { + editor.setReadOnly(readOnly); } } - public IAction getViewSourceAction() { - return viewSourceAction; + @Override + public void refresh() { + try { + ignoreNotification = true; + editor.setText(getValue()); + } finally { + ignoreNotification = false; + } } } Index: src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextEditor.java =================================================================== RCS file: src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextEditor.java diff -N src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextEditor.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/mylyn/internal/tasks/ui/editors/RichTextEditor.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,557 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 Tasktop Technologies 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: + * Tasktop Technologies - initial API and implementation + * Raphael Ackermann - spell checking support on bug 195514 + * Jingwen Ou - extensibility improvements + * David Green - fix for bug 256702 + *******************************************************************************/ + +package org.eclipse.mylyn.internal.tasks.ui.editors; + +import java.util.Iterator; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.ITextListener; +import org.eclipse.jface.text.TextEvent; +import org.eclipse.jface.text.source.AnnotationModel; +import org.eclipse.jface.text.source.IAnnotationAccess; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonFormUtil; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonTextSupport; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonThemes; +import org.eclipse.mylyn.internal.tasks.ui.commands.ViewSourceHandler; +import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTextViewerConfiguration.Mode; +import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal; +import org.eclipse.mylyn.tasks.core.TaskRepository; +import org.eclipse.mylyn.tasks.ui.editors.AbstractRenderingEngine; +import org.eclipse.mylyn.tasks.ui.editors.AbstractTaskEditorExtension; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.texteditor.AnnotationPreference; +import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; +import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; +import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; +import org.eclipse.ui.themes.IThemeManager; + +/** + * A text attribute editor that can switch between a editor, preview and source view. + * + * @author Raphael Ackermann + * @author Steffen Pingel + * @author Jingwen Ou + */ +public class RichTextEditor { + + public class ViewSourceAction extends Action { + + public ViewSourceAction() { + super(Messages.RichTextAttributeEditor_Viewer_Source, SWT.TOGGLE); + setChecked(false); + setEnabled(false); + } + + @Override + public void run() { + if (isChecked()) { + showDefault(); + } else { + showEditor(); + } + if (editorLayout != null) { + EditorUtil.reflow(editorLayout.topControl); + } + ViewSourceHandler.setChecked(isChecked()); + } + + } + + private BrowserPreviewViewer browserViewer; + + private IContextActivation contextActivation; + + private final IContextService contextService; + + private Control control; + + private SourceViewer defaultViewer; + + private final Document document; + + private Composite editorComposite; + + private StackLayout editorLayout; + + private SourceViewer editorViewer; + + private final AbstractTaskEditorExtension extension; + + private Mode mode; + + private SourceViewer previewViewer; + + boolean readOnly; + + private AbstractRenderingEngine renderingEngine; + + private final TaskRepository repository; + + private boolean spellCheckingEnabled; + + private final int style; + + private FormToolkit toolkit; + + private final IAction viewSourceAction; + + private String text; + + public RichTextEditor(TaskRepository repository, int style) { + this(repository, style, null, null); + } + + public RichTextEditor(TaskRepository repository, int style, IContextService contextService, + AbstractTaskEditorExtension extension) { + this.repository = repository; + this.style = style; + this.contextService = contextService; + this.extension = extension; + this.text = ""; //$NON-NLS-1$ + this.document = new Document(); + this.viewSourceAction = new ViewSourceAction(); + setMode(Mode.DEFAULT); + } + + private SourceViewer configure(final SourceViewer viewer, Document document, boolean readOnly) { + // do this before setting the document to not require invalidating the presentation + installHyperlinkPresenter(viewer); + + if (readOnly) { + viewer.setDocument(document); + if (extension != null) { + // setting view source action + viewer.getControl().setData(ViewSourceHandler.VIEW_SOURCE_ACTION, viewSourceAction); + viewer.getControl().addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + ViewSourceHandler.setChecked(getViewer() == defaultViewer); + } + }); + } + } else { + configureAsEditor(viewer, document); + installListeners(viewer); + viewer.getControl().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); + } + + // enable cut/copy/paste + CommonTextSupport.setTextViewer(viewer.getTextWidget(), viewer); + viewer.setEditable(!readOnly); + viewer.getTextWidget().setFont(getFont()); + if (toolkit != null) { + toolkit.adapt(viewer.getControl(), false, false); + } + + return viewer; + } + + /** Configures annotation model for spell checking. */ + private void configureAsEditor(SourceViewer viewer, Document document) { + AnnotationModel annotationModel = new AnnotationModel(); + viewer.showAnnotations(false); + viewer.showAnnotationsOverview(false); + IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess(); + final SourceViewerDecorationSupport support = new SourceViewerDecorationSupport(viewer, null, annotationAccess, + EditorsUI.getSharedTextColors()); + Iterator e = new MarkerAnnotationPreferences().getAnnotationPreferences().iterator(); + while (e.hasNext()) { + support.setAnnotationPreference((AnnotationPreference) e.next()); + } + support.install(EditorsUI.getPreferenceStore()); + viewer.getTextWidget().addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + support.uninstall(); + } + }); + //viewer.getTextWidget().setIndent(2); + viewer.setDocument(document, annotationModel); + } + + public void createControl(Composite parent, FormToolkit toolkit) { + this.toolkit = toolkit; + + int style = this.style; + if (!isReadOnly() && (style & TasksUiInternal.SWT_NO_SCROLL) == 0) { + style |= SWT.V_SCROLL; + } + + if (extension != null || renderingEngine != null) { + editorComposite = new Composite(parent, SWT.NULL); + editorLayout = new StackLayout() { + @Override + protected Point computeSize(Composite composite, int hint, int hint2, boolean flushCache) { + return topControl.computeSize(hint, hint2, flushCache); + } + }; + editorComposite.setLayout(editorLayout); + setControl(editorComposite); + + if (extension != null) { + if (isReadOnly()) { + editorViewer = extension.createViewer(repository, editorComposite, style); + } else { + editorViewer = extension.createEditor(repository, editorComposite, style); + editorViewer.getTextWidget().addFocusListener(new FocusListener() { + public void focusGained(FocusEvent e) { + setContext(); + } + + public void focusLost(FocusEvent e) { + unsetContext(); + } + }); + editorViewer.getTextWidget().addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + unsetContext(); + } + }); + } + configure(editorViewer, document, isReadOnly()); + show(editorViewer); + } else { + defaultViewer = createDefaultEditor(editorComposite, style); + configure(defaultViewer, document, isReadOnly()); + show(defaultViewer); + } + + if (!isReadOnly() && (style & TasksUiInternal.SWT_NO_SCROLL) == 0) { + editorComposite.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); + } + + viewSourceAction.setEnabled(true); + } else { + defaultViewer = createDefaultEditor(parent, style); + configure(defaultViewer, document, isReadOnly()); + setControl(defaultViewer.getControl()); + + viewSourceAction.setEnabled(false); + } + } + + private SourceViewer createDefaultEditor(Composite parent, int styles) { + SourceViewer defaultEditor = new SourceViewer(parent, null, styles | SWT.WRAP); + + RepositoryTextViewerConfiguration viewerConfig = new RepositoryTextViewerConfiguration(repository, + isSpellCheckingEnabled() && !isReadOnly()); + viewerConfig.setMode(getMode()); + defaultEditor.configure(viewerConfig); + + return defaultEditor; + } + + private BrowserPreviewViewer getBrowserViewer() { + if (editorComposite == null || renderingEngine == null) { + return null; + } + + if (browserViewer == null) { + browserViewer = new BrowserPreviewViewer(getRepository(), renderingEngine); + browserViewer.createControl(editorComposite, toolkit); + } + return browserViewer; + } + + public Control getControl() { + return control; + } + + public SourceViewer getDefaultViewer() { + if (defaultViewer == null) { + defaultViewer = createDefaultEditor(editorComposite, style); + configure(defaultViewer, document, isReadOnly()); + + // fixed font size + defaultViewer.getTextWidget().setFont(JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT)); + // adapt maximize action + defaultViewer.getControl().setData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION, + editorViewer.getControl().getData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION)); + // adapt menu to the new viewer + installMenu(defaultViewer.getControl(), editorViewer.getControl().getMenu()); + } + return defaultViewer; + } + + public SourceViewer getEditorViewer() { + return editorViewer; + } + + private Font getFont() { + IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); + Font font = themeManager.getCurrentTheme().getFontRegistry().get(CommonThemes.FONT_EDITOR_COMMENT); + return font; + } + + public Mode getMode() { + return mode; + } + + private SourceViewer getPreviewViewer() { + if (extension == null) { + return null; + } + + // construct as needed + if (previewViewer == null) { + // previewer should always have a vertical scroll bar if it's editable + int previewViewerStyle = style; + if (getEditorViewer() != null) { + previewViewerStyle |= SWT.V_SCROLL; + } + previewViewer = extension.createViewer(repository, editorComposite, previewViewerStyle); + configure(previewViewer, new Document(editorViewer.getDocument().get()), true); + // adapt maximize action + previewViewer.getControl().setData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION, + editorViewer.getControl().getData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION)); + } else { + // update content + previewViewer.setDocument(new Document(editorViewer.getDocument().get())); + } + return previewViewer; + } + + public AbstractRenderingEngine getRenderingEngine() { + return renderingEngine; + } + + public TaskRepository getRepository() { + return repository; + } + + public String getText() { + return this.text; + } + + public SourceViewer getViewer() { + if (editorLayout == null) { + return defaultViewer; + } + if (defaultViewer != null && editorLayout.topControl == defaultViewer.getControl()) { + return defaultViewer; + } else if (previewViewer != null && editorLayout.topControl == previewViewer.getControl()) { + return previewViewer; + } else { + return editorViewer; + } + } + + public IAction getViewSourceAction() { + return viewSourceAction; + } + + public boolean hasBrowser() { + return renderingEngine != null; + } + + public boolean hasPreview() { + return extension != null && !isReadOnly(); + } + + private RepositoryTextViewerConfiguration installHyperlinkPresenter(SourceViewer viewer) { + RepositoryTextViewerConfiguration configuration = new RepositoryTextViewerConfiguration(repository, false); + configuration.setMode(getMode()); + + // do not configure viewer, this has already been done in extension + + AbstractHyperlinkTextPresentationManager manager; + if (getMode() == Mode.DEFAULT) { + manager = new HighlightingHyperlinkTextPresentationManager(); + manager.setHyperlinkDetectors(configuration.getDefaultHyperlinkDetectors(viewer, null)); + manager.install(viewer); + + manager = new TaskHyperlinkTextPresentationManager(); + manager.setHyperlinkDetectors(configuration.getDefaultHyperlinkDetectors(viewer, Mode.TASK)); + manager.install(viewer); + } else if (getMode() == Mode.TASK_RELATION) { + manager = new TaskHyperlinkTextPresentationManager(); + manager.setHyperlinkDetectors(configuration.getDefaultHyperlinkDetectors(viewer, Mode.TASK_RELATION)); + manager.install(viewer); + } + + return configuration; + } + + private void installListeners(final SourceViewer viewer) { + viewer.addTextListener(new ITextListener() { + public void textChanged(TextEvent event) { + // filter out events caused by text presentation changes, e.g. annotation drawing + String value = viewer.getTextWidget().getText(); + if (!RichTextEditor.this.text.equals(value)) { + RichTextEditor.this.text = value; + valueChanged(value); + CommonFormUtil.ensureVisible(viewer.getTextWidget()); + } + } + }); + // ensure that tab traverses to next control instead of inserting a tab character unless editing multi-line text + if ((style & SWT.MULTI) != 0 && mode != Mode.DEFAULT) { + viewer.getTextWidget().addListener(SWT.Traverse, new Listener() { + public void handleEvent(Event event) { + switch (event.detail) { + case SWT.TRAVERSE_TAB_NEXT: + case SWT.TRAVERSE_TAB_PREVIOUS: + event.doit = true; + break; + } + } + }); + } + } + + private void installMenu(final Control control, Menu menu) { + if (menu != null) { + control.setMenu(menu); + control.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + control.setMenu(null); + } + }); + } + } + + public boolean isReadOnly() { + return readOnly; + } + + public boolean isSpellCheckingEnabled() { + return spellCheckingEnabled; + } + + private void setContext() { + if (contextService == null) { + return; + } + if (contextActivation != null) { + contextService.deactivateContext(contextActivation); + contextActivation = null; + } + if (contextService != null && extension.getEditorContextId() != null) { + contextActivation = contextService.activateContext(extension.getEditorContextId()); + } + } + + private void setControl(Control control) { + this.control = control; + } + + public void setMode(Mode mode) { + Assert.isNotNull(mode); + this.mode = mode; + } + + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + public void setRenderingEngine(AbstractRenderingEngine renderingEngine) { + this.renderingEngine = renderingEngine; + } + + public void setSpellCheckingEnabled(boolean spellCheckingEnabled) { + this.spellCheckingEnabled = spellCheckingEnabled; + } + + public void setText(String value) { + this.text = value; + document.set(value); + } + + /** + * Brings control to top. + */ + private void show(Control control) { + // no extension is available + if (editorComposite == null) { + return; + } + + editorLayout.topControl = control; + if (editorComposite.getParent().getLayout() instanceof FillWidthLayout) { + ((FillWidthLayout) editorComposite.getParent().getLayout()).flush(); + } + editorComposite.layout(); + control.setFocus(); + } + + /** + * Brings viewer to top. + */ + private void show(SourceViewer viewer) { + show(viewer.getControl()); + } + + public void showBrowser() { + BrowserPreviewViewer viewer = getBrowserViewer(); + viewer.update(getText()); + if (viewer != null) { + show(viewer.getControl()); + } + } + + public void showDefault() { + show(getDefaultViewer()); + } + + public void showEditor() { + if (getEditorViewer() != null) { + show(getEditorViewer()); + } else { + show(getDefaultViewer()); + } + } + + public void showPreview() { + if (!isReadOnly()) { + show(getPreviewViewer()); + } + } + + private void unsetContext() { + if (contextService == null) { + return; + } + if (contextActivation != null) { + contextService.deactivateContext(contextActivation); + contextActivation = null; + } + } + + protected void valueChanged(String value) { + } + +}