NEW - bug 241167: reimplement ExtensibleRichTextAttributeEditor to supports switching between RepositoryTextViewer and MarkViewer(WikiText)
https://bugs.eclipse.org/bugs/show_bug.cgi?id=241167
diff --git a/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleRichTextAttributeEditor.java b/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleRichTextAttributeEditor.java
index 9454cd2..378f3e9 100644
--- a/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleRichTextAttributeEditor.java
+++ b/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleRichTextAttributeEditor.java
@@ -8,60 +8,71 @@
 
 package org.eclipse.mylyn.internal.sandbox.ui.editors;
 
-import org.eclipse.jface.layout.GridDataFactory;
+import java.util.Iterator;
+
 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.jface.viewers.Viewer;
 import org.eclipse.mylyn.internal.provisional.commons.ui.CommonThemes;
 import org.eclipse.mylyn.internal.tasks.ui.editors.EditorUtil;
+import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTextViewer;
+import org.eclipse.mylyn.internal.tasks.ui.editors.RepositoryTextViewerConfiguration;
 import org.eclipse.mylyn.internal.tasks.ui.editors.RichTextAttributeEditor;
+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;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.CTabFolder;
-import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.custom.StackLayout;
 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.Font;
-import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 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 multi-tab source viewer that can edit and preview markup.
+ * A text attribute that can switch between a source editor and a preview using StackLayout.
  * 
  * @author Jingwen Ou
- * @author David Green added context activation, layout
+ * @author Steffen Pingel
  */
