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,