From 7dcd391057bf21fff063764a396927ec4e408222 Mon Sep 17 00:00:00 2001
From: Daniel Grunwald <daniel@danielgrunwald.de>
Date: Tue, 19 Jan 2010 21:02:53 +0000
Subject: [PATCH] SD2-1607 - Add printing support to AvalonEdit

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5419 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
---
 .../Test/Utils/MockViewContent.cs             |   6 +
 .../WixBinding/Test/Utils/MockViewContent.cs  |   6 +
 .../AvalonEdit.AddIn/AvalonEdit.AddIn.csproj  |   8 ++
 .../Src/AvalonEditViewContent.cs              |   4 +
 .../AvalonEdit.AddIn/Src/CodeEditorView.cs    |  29 +++++
 .../AvalonEdit.AddIn/Src/DocumentPrinter.cs   |  67 +++++++++++
 .../Src/PrintPreviewViewContent.cs            | 105 ++++++++++++++++++
 .../XmlEditor/Test/Utils/MockViewContent.cs   |   6 +
 .../Project/Gui/SearchResultNode.cs           |   7 +-
 .../Editing/EditingCommandHandler.cs          |   2 +-
 .../Editing/Selection.cs                      |   2 +-
 .../Highlighting/DocumentHighlighter.cs       |  21 ++--
 .../Highlighting/HighlightingColorizer.cs     |   5 +
 .../Highlighting/HtmlClipboard.cs             |  10 +-
 .../Highlighting/IHighlighter.cs              |  39 +++++++
 .../ICSharpCode.AvalonEdit.csproj             |   1 +
 .../AvalonEditSyntaxHighlighterAdapter.cs     |   2 +-
 .../Project/Src/Gui/AbstractViewContent.cs    |   4 +
 src/Main/Base/Project/Src/Gui/IViewContent.cs |   9 +-
 .../Layouts/AvalonWorkbenchWindow.cs          |   3 +
 20 files changed, 315 insertions(+), 21 deletions(-)
 create mode 100644 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs
 create mode 100644 src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/PrintPreviewViewContent.cs
 create mode 100644 src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs

diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockViewContent.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockViewContent.cs
index 6a9b00caa0..1cf5b6b1d4 100644
--- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockViewContent.cs
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockViewContent.cs
@@ -63,6 +63,12 @@ namespace PythonBinding.Tests.Utils
 			}
 		}
 		
+		public object InitiallyFocusedControl {
+			get {
+				throw new NotImplementedException();
+			}
+		}
+		
 		public IWorkbenchWindow WorkbenchWindow {
 			get {
 				throw new NotImplementedException();
diff --git a/src/AddIns/BackendBindings/WixBinding/Test/Utils/MockViewContent.cs b/src/AddIns/BackendBindings/WixBinding/Test/Utils/MockViewContent.cs
index 32b47e58a7..66e3688be2 100644
--- a/src/AddIns/BackendBindings/WixBinding/Test/Utils/MockViewContent.cs
+++ b/src/AddIns/BackendBindings/WixBinding/Test/Utils/MockViewContent.cs
@@ -106,6 +106,12 @@ namespace WixBinding.Tests.Utils
 			}
 		}
 		