-// TODO generalize RichTextAttributeEditor: add a protected method to create the SourceViewer then subclasses can then
-// create its own SourceViewer without overriding createControl and copying the code in it
 public class ExtensibleRichTextAttributeEditor extends RichTextAttributeEditor {
 
-	private SourceViewer source;
-
-	private final TaskRepository taskRepository;
-
-	private final int styles;
-
-	private final IContextService contextService;
-
 	private IContextActivation contextActivation;
 
+	private final IContextService contextService;
+
+	private SourceViewer defaultViewer;
+
+	private Composite editorComposite;
+
+	private StackLayout editorLayout;
+
 	private final AbstractTaskEditorExtension extension;
 
-	private CTabFolder folder;
+	private SourceViewer editorViewer;
 
-	private SourceViewer preview;
+	private SourceViewer previewViewer;
+
+	private final int styles;
+
+	private final TaskRepository taskRepository;
+
+	private FormToolkit toolkit;
 
 	public ExtensibleRichTextAttributeEditor(IContextService contextService, TaskDataModel manager,
 			TaskRepository taskRepository, AbstractTaskEditorExtension extension, TaskAttribute taskAttribute,
@@ -73,45 +84,79 @@
 		this.styles = styles;
 	}
 
-	@Override
-	public void createControl(Composite parent, FormToolkit toolkit) {
-		if (isReadOnly()) {
-			source = extension.createViewer(taskRepository, parent, styles);
-			source.setDocument(new Document(getValue()));
-			setControl(source.getControl());
-		} else {
-			folder = new CTabFolder(parent, SWT.FLAT | SWT.BOTTOM);
-			folder.setLayout(new GridLayout());
-			// invisible tab text
-			folder.setTabHeight(0);
-
-			// editor
-			CTabItem viewerItem = new CTabItem(folder, SWT.NONE);
-			viewerItem.setText("Source");
-			viewerItem.setToolTipText("Edit Source");
-
-			source = extension.createEditor(taskRepository, folder, styles | SWT.V_SCROLL);
-			source.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
-
-			// enable cut/copy/paste
-			EditorUtil.setTextViewer(source.getTextWidget(), source);
-
-			if (source.getDocument() == null) {
-				source.setDocument(new Document(getValue()), new AnnotationModel());
+	/** Configures annontation model for spell checking. */
+	private void configureAsTextEditor(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();
 			}
-			source.addTextListener(new ITextListener() {
+		});
+		viewer.getTextWidget().setIndent(2);
+		viewer.setDocument(document, annotationModel);
+	}
+
+	private SourceViewer configureEditor(final SourceViewer viewer, boolean readOnly) {
+		Document document = new Document(getValue());
+		if (readOnly) {
+			viewer.setDocument(document);
+		} else {
+			configureAsTextEditor(viewer, document);
+			viewer.addTextListener(new ITextListener() {
 				public void textChanged(TextEvent event) {
 					// filter out events caused by text presentation changes, e.g. annotation drawing
-					String value = source.getTextWidget().getText();
+					String value = viewer.getTextWidget().getText();
 					if (!getValue().equals(value)) {
 						setValue(value);
+						EditorUtil.ensureVisible(viewer.getTextWidget());
 					}
 				}
 			});
-			source.getControl().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
 
-			FocusListener focusListener = new FocusListener() {
+			viewer.getControl().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER);
 
+			// drop & drag support, under review
+			// TaskEditorDropTarget.addDropTargetSupport(viewer);
+		}
+
+		// enable cut/copy/paste
+		EditorUtil.setTextViewer(viewer.getTextWidget(), viewer);
+		viewer.setEditable(!readOnly);
+		viewer.getTextWidget().setFont(getFont());
+		toolkit.adapt(viewer.getControl(), false, false);
+
+		return viewer;
+	}
+
+	@Override
+	public void createControl(Composite parent, FormToolkit toolkit) {
+		this.toolkit = toolkit;
+
+		editorComposite = new Composite(parent, SWT.NULL);
+		editorLayout = new StackLayout();
+		editorComposite.setLayout(editorLayout);
+		setControl(editorComposite);
+
+		int styles = this.styles;
+		if (!isReadOnly() && (styles & TasksUiInternal.SWT_NO_SCROLL) == 0) {
+			styles |= SWT.V_SCROLL;
+		}
+
+		if (isReadOnly()) {
+			editorViewer = extension.createViewer(taskRepository, editorComposite, styles);
+		} else {
+			editorViewer = extension.createEditor(taskRepository, editorComposite, styles);
+			editorViewer.getTextWidget().addFocusListener(new FocusListener() {
 				public void focusGained(FocusEvent e) {
 					setContext();
 				}
@@ -119,35 +164,38 @@
 				public void focusLost(FocusEvent e) {
 					unsetContext();
 				}
-			};
-			source.getTextWidget().addFocusListener(focusListener);
-			DisposeListener disposeListener = new DisposeListener() {
+			});
+			editorViewer.getTextWidget().addDisposeListener(new DisposeListener() {
 				public void widgetDisposed(DisposeEvent e) {
 					unsetContext();
 				}
-			};
-			source.getTextWidget().addDisposeListener(disposeListener);
-
-			viewerItem.setControl(source instanceof Viewer ? ((Viewer) source).getControl() : source.getTextWidget());
-			folder.setSelection(viewerItem);
-
-			// preview
-			CTabItem previewItem = new CTabItem(folder, SWT.NONE);
-			previewItem.setText("Preview");
-			previewItem.setToolTipText("Preview Source");
-
-			preview = extension.createViewer(taskRepository, folder, styles | SWT.V_SCROLL);
-
-			previewItem.setControl(preview instanceof Viewer ? ((Viewer) preview).getControl()
-					: preview.getTextWidget());
-
-			preview.getTextWidget().setFont(getFont());
-
-			setControl(folder);
+			});
 		}
+		configureEditor(editorViewer, isReadOnly());
 
-		source.getTextWidget().setFont(getFont());
-		toolkit.adapt(source.getControl(), false, false);
+		show(editorViewer);
+	}
+
+	private SourceViewer createDefaultEditor(Composite parent, int styles) {
+		SourceViewer defaultEditor = new RepositoryTextViewer(taskRepository, parent, styles);
+
+		RepositoryTextViewerConfiguration viewerConfig = new RepositoryTextViewerConfiguration(taskRepository,
+				isSpellCheckingEnabled());
+		viewerConfig.setMode(getMode());
+		defaultEditor.configure(viewerConfig);
+
+		return defaultEditor;
+	}
+
+	private SourceViewer getDefaultViewer() {
+		if (defaultViewer == null) {
+			defaultViewer = createDefaultEditor(editorComposite, styles);
+			configureEditor(defaultViewer, isReadOnly());
+			// adapt maximize action
+			defaultViewer.getControl().setData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION,
+					editorViewer.getControl().getData(EditorUtil.KEY_TOGGLE_TO_MAXIMIZE_ACTION));
+		}
+		return defaultViewer;
 	}
 
 	private Font getFont() {
@@ -156,12 +204,36 @@
 		return font;
 	}
 
-	@Override
-	public SourceViewer getViewer() {
-		return source;
+	private SourceViewer getPreviewViewer() {
+		// construct as needed
+		if (previewViewer == null) {
+			previewViewer = extension.createViewer(taskRepository, editorComposite, styles);
+			configureEditor(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;
 	}
 
-	protected void setContext() {
+	public SourceViewer getEditorViewer() {
+		return editorViewer;
+	}
+
+	@Override
+	public SourceViewer getViewer() {
+		if (defaultViewer != null && editorLayout.topControl == defaultViewer.getControl()) {
+			return defaultViewer;
+		} else if (previewViewer != null && editorLayout.topControl == previewViewer.getControl()) {
+			return previewViewer;
+		} else {
+			return editorViewer;
+		}
+	}
+
+	private void setContext() {
 		if (contextActivation != null) {
 			contextService.deactivateContext(contextActivation);
 			contextActivation = null;
@@ -171,19 +243,30 @@
 		}
 	}
 
-	public void toggleEditing(boolean editing) {
-		if (!isReadOnly() && folder != null) {
-			if (!editing) {
-				folder.setSelection(1);
-				Document document = new Document(source.getDocument().get());
-				preview.setDocument(document);
-			} else {
-				folder.setSelection(0);
-			}
+	/**
+	 * Brings <code>viewer</code> to top.
+	 */
+	private void show(SourceViewer viewer) {
+		editorLayout.topControl = viewer.getControl();
+		editorComposite.layout();
+		viewer.getControl().setFocus();
+	}
+
+	public void showDefault() {
+		show(getDefaultViewer());
+	}
+
+	public void showPreview() {
+		if (!isReadOnly()) {
+			show(getPreviewViewer());
 		}
 	}
 
-	protected void unsetContext() {
+	public void showEditor() {
+		show(getEditorViewer());
+	}
+
+	private void unsetContext() {
 		if (contextActivation != null) {
 			contextService.deactivateContext(contextActivation);
 			contextActivation = null;
diff --git a/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleTaskEditorNewCommentPart.java b/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleTaskEditorNewCommentPart.java
index 1981c68..3dbe38a 100644
--- a/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleTaskEditorNewCommentPart.java
+++ b/org.eclipse.mylyn.sandbox.ui/src/org/eclipse/mylyn/internal/sandbox/ui/editors/ExtensibleTaskEditorNewCommentPart.java
@@ -43,7 +43,11 @@
 	private void toggleEditing(Action action) {
 		if (getEditor() instanceof ExtensibleRichTextAttributeEditor) {
 			ExtensibleRichTextAttributeEditor editor = (ExtensibleRichTextAttributeEditor) getEditor();
-			editor.toggleEditing(!action.isChecked());
+			if (action.isChecked()) {
+				editor.showPreview();
+			} else {
+				editor.showEditor();
+			}
 		}
 	}