+		public object InitiallyFocusedControl {
+			get {
+				throw new NotImplementedException();
+			}
+		}
+		
 		public IWorkbenchWindow WorkbenchWindow {
 			get {
 				throw new NotImplementedException();
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
index fbbe1199b3..ee2c29cb19 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
@@ -47,11 +47,17 @@
     <Reference Include="PresentationFramework">
       <RequiredTargetFramework>3.0</RequiredTargetFramework>
     </Reference>
+    <Reference Include="ReachFramework">
+      <RequiredTargetFramework>3.0</RequiredTargetFramework>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core">
       <RequiredTargetFramework>3.5</RequiredTargetFramework>
     </Reference>
     <Reference Include="System.Drawing" />
+    <Reference Include="System.Printing">
+      <RequiredTargetFramework>3.0</RequiredTargetFramework>
+    </Reference>
     <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml" />
     <Reference Include="System.Xaml" />
@@ -86,6 +92,7 @@
     <Compile Include="Src\Commands\SortSelectionCommand.cs" />
     <Compile Include="Src\Commands\SurroundWithCommand.cs" />
     <Compile Include="Src\CustomCommands.cs" />
+    <Compile Include="Src\DocumentPrinter.cs" />
     <Compile Include="Src\IncrementalSearch.cs" />
     <Compile Include="Src\InlineUIElementGenerator.cs" />
     <Compile Include="Src\IconBarManager.cs" />
@@ -103,6 +110,7 @@
       <DependentUpon>TextViewOptions.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Src\PrintPreviewViewContent.cs" />
     <Compile Include="Src\Snippets\CodeSnippet.cs" />
     <Compile Include="Src\Snippets\CodeSnippetCompletionWindow.cs" />
     <Compile Include="Src\Snippets\CodeSnippetGroup.cs" />
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs
index 7e5bac0f76..7c971fc5b2 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs
@@ -62,6 +62,10 @@ namespace ICSharpCode.AvalonEdit.AddIn
 			get { return codeEditor; }
 		}
 		
+		public override object InitiallyFocusedControl {
+			get { return codeEditor.PrimaryTextEditor.TextArea; }
+		}
+		
 		public override void Save(OpenedFile file, Stream stream)
 		{
 			if (file != PrimaryFile)
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
index 32d9150900..cd609dfc99 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
@@ -14,6 +14,7 @@ using System.Linq;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls.Primitives;
+using System.Windows.Documents;
 using System.Windows.Input;
 
 using ICSharpCode.AvalonEdit.AddIn.Options;
@@ -43,6 +44,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
 		public CodeEditorView()
 		{
 			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, OnHelpExecuted));
+			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Print, OnPrint));
+			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.PrintPreview, OnPrintPreview));
+			
 			options = ICSharpCode.AvalonEdit.AddIn.Options.CodeEditorOptions.Instance;
 			options.BindToTextEditor(this);
 			
@@ -365,5 +369,30 @@ namespace ICSharpCode.AvalonEdit.AddIn
 				folding.UpdateFoldings(parseInfo);
 			}
 		}
+		
+		void OnPrint(object sender, ExecutedRoutedEventArgs e)
+		{
+			PrintDialog printDialog = PrintPreviewViewContent.PrintDialog;
+			if (printDialog.ShowDialog() == true) {
+				FlowDocument fd = DocumentPrinter.CreateFlowDocumentForEditor(this);
+				fd.ColumnGap = 0;
+				fd.ColumnWidth = printDialog.PrintableAreaWidth;
+				fd.PageHeight = printDialog.PrintableAreaHeight;
+				fd.PageWidth = printDialog.PrintableAreaWidth;
+				IDocumentPaginatorSource doc = fd;
+				printDialog.PrintDocument(doc.DocumentPaginator, Path.GetFileName(this.Adapter.FileName));
+			}
+		}
+		
+		void OnPrintPreview(object sender, ExecutedRoutedEventArgs e)
+		{
+			PrintDialog printDialog = PrintPreviewViewContent.PrintDialog;
+			FlowDocument fd = DocumentPrinter.CreateFlowDocumentForEditor(this);
+			fd.ColumnGap = 0;
+			fd.ColumnWidth = printDialog.PrintableAreaWidth;
+			fd.PageHeight = printDialog.PrintableAreaHeight;
+			fd.PageWidth = printDialog.PrintableAreaWidth;
+			PrintPreviewViewContent.ShowDocument(fd, Path.GetFileName(this.Adapter.FileName));
+		}
 	}
 }
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs
new file mode 100644
index 0000000000..50e8548d38
--- /dev/null
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs
@@ -0,0 +1,67 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Daniel Grunwald"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.IO;
+using System.IO.Packaging;
+using System.Printing;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Xps;
+using System.Windows.Xps.Packaging;
+
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Highlighting;
+
+namespace ICSharpCode.AvalonEdit.AddIn
+{
+	/// <summary>
+	/// Helps printing documents.
+	/// </summary>
+	public static class DocumentPrinter
+	{
+		public static Block ConvertTextDocumentToBlock(TextDocument document, IHighlighter highlighter)
+		{
+			if (document == null)
+				throw new ArgumentNullException("document");
+//			Table table = new Table();
+//			table.Columns.Add(new TableColumn { Width = GridLength.Auto });
+//			table.Columns.Add(new TableColumn { Width = new GridLength(1, GridUnitType.Star) });
+//			TableRowGroup trg = new TableRowGroup();
+//			table.RowGroups.Add(trg);
+			Paragraph p = new Paragraph();
+			foreach (DocumentLine line in document.Lines) {
+				int lineNumber = line.LineNumber;
+//				TableRow row = new TableRow();
+//				trg.Rows.Add(row);
+//				row.Cells.Add(new TableCell(new Paragraph(new Run(lineNumber.ToString()))) { TextAlignment = TextAlignment.Right });
+				HighlightedInlineBuilder inlineBuilder = new HighlightedInlineBuilder(document.GetText(line));
+				if (highlighter != null) {
+					HighlightedLine highlightedLine = highlighter.HighlightLine(lineNumber);
+					int lineStartOffset = line.Offset;
+					foreach (HighlightedSection section in highlightedLine.Sections)
+						inlineBuilder.SetHighlighting(section.Offset - lineStartOffset, section.Length, section.Color);
+				}
+//				Paragraph p = new Paragraph();
+//				row.Cells.Add(new TableCell(p));
+				p.Inlines.AddRange(inlineBuilder.CreateRuns());
+				p.Inlines.Add(new LineBreak());
+			}
+			return p;
+		}
+		
+		public static FlowDocument CreateFlowDocumentForEditor(CodeEditorView editor)
+		{
+			IHighlighter highlighter = editor.Adapter.GetService(typeof(IHighlighter)) as IHighlighter;
+			FlowDocument doc = new FlowDocument(ConvertTextDocumentToBlock(editor.Document, highlighter));
+			doc.FontFamily = editor.FontFamily;
+			doc.FontSize = editor.FontSize;
+			return doc;
+		}
+	}
+}
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/PrintPreviewViewContent.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/PrintPreviewViewContent.cs
new file mode 100644
index 0000000000..1f337d8e63
--- /dev/null
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/PrintPreviewViewContent.cs
@@ -0,0 +1,105 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Daniel Grunwald"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using System.IO;
+using System.IO.Packaging;
+using System.Linq;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Xps;
+using System.Windows.Xps.Packaging;
+
+using ICSharpCode.SharpDevelop.Gui;
+
+namespace ICSharpCode.AvalonEdit.AddIn
+{
+	public class PrintPreviewViewContent : AbstractViewContent
+	{
+		static readonly PrintDialog printDialog = new PrintDialog();
+		
+		public static PrintDialog PrintDialog {
+			get { return printDialog; }
+		}
+		
+		Action cleanup;
+		DocumentViewer viewer = new DocumentViewer();
+		
+		public PrintPreviewViewContent()
+		{
+			SetLocalizedTitle("${res:Global.Preview}");
+			viewer.CommandBindings.Add(new CommandBinding(ApplicationCommands.Print, OnPrint));
+		}
+		
+		public IDocumentPaginatorSource Document {
+			get { return viewer.Document; }
+			set {
+				if (cleanup != null) {
+					cleanup();
+					cleanup = null;
+				}
+				
+				if (value is FlowDocument) {
+					// DocumentViewer does not support FlowDocument, so we'll convert it
+					MemoryStream xpsStream = new MemoryStream();
+					Package package = Package.Open(xpsStream, FileMode.Create, FileAccess.ReadWrite);
+					string packageUriString = "memorystream://data.xps";
+					PackageStore.AddPackage(new Uri(packageUriString), package);
+					
+					XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Normal, packageUriString);
+					XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
+					
+					writer.Write(value.DocumentPaginator);
+					viewer.Document = xpsDocument.GetFixedDocumentSequence();
+					cleanup = delegate {
+						viewer.Document = null;
+						xpsDocument.Close();
+						package.Close();
+						PackageStore.RemovePackage(new Uri(packageUriString));
+					};
+				} else {
+					viewer.Document = value;
+				}
+			}
+		}
+		
+		public override void Dispose()
+		{
+			base.Dispose();
+			if (cleanup != null) {
+				cleanup();
+				cleanup = null;
+			}
+		}
+		
+		public string Description { get; set; }
+		
+		public override object Control {
+			get { return viewer; }
+		}
+		
+		void OnPrint(object sender, ExecutedRoutedEventArgs e)
+		{
+			viewer.Print();
+		}
+		
+		public static void ShowDocument(IDocumentPaginatorSource document, string description)
+		{
+			PrintPreviewViewContent vc = WorkbenchSingleton.Workbench.ViewContentCollection.OfType<PrintPreviewViewContent>().FirstOrDefault();
+			if (vc != null) {
+				vc.WorkbenchWindow.SelectWindow();
+			} else {
+				vc = new PrintPreviewViewContent();
+				WorkbenchSingleton.Workbench.ShowView(vc);
+			}
+			vc.Document = document;
+			vc.Description = description;
+		}
+	}
+}
diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockViewContent.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockViewContent.cs
index feeecd6240..ecc56bbbbf 100644
--- a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockViewContent.cs
+++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockViewContent.cs
@@ -54,6 +54,12 @@ namespace XmlEditor.Tests.Utils
 			}
 		}
 		
+		public object InitiallyFocusedControl {
+			get {
+				throw new NotImplementedException();
+			}
+		}
+		
 		public IWorkbenchWindow WorkbenchWindow {
 			get {
 				throw new NotImplementedException();
diff --git a/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs b/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
index c616f627cf..261d407ec4 100644
--- a/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
+++ b/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
@@ -47,10 +47,9 @@ namespace SearchAndReplace
 				inlineBuilder = new HighlightedInlineBuilder(matchedLine.Text);
 				inlineBuilder.SetFontFamily(0, inlineBuilder.Text.Length, resultLineFamily);
 				
-				DocumentHighlighter highlighter = document.GetService(typeof(DocumentHighlighter)) as DocumentHighlighter;
-				TextDocument textDocument = document.GetService(typeof(TextDocument)) as TextDocument;
-				if (highlighter != null && textDocument != null) {
-					HighlightedLine highlightedLine = highlighter.HighlightLine(textDocument.GetLineByNumber(lineNumber));
+				IHighlighter highlighter = document.GetService(typeof(IHighlighter)) as IHighlighter;
+				if (highlighter != null) {
+					HighlightedLine highlightedLine = highlighter.HighlightLine(lineNumber);
 					int startOffset = highlightedLine.DocumentLine.Offset;
 					// copy only the foreground color
 					foreach (HighlightedSection section in highlightedLine.Sections) {
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
index 09e01cb239..39de3372d9 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/EditingCommandHandler.cs
@@ -325,7 +325,7 @@ namespace ICSharpCode.AvalonEdit.Editing
 			
 			// Also copy text in HTML format to clipboard - good for pasting text into Word
 			// or to the SharpDevelop forums.
-			DocumentHighlighter highlighter = textArea.GetService(typeof(DocumentHighlighter)) as DocumentHighlighter;
+			IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter;
 			HtmlClipboard.SetHtml(data, HtmlClipboard.CreateHtmlFragment(textArea.Document, highlighter, wholeLine, new HtmlOptions(textArea.Options)));
 			
 			MemoryStream lineSelected = new MemoryStream(1);
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs
index e925d9c32d..fe5cc6229a 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Selection.cs
@@ -127,7 +127,7 @@ namespace ICSharpCode.AvalonEdit.Editing
 				throw new ArgumentNullException("textArea");
 			if (options == null)
 				throw new ArgumentNullException("options");
-			DocumentHighlighter highlighter = textArea.GetService(typeof(DocumentHighlighter)) as DocumentHighlighter;
+			IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter;
 			StringBuilder html = new StringBuilder();
 			bool first = true;
 			foreach (ISegment selectedSegment in this.Segments) {
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs
index 1190d670fd..9140ac8fb1 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs
@@ -21,7 +21,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 	/// This class can syntax-highlight a document.
 	/// It automatically manages invalidating the highlighting when the document changes.
 	/// </summary>
-	public class DocumentHighlighter : ILineTracker
+	public class DocumentHighlighter : ILineTracker, IHighlighter
 	{
 		/// <summary>
 		/// Stores the span state at the end of each line.
@@ -123,13 +123,20 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 		{
 			if (!document.Lines.Contains(line))
 				throw new ArgumentException("The specified line does not belong to the document.");
+			return HighlightLine(line.LineNumber);
+		}
+		
+		/// <inheritdoc/>
+		public HighlightedLine HighlightLine(int lineNumber)
+		{
+			ThrowUtil.CheckInRangeInclusive(lineNumber, "lineNumber", 1, document.LineCount);
 			CheckIsHighlighting();
 			isHighlighting = true;
 			try {
-				int targetLineNumber = line.LineNumber;
-				HighlightUpTo(targetLineNumber);
+				HighlightUpTo(lineNumber);
+				DocumentLine line = document.GetLineByNumber(lineNumber);
 				highlightedLine = new HighlightedLine(document, line);
-				HighlightLineAndUpdateTreeList(line, targetLineNumber);
+				HighlightLineAndUpdateTreeList(line, lineNumber);
 				return highlightedLine;
 			} finally {
 				highlightedLine = null;
@@ -137,11 +144,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 			}
 		}
 		
-		/// <summary>
-		/// Gets the span stack at the end of the specified line.
-		/// -> GetSpanStack(1) returns the spans at the start of the second line.
-		/// </summary>
-		/// <remarks>GetSpanStack(0) is valid and will always return the empty stack.</remarks>
+		/// <inheritdoc/>
 		public SpanStack GetSpanStack(int lineNumber)
 		{
 			ThrowUtil.CheckInRangeInclusive(lineNumber, "lineNumber", 0, document.LineCount);
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs
index fb65870875..e12b3f2992 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs
@@ -62,6 +62,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 		{
 			if (highlighter != null && isInTextView) {
 				textView.Services.RemoveService(typeof(DocumentHighlighter));
+				textView.Services.RemoveService(typeof(IHighlighter));
 			}
 			
 			TextDocument document = textView.Document;
@@ -71,7 +72,9 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 				highlighter = null;
 			
 			if (highlighter != null && isInTextView) {
+				// for backward compatiblity, we're registering using both the interface and concrete types
 				textView.Services.AddService(typeof(DocumentHighlighter), highlighter);
+				textView.Services.AddService(typeof(IHighlighter), highlighter);
 			}
 		}
 		
@@ -82,6 +85,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 			isInTextView = true;
 			if (highlighter != null) {
 				textView.Services.AddService(typeof(DocumentHighlighter), highlighter);
+				textView.Services.AddService(typeof(IHighlighter), highlighter);
 			}
 		}
 		
@@ -92,6 +96,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 			isInTextView = false;
 			if (highlighter != null) {
 				textView.Services.RemoveService(typeof(DocumentHighlighter));
+				textView.Services.RemoveService(typeof(IHighlighter));
 			}
 		}
 		
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HtmlClipboard.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HtmlClipboard.cs
index 559419ae25..acf8aff925 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HtmlClipboard.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HtmlClipboard.cs
@@ -65,16 +65,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 		/// Creates a HTML fragment from a part of a document.
 		/// </summary>
 		/// <param name="document">The document to create HTML from.</param>
-		/// <param name="highlighter">The highlighter used to highlight the document.</param>
-		/// <param name="segment">The part of the document to create HTML for. You can pass null to create HTML for the whole document.</param>
+		/// <param name="highlighter">The highlighter used to highlight the document. <c>null</c> is valid and will create HTML without any highlighting.</param>
+		/// <param name="segment">The part of the document to create HTML for. You can pass <c>null</c> to create HTML for the whole document.</param>
 		/// <param name="options">The options for the HTML creation.</param>
 		/// <returns>HTML code for the document part.</returns>
-		public static string CreateHtmlFragment(TextDocument document, DocumentHighlighter highlighter, ISegment segment, HtmlOptions options)
+		public static string CreateHtmlFragment(TextDocument document, IHighlighter highlighter, ISegment segment, HtmlOptions options)
 		{
 			if (document == null)
 				throw new ArgumentNullException("document");
 			if (options == null)
 				throw new ArgumentNullException("options");
+			if (highlighter != null && highlighter.Document != document)
+				throw new ArgumentException("Highlighter does not belong to the specified document.");
 			if (segment == null)
 				segment = new SimpleSegment(0, document.TextLength);
 			
@@ -84,7 +86,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
 			while (line != null && line.Offset < segmentEndOffset) {
 				HighlightedLine highlightedLine;
 				if (highlighter != null)
-					highlightedLine = highlighter.HighlightLine(line);
+					highlightedLine = highlighter.HighlightLine(line.LineNumber);
 				else
 					highlightedLine = new HighlightedLine(document, line);
 				SimpleSegment s = segment.GetOverlap(line);
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs
new file mode 100644
index 0000000000..85ac6f0132
--- /dev/null
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs
@@ -0,0 +1,39 @@
+// <file>
+//     <copyright see="prj:///doc/copyright.txt"/>
+//     <license see="prj:///doc/license.txt"/>
+//     <owner name="Daniel Grunwald"/>
+//     <version>$Revision$</version>
+// </file>
+
+using System;
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Utils;
+
+namespace ICSharpCode.AvalonEdit.Highlighting
+{
+	/// <summary>
+	/// Represents a highlighted document.
+	/// </summary>
+	/// <remarks>This interface is used by the <see cref="HighlightingColorizer"/> to register the highlighter as a TextView service.</remarks>
+	public interface IHighlighter
+	{
+		/// <summary>
+		/// Gets the underlying text document.
+		/// </summary>
+		TextDocument Document { get; }
+		
+			/// <summary>
+		/// Gets the span stack at the end of the specified line.
+		/// -> GetSpanStack(1) returns the spans at the start of the second line.
+		/// </summary>
+		/// <remarks>GetSpanStack(0) is valid and will always return the empty stack.</remarks>
+		ImmutableStack<HighlightingSpan> GetSpanStack(int lineNumber);
+		
+		/// <summary>
+		/// Highlights the specified document line.
+		/// </summary>
+		/// <param name="lineNumber">The line to highlight.</param>
+		/// <returns>A <see cref="HighlightedLine"/> line object that represents the highlighted sections.</returns>
+		HighlightedLine HighlightLine(int lineNumber);
+	}
+}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
index 9b4af13f7a..37d1d21fa1 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
@@ -185,6 +185,7 @@
     <Compile Include="Highlighting\HighlightingDefinitionTypeConverter.cs" />
     <Compile Include="Highlighting\HighlightingManager.cs" />
     <Compile Include="Highlighting\HtmlClipboard.cs" />
+    <Compile Include="Highlighting\IHighlighter.cs" />
     <Compile Include="Highlighting\IHighlightingDefinition.cs" />
     <Compile Include="Highlighting\HighlightingRule.cs" />
     <Compile Include="Highlighting\Resources\Resources.cs" />
diff --git a/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs b/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs
index f89268ce71..e3b03be37b 100644
--- a/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs
+++ b/src/Main/Base/Project/Src/Editor/AvalonEdit/AvalonEditSyntaxHighlighterAdapter.cs
@@ -28,7 +28,7 @@ namespace ICSharpCode.SharpDevelop.Editor.AvalonEdit
 		
 		public IEnumerable<string> GetSpanColorNamesFromLineStart(int lineNumber)
 		{
-			DocumentHighlighter highlighter = textEditor.GetService(typeof(DocumentHighlighter)) as DocumentHighlighter;
+			IHighlighter highlighter = textEditor.GetService(typeof(IHighlighter)) as IHighlighter;
 			if (highlighter != null) {
 				// delayed evaluation doesn't cause a problem here: GetSpanStack is called immediately,
 				// only the where/select portian is evaluated later. But that won't be a problem because the
diff --git a/src/Main/Base/Project/Src/Gui/AbstractViewContent.cs b/src/Main/Base/Project/Src/Gui/AbstractViewContent.cs
index 4f1be51695..949820ec4e 100644
--- a/src/Main/Base/Project/Src/Gui/AbstractViewContent.cs
+++ b/src/Main/Base/Project/Src/Gui/AbstractViewContent.cs
@@ -52,6 +52,10 @@ namespace ICSharpCode.SharpDevelop.Gui
 			get;
 		}
 		
+		public virtual object InitiallyFocusedControl {
+			get { return this.Control; }
+		}
+		
 		IWorkbenchWindow workbenchWindow;
 		
 		IWorkbenchWindow IViewContent.WorkbenchWindow {
diff --git a/src/Main/Base/Project/Src/Gui/IViewContent.cs b/src/Main/Base/Project/Src/Gui/IViewContent.cs
index 00dc7928b0..437138877a 100644
--- a/src/Main/Base/Project/Src/Gui/IViewContent.cs
+++ b/src/Main/Base/Project/Src/Gui/IViewContent.cs
@@ -47,10 +47,17 @@ namespace ICSharpCode.SharpDevelop.Gui
 			get;
 		}
 		
+		/// <summary>
+		/// Gets the control which has focus initially.
+		/// </summary>
+		object InitiallyFocusedControl {
+			get;
+		}
+		
 		/// <summary>
 		/// The workbench window in which this view is displayed.
 		/// </summary>
-		IWorkbenchWindow  WorkbenchWindow {
+		IWorkbenchWindow WorkbenchWindow {
 			get;
 			set;
 		}
diff --git a/src/Main/Base/Project/Src/Gui/Workbench/Layouts/AvalonWorkbenchWindow.cs b/src/Main/Base/Project/Src/Gui/Workbench/Layouts/AvalonWorkbenchWindow.cs
index 06be17afa9..b08a829c16 100644
--- a/src/Main/Base/Project/Src/Gui/Workbench/Layouts/AvalonWorkbenchWindow.cs
+++ b/src/Main/Base/Project/Src/Gui/Workbench/Layouts/AvalonWorkbenchWindow.cs
@@ -46,6 +46,9 @@ namespace ICSharpCode.SharpDevelop.Gui
 		protected override void FocusContent()
 		{
 			IInputElement activeChild = CustomFocusManager.GetFocusedChild(this);
+			if (activeChild == null && ActiveViewContent != null) {
+				activeChild = ActiveViewContent.InitiallyFocusedControl as IInputElement;
+			}
 			if (activeChild != null) {
 				LoggingService.Debug("Will move focus to: " + activeChild);
 				Dispatcher.BeginInvoke(DispatcherPriority.Background,