aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors
diff options
context:
space:
mode:
authorShlomo Hecht <shlomo@twine-s.com>2019-02-11 15:32:29 +0200
committerShlomo Hecht <shlomo@twine-s.com>2019-02-11 15:32:29 +0200
commit2843c9ee0d8dc33e1eb21cb8e5dc25ca766e5170 (patch)
tree4b8dbd0763849e5924f30fead361b0c1dbe5b1e5 /Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors
parent46c70c15a983f3d0684d88b26f7ed8247588cd88 (diff)
parent4c5f48a52025e560f40418d5983926432adb1eaa (diff)
downloadTango-2843c9ee0d8dc33e1eb21cb8e5dc25ca766e5170.tar.gz
Tango-2843c9ee0d8dc33e1eb21cb8e5dc25ca766e5170.zip
Merge branch 'master' of https://twinetfs.visualstudio.com/Tango/_git/Tango
Diffstat (limited to 'Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors')
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/AvalonEditCommands.cs87
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.cs377
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.xaml72
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionListBox.cs96
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindow.cs221
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindowBase.cs380
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/ICompletionData.cs54
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/IOverloadProvider.cs42
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.cs94
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.xaml118
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadInsightWindow.cs58
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadViewer.cs101
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ChangeTrackingCheckpoint.cs140
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeEventArgs.cs131
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeOperation.cs52
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLine.cs242
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLineTree.cs712
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/GapTextBuffer.cs192
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ILineTracker.cs55
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ISegment.cs219
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ITextSource.cs320
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/IUndoableOperation.cs30
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineManager.cs288
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineNode.cs83
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/NewLineFinder.cs134
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/OffsetChangeMap.cs347
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchor.cs194
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorNode.cs87
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorTree.cs753
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocument.cs836
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocumentWeakEventManager.cs149
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextLocation.cs166
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegment.cs248
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegmentCollection.cs971
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextUtilities.cs332
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoOperationGroup.cs61
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoStack.cs450
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/WeakLineTracker.cs85
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/AbstractMargin.cs102
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Caret.cs472
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretLayer.cs86
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs375
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretWeakEventHandler.cs33
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DottedLineMargin.cs63
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DragDropException.cs46
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EditingCommandHandler.cs578
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EmptySelection.cs85
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/IReadOnlySectionProvider.cs32
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs206
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeSupport.cs150
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/LineNumberMargin.cs243
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/NoReadOnlySections.cs50
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/RectangleSelection.cs396
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Selection.cs268
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionColorizer.cs58
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionLayer.cs53
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs631
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionSegment.cs89
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SimpleSelection.cs144
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextArea.cs1048
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaDefaultInputHandlers.cs120
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaInputHandler.cs242
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextSegmentReadOnlySectionProvider.cs80
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/AbstractFoldingStrategy.cs30
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/BraceFoldingStrategy.cs156
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingElementGenerator.cs194
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingManager.cs388
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs343
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMarginMarker.cs90
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingSection.cs186
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/NewFolding.cs70
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/XmlFoldingStrategy.cs215
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/DocumentHighlighter.cs468
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedInlineBuilder.cs214
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedLine.cs154
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedSection.cs33
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingBrush.cs117
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColor.cs119
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColorizer.cs286
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionInvalidException.cs43
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionTypeConverter.cs54
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingManager.cs290
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRule.cs31
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRuleSet.cs46
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingSpan.cs64
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HtmlClipboard.cs201
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlighter.cs35
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinition.cs54
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinitionReferenceResolver.cs18
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ASPX.xshd16
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Boo.xshd212
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CPP-Mode.xshd195
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSS-Mode.xshd57
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSharp-Mode.xshd315
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Coco-Mode.xshd74
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/HTML-Mode.xshd388
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Java-Mode.xshd152
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/JavaScript-Mode.xshd132
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/MarkDown-Mode.xshd56
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV1.xsd296
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV2.xsd167
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PHP-Mode.xshd158
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Patch-Mode.xshd35
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PowerShell.xshd146
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Resources.cs48
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Tex-Mode.xshd108
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/VBNET-Mode.xshd256
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XML-Mode.xshd63
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XmlDoc.xshd57
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/HighlightingLoader.cs99
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/IXshdVisitor.cs32
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/SaveXshdVisitor.cs182
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V1Loader.cs325
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V2Loader.cs334
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XmlHighlightingDefinition.cs406
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdColor.cs97
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdElement.cs29
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdImport.cs25
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdKeywords.cs36
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdProperty.cs38
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdReference.cs127
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRule.cs35
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRuleSet.cs51
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSpan.cs82
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSyntaxDefinition.cs50
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs124
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs96
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs107
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs474
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs40
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs25
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/AssemblyInfo.cs43
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/CodeAnalysisDictionary.xml33
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/BackgroundGeometryBuilder.cs343
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/CollapsedLineSection.cs95
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColorizingTransformer.cs108
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColumnRulerRenderer.cs64
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DefaultTextRunTypographyProperties.cs171
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DocumentColorizingTransformer.cs81
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/FormattedTextElement.cs207
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/GlobalTextRunProperties.cs30
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTree.cs1092
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeLineNode.cs49
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeNode.cs155
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IBackgroundRenderer.cs26
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextRunConstructionContext.cs47
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextViewConnect.cs24
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IVisualLineTransformer.cs19
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/InlineObjectRun.cs145
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/Layer.cs43
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LayerPosition.cs91
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LinkElementGenerator.cs144
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/MouseHoverLogic.cs134
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SimpleTextSource.cs39
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SingleCharacterElementGenerator.cs268
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextLayer.cs70
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextView.cs2003
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewCachedElements.cs43
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewWeakEventManager.cs71
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLine.cs681
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineConstructionStartEventArgs.cs29
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElement.cs249
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementGenerator.cs63
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementTextRunProperties.cs241
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineLinkText.cs114
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineText.cs122
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextParagraphProperties.cs31
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextSource.cs123
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLinesInvalidException.cs44
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualYPosition.cs46
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/ScriptEditor.cs276
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.cs60
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.xaml71
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/ISearchStrategy.cs88
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/Localization.cs64
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/RegexSearchStrategy.cs70
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchCommands.cs105
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.cs467
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.xaml47
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchResultBackgroundRenderer.cs78
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchStrategyFactory.cs68
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/next.pngbin0 -> 1304 bytes
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/prev.pngbin0 -> 1300 bytes
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/IActiveElement.cs35
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/InsertionContext.cs269
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/Snippet.cs47
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetAnchorElement.cs97
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetBoundElement.cs122
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetCaretElement.cs58
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetContainerElement.cs49
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetElement.cs32
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetEventArgs.cs57
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetInputHandler.cs80
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs213
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetSelectionElement.cs45
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetTextElement.cs39
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj498
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.cs1144
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.xaml79
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorAutomationPeer.cs65
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorComponent.cs40
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorOptions.cs434
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorWeakEventManager.cs52
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextViewPosition.cs158
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Theme.xaml3066
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/Generic.xaml102
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/RightArrow.curbin0 -> 326 bytes
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Boxes.cs21
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/BusyManager.cs55
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CallbackOnDispose.cs35
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CharRope.cs172
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CompressingTreeList.cs882
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Constants.cs15
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/DelayedEvents.cs48
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Deque.cs174
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Empty.cs17
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ExtensionMethods.cs214
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/FileReader.cs208
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ImmutableStack.cs114
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/NullSafeCollection.cs31
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ObserveAddRemoveCollection.cs63
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PixelSnapHelpers.cs92
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PropertyChangedWeakEventManager.cs26
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Rope.cs789
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeNode.cs605
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeTextReader.cs105
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/StringSegment.cs107
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/TextFormatterFactory.cs71
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ThrowUtil.cs56
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/WeakEventManagerBase.cs86
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Win32.cs85
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttribute.cs129
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttributeCollection.cs119
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs282
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlDocument.cs69
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs226
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs266
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectCollection.cs90
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectEventArgs.cs21
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlParser.cs201
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlTag.cs108
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlText.cs62
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AbstractAXmlVisitor.cs44
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/CanonicalPrintAXmlVisitor.cs119
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/ExtensionMethods.cs47
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/FilteredCollection.cs99
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/IAXmlVisitor.cs29
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/InternalException.cs45
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/MergedCollection.cs70
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/PrettyPrintAXmlVisitor.cs69
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/SyntaxError.cs36
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagMatchingHeuristics.cs439
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagReader.cs740
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TextType.cs39
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TokenReader.cs309
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TrackedSegmentCollection.cs165
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/app.config4
-rw-r--r--Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/packages.config4
258 files changed, 47729 insertions, 0 deletions
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/AvalonEditCommands.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/AvalonEditCommands.cs
new file mode 100644
index 000000000..5aaed19d8
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/AvalonEditCommands.cs
@@ -0,0 +1,87 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Input;
+
+namespace Tango.Scripting.Editors
+{
+ /// <summary>
+ /// Custom commands for AvalonEdit.
+ /// </summary>
+ public static class AvalonEditCommands
+ {
+ /// <summary>
+ /// Deletes the current line.
+ /// The default shortcut is Ctrl+D.
+ /// </summary>
+ public static readonly RoutedCommand DeleteLine = new RoutedCommand(
+ "DeleteLine", typeof(TextEditor),
+ new InputGestureCollection {
+ new KeyGesture(Key.D, ModifierKeys.Control)
+ });
+
+ /// <summary>
+ /// Removes leading whitespace from the selected lines (or the whole document if the selection is empty).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "WPF uses 'Whitespace'")]
+ public static readonly RoutedCommand RemoveLeadingWhitespace = new RoutedCommand("RemoveLeadingWhitespace", typeof(TextEditor));
+
+ /// <summary>
+ /// Removes trailing whitespace from the selected lines (or the whole document if the selection is empty).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "WPF uses 'Whitespace'")]
+ public static readonly RoutedCommand RemoveTrailingWhitespace = new RoutedCommand("RemoveTrailingWhitespace", typeof(TextEditor));
+
+ /// <summary>
+ /// Converts the selected text to upper case.
+ /// </summary>
+ public static readonly RoutedCommand ConvertToUppercase = new RoutedCommand("ConvertToUppercase", typeof(TextEditor));
+
+ /// <summary>
+ /// Converts the selected text to lower case.
+ /// </summary>
+ public static readonly RoutedCommand ConvertToLowercase = new RoutedCommand("ConvertToLowercase", typeof(TextEditor));
+
+ /// <summary>
+ /// Converts the selected text to title case.
+ /// </summary>
+ public static readonly RoutedCommand ConvertToTitleCase = new RoutedCommand("ConvertToTitleCase", typeof(TextEditor));
+
+ /// <summary>
+ /// Inverts the case of the selected text.
+ /// </summary>
+ public static readonly RoutedCommand InvertCase = new RoutedCommand("InvertCase", typeof(TextEditor));
+
+ /// <summary>
+ /// Converts tabs to spaces in the selected text.
+ /// </summary>
+ public static readonly RoutedCommand ConvertTabsToSpaces = new RoutedCommand("ConvertTabsToSpaces", typeof(TextEditor));
+
+ /// <summary>
+ /// Converts spaces to tabs in the selected text.
+ /// </summary>
+ public static readonly RoutedCommand ConvertSpacesToTabs = new RoutedCommand("ConvertSpacesToTabs", typeof(TextEditor));
+
+ /// <summary>
+ /// Converts leading tabs to spaces in the selected lines (or the whole document if the selection is empty).
+ /// </summary>
+ public static readonly RoutedCommand ConvertLeadingTabsToSpaces = new RoutedCommand("ConvertLeadingTabsToSpaces", typeof(TextEditor));
+
+ /// <summary>
+ /// Converts leading spaces to tabs in the selected lines (or the whole document if the selection is empty).
+ /// </summary>
+ public static readonly RoutedCommand ConvertLeadingSpacesToTabs = new RoutedCommand("ConvertLeadingSpacesToTabs", typeof(TextEditor));
+
+ /// <summary>
+ /// Runs the IIndentationStrategy on the selected lines (or the whole document if the selection is empty).
+ /// </summary>
+ public static readonly RoutedCommand IndentSelection = new RoutedCommand(
+ "IndentSelection", typeof(TextEditor),
+ new InputGestureCollection {
+ new KeyGesture(Key.I, ModifierKeys.Control)
+ });
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.cs
new file mode 100644
index 000000000..a50b82463
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.cs
@@ -0,0 +1,377 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Linq;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// The listbox used inside the CompletionWindow, contains CompletionListBox.
+ /// </summary>
+ public class CompletionList : Control
+ {
+ static CompletionList()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(CompletionList),
+ new FrameworkPropertyMetadata(typeof(CompletionList)));
+ }
+
+ bool isFiltering = true;
+ /// <summary>
+ /// If true, the CompletionList is filtered to show only matching items. Also enables search by substring.
+ /// If false, enables the old behavior: no filtering, search by string.StartsWith.
+ /// </summary>
+ public bool IsFiltering {
+ get { return isFiltering; }
+ set { isFiltering = value; }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="EmptyTemplate" />.
+ /// </summary>
+ public static readonly DependencyProperty EmptyTemplateProperty =
+ DependencyProperty.Register("EmptyTemplate", typeof(ControlTemplate), typeof(CompletionList),
+ new FrameworkPropertyMetadata());
+
+ /// <summary>
+ /// Content of EmptyTemplate will be shown when CompletionList contains no items.
+ /// If EmptyTemplate is null, nothing will be shown.
+ /// </summary>
+ public ControlTemplate EmptyTemplate {
+ get { return (ControlTemplate)GetValue(EmptyTemplateProperty); }
+ set { SetValue(EmptyTemplateProperty, value); }
+ }
+
+ /// <summary>
+ /// Is raised when the completion list indicates that the user has chosen
+ /// an entry to be completed.
+ /// </summary>
+ public event EventHandler InsertionRequested;
+
+ /// <summary>
+ /// Raises the InsertionRequested event.
+ /// </summary>
+ public void RequestInsertion(EventArgs e)
+ {
+ if (InsertionRequested != null)
+ InsertionRequested(this, e);
+ }
+
+ CompletionListBox listBox;
+
+ /// <inheritdoc/>
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ listBox = GetTemplateChild("PART_ListBox") as CompletionListBox;
+ if (listBox != null) {
+ listBox.ItemsSource = completionData;
+ }
+ }
+
+ /// <summary>
+ /// Gets the list box.
+ /// </summary>
+ public CompletionListBox ListBox {
+ get {
+ if (listBox == null)
+ ApplyTemplate();
+ return listBox;
+ }
+ }
+
+ /// <summary>
+ /// Gets the scroll viewer used in this list box.
+ /// </summary>
+ public ScrollViewer ScrollViewer {
+ get { return listBox != null ? listBox.scrollViewer : null; }
+ }
+
+ ObservableCollection<ICompletionData> completionData = new ObservableCollection<ICompletionData>();
+
+ /// <summary>
+ /// Gets the list to which completion data can be added.
+ /// </summary>
+ public IList<ICompletionData> CompletionData {
+ get { return completionData; }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ if (!e.Handled) {
+ HandleKey(e);
+ }
+ }
+
+ /// <summary>
+ /// Handles a key press. Used to let the completion list handle key presses while the
+ /// focus is still on the text editor.
+ /// </summary>
+ public void HandleKey(KeyEventArgs e)
+ {
+ if (listBox == null)
+ return;
+
+ // We have to do some key handling manually, because the default doesn't work with
+ // our simulated events.
+ // Also, the default PageUp/PageDown implementation changes the focus, so we avoid it.
+ switch (e.Key) {
+ case Key.Down:
+ e.Handled = true;
+ listBox.SelectIndex(listBox.SelectedIndex + 1);
+ break;
+ case Key.Up:
+ e.Handled = true;
+ listBox.SelectIndex(listBox.SelectedIndex - 1);
+ break;
+ case Key.PageDown:
+ e.Handled = true;
+ listBox.SelectIndex(listBox.SelectedIndex + listBox.VisibleItemCount);
+ break;
+ case Key.PageUp:
+ e.Handled = true;
+ listBox.SelectIndex(listBox.SelectedIndex - listBox.VisibleItemCount);
+ break;
+ case Key.Home:
+ e.Handled = true;
+ listBox.SelectIndex(0);
+ break;
+ case Key.End:
+ e.Handled = true;
+ listBox.SelectIndex(listBox.Items.Count - 1);
+ break;
+ case Key.Tab:
+ case Key.Enter:
+ e.Handled = true;
+ RequestInsertion(e);
+ break;
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
+ {
+ base.OnMouseDoubleClick(e);
+ if (e.ChangedButton == MouseButton.Left) {
+ e.Handled = true;
+ RequestInsertion(e);
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the selected item.
+ /// </summary>
+ public ICompletionData SelectedItem {
+ get {
+ return (listBox != null ? listBox.SelectedItem : null) as ICompletionData;
+ }
+ set {
+ if (listBox == null && value != null)
+ ApplyTemplate();
+ listBox.SelectedItem = value;
+ }
+ }
+
+ /// <summary>
+ /// Occurs when the SelectedItem property changes.
+ /// </summary>
+ public event SelectionChangedEventHandler SelectionChanged {
+ add { AddHandler(Selector.SelectionChangedEvent, value); }
+ remove { RemoveHandler(Selector.SelectionChangedEvent, value); }
+ }
+
+ // SelectItem gets called twice for every typed character (once from FormatLine), this helps execute SelectItem only once
+ string currentText;
+ ObservableCollection<ICompletionData> currentList;
+
+ /// <summary>
+ /// Selects the best match, and filter the items if turned on using <see cref="IsFiltering" />.
+ /// </summary>
+ public void SelectItem(string text)
+ {
+ if (text == currentText)
+ return;
+ if (listBox == null)
+ ApplyTemplate();
+
+ if (this.IsFiltering) {
+ SelectItemFiltering(text);
+ }
+ else {
+ SelectItemWithStart(text);
+ }
+ currentText = text;
+ }
+
+ /// <summary>
+ /// Filters CompletionList items to show only those matching given query, and selects the best match.
+ /// </summary>
+ void SelectItemFiltering(string query)
+ {
+ // if the user just typed one more character, don't filter all data but just filter what we are already displaying
+ var listToFilter = (this.currentList != null && (!string.IsNullOrEmpty(this.currentText)) && (!string.IsNullOrEmpty(query)) &&
+ query.StartsWith(this.currentText, StringComparison.Ordinal)) ?
+ this.currentList : this.completionData;
+
+ var matchingItems =
+ from item in listToFilter
+ let quality = GetMatchQuality(item.Text, query)
+ where quality > 0
+ select new { Item = item, Quality = quality };
+
+ // e.g. "DateTimeKind k = (*cc here suggests DateTimeKind*)"
+ ICompletionData suggestedItem = listBox.SelectedIndex != -1 ? (ICompletionData)(listBox.Items[listBox.SelectedIndex]) : null;
+
+ var listBoxItems = new ObservableCollection<ICompletionData>();
+ int bestIndex = -1;
+ int bestQuality = -1;
+ double bestPriority = 0;
+ int i = 0;
+ foreach (var matchingItem in matchingItems) {
+ double priority = matchingItem.Item == suggestedItem ? double.PositiveInfinity : matchingItem.Item.Priority;
+ int quality = matchingItem.Quality;
+ if (quality > bestQuality || (quality == bestQuality && (priority > bestPriority))) {
+ bestIndex = i;
+ bestPriority = priority;
+ bestQuality = quality;
+ }
+ listBoxItems.Add(matchingItem.Item);
+ i++;
+ }
+ this.currentList = listBoxItems;
+ listBox.ItemsSource = listBoxItems;
+ SelectIndexCentered(bestIndex);
+ }
+
+ /// <summary>
+ /// Selects the item that starts with the specified query.
+ /// </summary>
+ void SelectItemWithStart(string query)
+ {
+ if (string.IsNullOrEmpty(query))
+ return;
+
+ int suggestedIndex = listBox.SelectedIndex;
+
+ int bestIndex = -1;
+ int bestQuality = -1;
+ double bestPriority = 0;
+ for (int i = 0; i < completionData.Count; ++i) {
+ int quality = GetMatchQuality(completionData[i].Text, query);
+ if (quality < 0)
+ continue;
+
+ double priority = completionData[i].Priority;
+ bool useThisItem;
+ if (bestQuality < quality) {
+ useThisItem = true;
+ } else {
+ if (bestIndex == suggestedIndex) {
+ useThisItem = false;
+ } else if (i == suggestedIndex) {
+ // prefer recommendedItem, regardless of its priority
+ useThisItem = bestQuality == quality;
+ } else {
+ useThisItem = bestQuality == quality && bestPriority < priority;
+ }
+ }
+ if (useThisItem) {
+ bestIndex = i;
+ bestPriority = priority;
+ bestQuality = quality;
+ }
+ }
+ SelectIndexCentered(bestIndex);
+ }
+
+ void SelectIndexCentered(int bestIndex)
+ {
+ if (bestIndex < 0) {
+ listBox.ClearSelection();
+ } else {
+ int firstItem = listBox.FirstVisibleItem;
+ if (bestIndex < firstItem || firstItem + listBox.VisibleItemCount <= bestIndex) {
+ // CenterViewOn does nothing as CompletionListBox.ScrollViewer is null
+ listBox.CenterViewOn(bestIndex);
+ listBox.SelectIndex(bestIndex);
+ } else {
+ listBox.SelectIndex(bestIndex);
+ }
+ }
+ }
+
+ int GetMatchQuality(string itemText, string query)
+ {
+ if (itemText == null)
+ throw new ArgumentNullException("itemText", "ICompletionData.Text returned null");
+
+ // Qualities:
+ // 8 = full match case sensitive
+ // 7 = full match
+ // 6 = match start case sensitive
+ // 5 = match start
+ // 4 = match CamelCase when length of query is 1 or 2 characters
+ // 3 = match substring case sensitive
+ // 2 = match sustring
+ // 1 = match CamelCase
+ // -1 = no match
+ if (query == itemText)
+ return 8;
+ if (string.Equals(itemText, query, StringComparison.InvariantCultureIgnoreCase))
+ return 7;
+
+ if (itemText.StartsWith(query, StringComparison.InvariantCulture))
+ return 6;
+ if (itemText.StartsWith(query, StringComparison.InvariantCultureIgnoreCase))
+ return 5;
+
+ bool? camelCaseMatch = null;
+ if (query.Length <= 2) {
+ camelCaseMatch = CamelCaseMatch(itemText, query);
+ if (camelCaseMatch == true) return 4;
+ }
+
+ // search by substring, if filtering (i.e. new behavior) turned on
+ if (IsFiltering) {
+ if (itemText.IndexOf(query, StringComparison.InvariantCulture) >= 0)
+ return 3;
+ if (itemText.IndexOf(query, StringComparison.InvariantCultureIgnoreCase) >= 0)
+ return 2;
+ }
+
+ if (!camelCaseMatch.HasValue)
+ camelCaseMatch = CamelCaseMatch(itemText, query);
+ if (camelCaseMatch == true)
+ return 1;
+
+ return -1;
+ }
+
+ static bool CamelCaseMatch(string text, string query)
+ {
+ int i = 0;
+ foreach (char upper in text.Where(c => char.IsUpper(c))) {
+ if (i > query.Length - 1)
+ return true; // return true here for CamelCase partial match ("CQ" matches "CodeQualityAnalysis")
+ if (char.ToUpper(query[i], CultureInfo.InvariantCulture) != upper)
+ return false;
+ i++;
+ }
+ if (i >= query.Length)
+ return true;
+ return false;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.xaml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.xaml
new file mode 100644
index 000000000..3887b139d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionList.xaml
@@ -0,0 +1,72 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:AvalonEdit="clr-namespace:Tango.Scripting.Editors"
+ xmlns:cc="clr-namespace:Tango.Scripting.Editors.CodeCompletion"
+>
+
+
+ <!--<Style TargetType="{x:Type ListBoxItem}" x:Key="CompletionListBoxItem">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ListBoxItem}">
+ <Border Name="Bd"
+ Background="#202020"
+ BorderBrush="#222222"
+ BorderThickness="0 0 0 1"
+ Padding="{TemplateBinding Padding}"
+ SnapsToDevicePixels="true">
+ <ContentPresenter
+ HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
+ VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
+ SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
+ </Border>
+ --><!-- Simplified triggers:
+ we don't want a gray selection background when the ListBox doesn't have focus
+ --><!--
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsSelected" Value="true">
+ <Setter Property="Background"
+ Value="#242424"/>
+ <Setter Property="Foreground"
+ Value="#242424"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>-->
+
+ <Style TargetType="{x:Type cc:CompletionList}">
+ <Style.Resources>
+ <Style TargetType="ListBoxItem">
+ <Setter Property="Background" Value="#202020"></Setter>
+ <Setter Property="Foreground" Value="Gainsboro"></Setter>
+ <Setter Property="Padding" Value="2"></Setter>
+ <Setter Property="BorderThickness" Value="0"></Setter>
+ <Style.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Foreground" Value="#202020"></Setter>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+ </Style.Resources>
+ <Setter Property="Background" Value="#202020"></Setter>
+ <Setter Property="BorderThickness" Value="0"></Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type cc:CompletionList}">
+ <cc:CompletionListBox x:Name="PART_ListBox">
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <StackPanel Orientation="Horizontal">
+ <Image Source="{Binding Image}" Width="16" Height="16" Margin="0,0,4,0"/>
+ <ContentControl Content="{Binding Content}" />
+ </StackPanel>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </cc:CompletionListBox>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionListBox.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionListBox.cs
new file mode 100644
index 000000000..f515c955c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionListBox.cs
@@ -0,0 +1,96 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// The list box used inside the CompletionList.
+ /// </summary>
+ public class CompletionListBox : ListBox
+ {
+ internal ScrollViewer scrollViewer;
+
+ /// <inheritdoc/>
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ // Find the scroll viewer:
+ scrollViewer = null;
+ if (this.VisualChildrenCount > 0) {
+ Border border = this.GetVisualChild(0) as Border;
+ if (border != null)
+ scrollViewer = border.Child as ScrollViewer;
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of the first visible item.
+ /// </summary>
+ public int FirstVisibleItem {
+ get {
+ if (scrollViewer == null || scrollViewer.ExtentHeight == 0) {
+ return 0;
+ } else {
+ return (int)(this.Items.Count * scrollViewer.VerticalOffset / scrollViewer.ExtentHeight);
+ }
+ }
+ set {
+ value = value.CoerceValue(0, this.Items.Count - this.VisibleItemCount);
+ if (scrollViewer != null) {
+ scrollViewer.ScrollToVerticalOffset((double)value / this.Items.Count * scrollViewer.ExtentHeight);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of visible items.
+ /// </summary>
+ public int VisibleItemCount {
+ get {
+ if (scrollViewer == null || scrollViewer.ExtentHeight == 0) {
+ return 10;
+ } else {
+ return Math.Max(
+ 3,
+ (int)Math.Ceiling(this.Items.Count * scrollViewer.ViewportHeight
+ / scrollViewer.ExtentHeight));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Removes the selection.
+ /// </summary>
+ public void ClearSelection()
+ {
+ this.SelectedIndex = -1;
+ }
+
+ /// <summary>
+ /// Selects the item with the specified index and scrolls it into view.
+ /// </summary>
+ public void SelectIndex(int index)
+ {
+ if (index >= this.Items.Count)
+ index = this.Items.Count - 1;
+ if (index < 0)
+ index = 0;
+ this.SelectedIndex = index;
+ this.ScrollIntoView(this.SelectedItem);
+ }
+
+ /// <summary>
+ /// Centers the view on the item with the specified index.
+ /// </summary>
+ public void CenterViewOn(int index)
+ {
+ this.FirstVisibleItem = index - VisibleItemCount / 2;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindow.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindow.cs
new file mode 100644
index 000000000..44621df72
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindow.cs
@@ -0,0 +1,221 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Threading;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// The code completion window.
+ /// </summary>
+ public class CompletionWindow : CompletionWindowBase
+ {
+ readonly CompletionList completionList = new CompletionList();
+ ToolTip toolTip = new ToolTip();
+
+ /// <summary>
+ /// Gets the completion list used in this completion window.
+ /// </summary>
+ public CompletionList CompletionList
+ {
+ get { return completionList; }
+ }
+
+ /// <summary>
+ /// Creates a new code completion window.
+ /// </summary>
+ public CompletionWindow(TextArea textArea) : base(textArea)
+ {
+ // keep height automatic
+ this.CloseAutomatically = true;
+ this.SizeToContent = SizeToContent.Height;
+ this.MaxHeight = 300;
+ this.Width = 175;
+ this.Content = completionList;
+ // prevent user from resizing window to 0x0
+ this.MinHeight = 15;
+ this.MinWidth = 30;
+ //this.Background = new SolidColorBrush(Color.FromRgb(15, 15, 15));
+ this.Foreground = Brushes.Gainsboro;
+
+ toolTip.PlacementTarget = this;
+ toolTip.Placement = PlacementMode.Right;
+ toolTip.Closed += toolTip_Closed;
+
+ AttachEvents();
+ }
+
+ #region ToolTip handling
+ void toolTip_Closed(object sender, RoutedEventArgs e)
+ {
+ // Clear content after tooltip is closed.
+ // We cannot clear is immediately when setting IsOpen=false
+ // because the tooltip uses an animation for closing.
+ if (toolTip != null)
+ toolTip.Content = null;
+ }
+
+ void completionList_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ var item = completionList.SelectedItem;
+ if (item == null)
+ return;
+ object description = item.Description;
+ if (description != null)
+ {
+ string descriptionText = description as string;
+ if (descriptionText != null)
+ {
+ toolTip.Content = new TextBlock
+ {
+ Text = descriptionText,
+ TextWrapping = TextWrapping.Wrap
+ };
+ }
+ else
+ {
+ toolTip.Content = description;
+ }
+ toolTip.IsOpen = true;
+ }
+ else
+ {
+ toolTip.IsOpen = false;
+ }
+ }
+ #endregion
+
+ void completionList_InsertionRequested(object sender, EventArgs e)
+ {
+ Close();
+ // The window must close before Complete() is called.
+ // If the Complete callback pushes stacked input handlers, we don't want to pop those when the CC window closes.
+ var item = completionList.SelectedItem;
+ if (item != null)
+ item.Complete(this.TextArea, new AnchorSegment(this.TextArea.Document, this.StartOffset, this.EndOffset - this.StartOffset), e);
+ }
+
+ void AttachEvents()
+ {
+ this.completionList.InsertionRequested += completionList_InsertionRequested;
+ this.completionList.SelectionChanged += completionList_SelectionChanged;
+ this.TextArea.Caret.PositionChanged += CaretPositionChanged;
+ this.TextArea.MouseWheel += textArea_MouseWheel;
+ this.TextArea.PreviewTextInput += textArea_PreviewTextInput;
+ }
+
+ /// <inheritdoc/>
+ protected override void DetachEvents()
+ {
+ this.completionList.InsertionRequested -= completionList_InsertionRequested;
+ this.completionList.SelectionChanged -= completionList_SelectionChanged;
+ this.TextArea.Caret.PositionChanged -= CaretPositionChanged;
+ this.TextArea.MouseWheel -= textArea_MouseWheel;
+ this.TextArea.PreviewTextInput -= textArea_PreviewTextInput;
+ base.DetachEvents();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnClosed(EventArgs e)
+ {
+ base.OnClosed(e);
+ if (toolTip != null)
+ {
+ toolTip.IsOpen = false;
+ toolTip = null;
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ if (!e.Handled)
+ {
+ completionList.HandleKey(e);
+ }
+ }
+
+ void textArea_PreviewTextInput(object sender, TextCompositionEventArgs e)
+ {
+ e.Handled = RaiseEventPair(this, PreviewTextInputEvent, TextInputEvent,
+ new TextCompositionEventArgs(e.Device, e.TextComposition));
+ }
+
+ void textArea_MouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ e.Handled = RaiseEventPair(GetScrollEventTarget(),
+ PreviewMouseWheelEvent, MouseWheelEvent,
+ new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta));
+ }
+
+ UIElement GetScrollEventTarget()
+ {
+ if (completionList == null)
+ return this;
+ return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList;
+ }
+
+ /// <summary>
+ /// Gets/Sets whether the completion window should close automatically.
+ /// The default value is true.
+ /// </summary>
+ public bool CloseAutomatically { get; set; }
+
+ /// <inheritdoc/>
+ protected override bool CloseOnFocusLost
+ {
+ get { return this.CloseAutomatically; }
+ }
+
+ /// <summary>
+ /// When this flag is set, code completion closes if the caret moves to the
+ /// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing",
+ /// but not in dot-completion.
+ /// Has no effect if CloseAutomatically is false.
+ /// </summary>
+ public bool CloseWhenCaretAtBeginning { get; set; }
+
+ void CaretPositionChanged(object sender, EventArgs e)
+ {
+ int offset = this.TextArea.Caret.Offset;
+ if (offset == this.StartOffset)
+ {
+ if (CloseAutomatically && CloseWhenCaretAtBeginning)
+ {
+ Close();
+ }
+ else
+ {
+ completionList.SelectItem(string.Empty);
+ }
+ return;
+ }
+ if (offset < this.StartOffset || offset > this.EndOffset)
+ {
+ if (CloseAutomatically)
+ {
+ Close();
+ }
+ }
+ else
+ {
+ TextDocument document = this.TextArea.Document;
+ if (document != null)
+ {
+ completionList.SelectItem(document.GetText(this.StartOffset, offset - this.StartOffset));
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindowBase.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindowBase.cs
new file mode 100644
index 000000000..40c611843
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/CompletionWindowBase.cs
@@ -0,0 +1,380 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Threading;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// Base class for completion windows. Handles positioning the window at the caret.
+ /// </summary>
+ public class CompletionWindowBase : Window
+ {
+ static CompletionWindowBase()
+ {
+ WindowStyleProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(WindowStyle.None));
+ ShowActivatedProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(Boxes.False));
+ ShowInTaskbarProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(Boxes.False));
+ }
+
+ /// <summary>
+ /// Gets the parent TextArea.
+ /// </summary>
+ public TextArea TextArea { get; private set; }
+
+ Window parentWindow;
+ TextDocument document;
+
+ /// <summary>
+ /// Gets/Sets the start of the text range in which the completion window stays open.
+ /// This text portion is used to determine the text used to select an entry in the completion list by typing.
+ /// </summary>
+ public int StartOffset { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the end of the text range in which the completion window stays open.
+ /// This text portion is used to determine the text used to select an entry in the completion list by typing.
+ /// </summary>
+ public int EndOffset { get; set; }
+
+ /// <summary>
+ /// Gets whether the window was opened above the current line.
+ /// </summary>
+ protected bool IsUp { get; private set; }
+
+ /// <summary>
+ /// Creates a new CompletionWindowBase.
+ /// </summary>
+ public CompletionWindowBase(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.TextArea = textArea;
+ parentWindow = Window.GetWindow(textArea);
+ this.Owner = parentWindow;
+ this.AddHandler(MouseUpEvent, new MouseButtonEventHandler(OnMouseUp), true);
+
+ StartOffset = EndOffset = this.TextArea.Caret.Offset;
+
+ AttachEvents();
+ }
+
+ #region Event Handlers
+ void AttachEvents()
+ {
+ document = this.TextArea.Document;
+ if (document != null) {
+ document.Changing += textArea_Document_Changing;
+ }
+ // LostKeyboardFocus seems to be more reliable than PreviewLostKeyboardFocus - see SD-1729
+ this.TextArea.LostKeyboardFocus += TextAreaLostFocus;
+ this.TextArea.TextView.ScrollOffsetChanged += TextViewScrollOffsetChanged;
+ this.TextArea.DocumentChanged += TextAreaDocumentChanged;
+ if (parentWindow != null) {
+ parentWindow.LocationChanged += parentWindow_LocationChanged;
+ }
+
+ // close previous completion windows of same type
+ foreach (InputHandler x in this.TextArea.StackedInputHandlers.OfType<InputHandler>()) {
+ if (x.window.GetType() == this.GetType())
+ this.TextArea.PopStackedInputHandler(x);
+ }
+
+ myInputHandler = new InputHandler(this);
+ this.TextArea.PushStackedInputHandler(myInputHandler);
+ }
+
+ /// <summary>
+ /// Detaches events from the text area.
+ /// </summary>
+ protected virtual void DetachEvents()
+ {
+ if (document != null) {
+ document.Changing -= textArea_Document_Changing;
+ }
+ this.TextArea.LostKeyboardFocus -= TextAreaLostFocus;
+ this.TextArea.TextView.ScrollOffsetChanged -= TextViewScrollOffsetChanged;
+ this.TextArea.DocumentChanged -= TextAreaDocumentChanged;
+ if (parentWindow != null) {
+ parentWindow.LocationChanged -= parentWindow_LocationChanged;
+ }
+ this.TextArea.PopStackedInputHandler(myInputHandler);
+ }
+
+ #region InputHandler
+ InputHandler myInputHandler;
+
+ /// <summary>
+ /// A dummy input handler (that justs invokes the default input handler).
+ /// This is used to ensure the completion window closes when any other input handler
+ /// becomes active.
+ /// </summary>
+ sealed class InputHandler : TextAreaStackedInputHandler
+ {
+ internal readonly CompletionWindowBase window;
+
+ public InputHandler(CompletionWindowBase window)
+ : base(window.TextArea)
+ {
+ Debug.Assert(window != null);
+ this.window = window;
+ }
+
+ public override void Detach()
+ {
+ base.Detach();
+ window.Close();
+ }
+
+ const Key KeyDeadCharProcessed = (Key)0xac; // Key.DeadCharProcessed; // new in .NET 4
+
+ public override void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ // prevents crash when typing deadchar while CC window is open
+ if (e.Key == KeyDeadCharProcessed)
+ return;
+ e.Handled = RaiseEventPair(window, PreviewKeyDownEvent, KeyDownEvent,
+ new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key));
+ }
+
+ public override void OnPreviewKeyUp(KeyEventArgs e)
+ {
+ if (e.Key == KeyDeadCharProcessed)
+ return;
+ e.Handled = RaiseEventPair(window, PreviewKeyUpEvent, KeyUpEvent,
+ new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key));
+ }
+ }
+ #endregion
+
+ void TextViewScrollOffsetChanged(object sender, EventArgs e)
+ {
+ // Workaround for crash #1580 (reproduction steps unknown):
+ // NullReferenceException in System.Windows.Window.CreateSourceWindow()
+ if (!sourceIsInitialized)
+ return;
+
+ IScrollInfo scrollInfo = this.TextArea.TextView;
+ Rect visibleRect = new Rect(scrollInfo.HorizontalOffset, scrollInfo.VerticalOffset, scrollInfo.ViewportWidth, scrollInfo.ViewportHeight);
+ // close completion window when the user scrolls so far that the anchor position is leaving the visible area
+ if (visibleRect.Contains(visualLocation) || visibleRect.Contains(visualLocationTop))
+ UpdatePosition();
+ else
+ Close();
+ }
+
+ void TextAreaDocumentChanged(object sender, EventArgs e)
+ {
+ Close();
+ }
+
+ void TextAreaLostFocus(object sender, RoutedEventArgs e)
+ {
+ Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background);
+ }
+
+ void parentWindow_LocationChanged(object sender, EventArgs e)
+ {
+ UpdatePosition();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnDeactivated(EventArgs e)
+ {
+ base.OnDeactivated(e);
+ Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background);
+ }
+ #endregion
+
+ /// <summary>
+ /// Raises a tunnel/bubble event pair for a WPF control.
+ /// </summary>
+ /// <param name="target">The WPF control for which the event should be raised.</param>
+ /// <param name="previewEvent">The tunneling event.</param>
+ /// <param name="event">The bubbling event.</param>
+ /// <param name="args">The event args to use.</param>
+ /// <returns>The <see cref="RoutedEventArgs.Handled"/> value of the event args.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
+ protected static bool RaiseEventPair(UIElement target, RoutedEvent previewEvent, RoutedEvent @event, RoutedEventArgs args)
+ {
+ if (target == null)
+ throw new ArgumentNullException("target");
+ if (previewEvent == null)
+ throw new ArgumentNullException("previewEvent");
+ if (@event == null)
+ throw new ArgumentNullException("event");
+ if (args == null)
+ throw new ArgumentNullException("args");
+ args.RoutedEvent = previewEvent;
+ target.RaiseEvent(args);
+ args.RoutedEvent = @event;
+ target.RaiseEvent(args);
+ return args.Handled;
+ }
+
+ // Special handler: handledEventsToo
+ void OnMouseUp(object sender, MouseButtonEventArgs e)
+ {
+ ActivateParentWindow();
+ }
+
+ /// <summary>
+ /// Activates the parent window.
+ /// </summary>
+ protected virtual void ActivateParentWindow()
+ {
+ if (parentWindow != null)
+ parentWindow.Activate();
+ }
+
+ void CloseIfFocusLost()
+ {
+ if (CloseOnFocusLost) {
+ Debug.WriteLine("CloseIfFocusLost: this.IsActive=" + this.IsActive + " IsTextAreaFocused=" + IsTextAreaFocused);
+ if (!this.IsActive && !IsTextAreaFocused) {
+ Close();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the completion window should automatically close when the text editor looses focus.
+ /// </summary>
+ protected virtual bool CloseOnFocusLost {
+ get { return true; }
+ }
+
+ bool IsTextAreaFocused {
+ get {
+ if (parentWindow != null && !parentWindow.IsActive)
+ return false;
+ return this.TextArea.IsKeyboardFocused;
+ }
+ }
+
+ bool sourceIsInitialized;
+
+ /// <inheritdoc/>
+ protected override void OnSourceInitialized(EventArgs e)
+ {
+ base.OnSourceInitialized(e);
+
+ if (document != null && this.StartOffset != this.TextArea.Caret.Offset) {
+ SetPosition(new TextViewPosition(document.GetLocation(this.StartOffset)));
+ } else {
+ SetPosition(this.TextArea.Caret.Position);
+ }
+ sourceIsInitialized = true;
+ }
+
+ /// <inheritdoc/>
+ protected override void OnClosed(EventArgs e)
+ {
+ base.OnClosed(e);
+ DetachEvents();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ if (!e.Handled && e.Key == Key.Escape) {
+ e.Handled = true;
+ Close();
+ }
+ }
+
+ Point visualLocation, visualLocationTop;
+
+ /// <summary>
+ /// Positions the completion window at the specified position.
+ /// </summary>
+ protected void SetPosition(TextViewPosition position)
+ {
+ TextView textView = this.TextArea.TextView;
+
+ visualLocation = textView.GetVisualPosition(position, VisualYPosition.LineBottom);
+ visualLocationTop = textView.GetVisualPosition(position, VisualYPosition.LineTop);
+ UpdatePosition();
+ }
+
+ /// <summary>
+ /// Updates the position of the CompletionWindow based on the parent TextView position and the screen working area.
+ /// It ensures that the CompletionWindow is completely visible on the screen.
+ /// </summary>
+ protected void UpdatePosition()
+ {
+ TextView textView = this.TextArea.TextView;
+ // PointToScreen returns device dependent units (physical pixels)
+ Point location = textView.PointToScreen(visualLocation - textView.ScrollOffset);
+ Point locationTop = textView.PointToScreen(visualLocationTop - textView.ScrollOffset);
+
+ // Let's use device dependent units for everything
+ Size completionWindowSize = new Size(this.ActualWidth, this.ActualHeight).TransformToDevice(textView);
+ Rect bounds = new Rect(location, completionWindowSize);
+ Rect workingScreen = System.Windows.Forms.Screen.GetWorkingArea(location.ToSystemDrawing()).ToWpf();
+ if (!workingScreen.Contains(bounds)) {
+ if (bounds.Left < workingScreen.Left) {
+ bounds.X = workingScreen.Left;
+ } else if (bounds.Right > workingScreen.Right) {
+ bounds.X = workingScreen.Right - bounds.Width;
+ }
+ if (bounds.Bottom > workingScreen.Bottom) {
+ bounds.Y = locationTop.Y - bounds.Height;
+ IsUp = true;
+ } else {
+ IsUp = false;
+ }
+ if (bounds.Y < workingScreen.Top) {
+ bounds.Y = workingScreen.Top;
+ }
+ }
+ // Convert the window bounds to device independent units
+ bounds = bounds.TransformFromDevice(textView);
+ this.Left = bounds.X;
+ this.Top = bounds.Y;
+ }
+
+ /// <inheritdoc/>
+ protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
+ {
+ base.OnRenderSizeChanged(sizeInfo);
+ if (sizeInfo.HeightChanged && IsUp) {
+ this.Top += sizeInfo.PreviousSize.Height - sizeInfo.NewSize.Height;
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets whether the completion window should expect text insertion at the start offset,
+ /// which not go into the completion region, but before it.
+ /// </summary>
+ /// <remarks>This property allows only a single insertion, it is reset to false
+ /// when that insertion has occurred.</remarks>
+ public bool ExpectInsertionBeforeStart { get; set; }
+
+ void textArea_Document_Changing(object sender, DocumentChangeEventArgs e)
+ {
+ if (e.Offset + e.RemovalLength == this.StartOffset && e.RemovalLength > 0) {
+ Close(); // removal immediately in front of completion segment: close the window
+ // this is necessary when pressing backspace after dot-completion
+ }
+ if (e.Offset == StartOffset && e.RemovalLength == 0 && ExpectInsertionBeforeStart) {
+ StartOffset = e.GetNewOffset(StartOffset, AnchorMovementType.AfterInsertion);
+ this.ExpectInsertionBeforeStart = false;
+ } else {
+ StartOffset = e.GetNewOffset(StartOffset, AnchorMovementType.BeforeInsertion);
+ }
+ EndOffset = e.GetNewOffset(EndOffset, AnchorMovementType.AfterInsertion);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/ICompletionData.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/ICompletionData.cs
new file mode 100644
index 000000000..b0e9eeccb
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/ICompletionData.cs
@@ -0,0 +1,54 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Media;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// Describes an entry in the <see cref="CompletionList"/>.
+ /// </summary>
+ public interface ICompletionData
+ {
+ /// <summary>
+ /// Gets the image.
+ /// </summary>
+ ImageSource Image { get; }
+
+ /// <summary>
+ /// Gets the text. This property is used to filter the list of visible elements.
+ /// </summary>
+ string Text { get; }
+
+ /// <summary>
+ /// The displayed content. This can be the same as 'Text', or a WPF UIElement if
+ /// you want to display rich content.
+ /// </summary>
+ object Content { get; }
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ object Description { get; }
+
+ /// <summary>
+ /// Gets the priority. This property is used in the selection logic. You can use it to prefer selecting those items
+ /// which the user is accessing most frequently.
+ /// </summary>
+ double Priority { get; }
+
+ /// <summary>
+ /// Perform the completion.
+ /// </summary>
+ /// <param name="textArea">The text area on which completion is performed.</param>
+ /// <param name="completionSegment">The text segment that was used by the completion window if
+ /// the user types (segment between CompletionWindow.StartOffset and CompletionWindow.EndOffset).</param>
+ /// <param name="insertionRequestEventArgs">The EventArgs used for the insertion request.
+ /// These can be TextCompositionEventArgs, KeyEventArgs, MouseEventArgs, depending on how
+ /// the insertion was triggered.</param>
+ void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/IOverloadProvider.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/IOverloadProvider.cs
new file mode 100644
index 000000000..8aaaa8d41
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/IOverloadProvider.cs
@@ -0,0 +1,42 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// Provides the items for the OverloadViewer.
+ /// </summary>
+ public interface IOverloadProvider : INotifyPropertyChanged
+ {
+ /// <summary>
+ /// Gets/Sets the selected index.
+ /// </summary>
+ int SelectedIndex { get; set; }
+
+ /// <summary>
+ /// Gets the number of overloads.
+ /// </summary>
+ int Count { get; }
+
+ /// <summary>
+ /// Gets the text 'SelectedIndex of Count'.
+ /// </summary>
+ string CurrentIndexText { get; }
+
+ /// <summary>
+ /// Gets the current header.
+ /// </summary>
+ object CurrentHeader { get; }
+
+ /// <summary>
+ /// Gets the current content.
+ /// </summary>
+ object CurrentContent { get; }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.cs
new file mode 100644
index 000000000..bea06f3b3
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.cs
@@ -0,0 +1,94 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// A popup-like window that is attached to a text segment.
+ /// </summary>
+ public class InsightWindow : CompletionWindowBase
+ {
+ static InsightWindow()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(InsightWindow),
+ new FrameworkPropertyMetadata(typeof(InsightWindow)));
+ AllowsTransparencyProperty.OverrideMetadata(typeof(InsightWindow),
+ new FrameworkPropertyMetadata(Boxes.True));
+ }
+
+ /// <summary>
+ /// Creates a new InsightWindow.
+ /// </summary>
+ public InsightWindow(TextArea textArea) : base(textArea)
+ {
+ this.CloseAutomatically = true;
+ AttachEvents();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnSourceInitialized(EventArgs e)
+ {
+ base.OnSourceInitialized(e);
+
+ Rect caret = this.TextArea.Caret.CalculateCaretRectangle();
+ Point pointOnScreen = this.TextArea.TextView.PointToScreen(caret.Location - this.TextArea.TextView.ScrollOffset);
+ Rect workingArea = System.Windows.Forms.Screen.FromPoint(pointOnScreen.ToSystemDrawing()).WorkingArea.ToWpf().TransformFromDevice(this);
+
+ MaxHeight = workingArea.Height;
+ MaxWidth = Math.Min(workingArea.Width, Math.Max(1000, workingArea.Width * 0.6));
+ }
+
+ /// <summary>
+ /// Gets/Sets whether the insight window should close automatically.
+ /// The default value is true.
+ /// </summary>
+ public bool CloseAutomatically { get; set; }
+
+ /// <inheritdoc/>
+ protected override bool CloseOnFocusLost {
+ get { return this.CloseAutomatically; }
+ }
+
+ void AttachEvents()
+ {
+ this.TextArea.Caret.PositionChanged += CaretPositionChanged;
+ }
+
+ /// <inheritdoc/>
+ protected override void DetachEvents()
+ {
+ this.TextArea.Caret.PositionChanged -= CaretPositionChanged;
+ base.DetachEvents();
+ }
+
+ void CaretPositionChanged(object sender, EventArgs e)
+ {
+ if (this.CloseAutomatically) {
+ int offset = this.TextArea.Caret.Offset;
+ if (offset < this.StartOffset || offset > this.EndOffset) {
+ Close();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// TemplateSelector for InsightWindow to replace plain string content by a TextBlock with TextWrapping.
+ /// </summary>
+ internal sealed class InsightWindowTemplateSelector : DataTemplateSelector
+ {
+ public override DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ if (item is string)
+ return (DataTemplate)((FrameworkElement)container).FindResource("TextBlockTemplate");
+
+ return null;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.xaml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.xaml
new file mode 100644
index 000000000..6b5a67388
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/InsightWindow.xaml
@@ -0,0 +1,118 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:cc="clr-namespace:Tango.Scripting.Editors.CodeCompletion"
+>
+ <cc:InsightWindowTemplateSelector x:Key="templateSelector" />
+
+ <!-- Template for InsightWindow. Based on the template for ToolTip. -->
+ <Style TargetType="{x:Type cc:InsightWindow}">
+ <Setter Property="SizeToContent" Value="WidthAndHeight" />
+
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" />
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.InfoBrushKey}}" />
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InfoTextBrushKey}}" />
+ <Setter Property="FontFamily" Value="{DynamicResource {x:Static SystemFonts.StatusFontFamilyKey}}" />
+ <Setter Property="FontSize" Value="{DynamicResource {x:Static SystemFonts.StatusFontSizeKey}}" />
+ <Setter Property="FontStyle" Value="{DynamicResource {x:Static SystemFonts.StatusFontStyleKey}}" />
+ <Setter Property="FontWeight" Value="{DynamicResource {x:Static SystemFonts.StatusFontWeightKey}}" />
+ <Setter Property="Padding" Value="1,1,3,1" />
+ <Setter Property="HorizontalContentAlignment" Value="Left" />
+ <Setter Property="VerticalContentAlignment" Value="Center" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type cc:InsightWindow}">
+ <Border BorderThickness="{TemplateBinding Border.BorderThickness}"
+ Padding="{TemplateBinding Control.Padding}"
+ CornerRadius="2,2,2,2"
+ BorderBrush="{TemplateBinding Border.BorderBrush}"
+ Background="{TemplateBinding Panel.Background}">
+ <AdornerDecorator>
+ <ContentPresenter
+ Content="{TemplateBinding ContentControl.Content}"
+ ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
+ ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
+ HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
+ VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
+ SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
+ </AdornerDecorator>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <!-- Template for OverloadViewer. -->
+ <Style TargetType="{x:Type cc:OverloadViewer}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type cc:OverloadViewer}">
+ <Grid>
+ <Grid.Resources>
+ <cc:CollapseIfSingleOverloadConverter x:Key="collapseIfSingleOverloadConverter"/>
+
+ <!-- Style of the UpDownButton -->
+ <Style TargetType="{x:Type Button}" x:Key="upDownButtonStyle">
+ <Style.Setters>
+ <Setter Property="Background" Value="LightGray"/>
+ <Setter Property="Padding" Value="2,2,2,2"/>
+ <Setter Property="Width" Value="9"/>
+ <Setter Property="Height" Value="9"/>
+ <Setter Property="SnapsToDevicePixels" Value="True"/>
+ <Setter Property="OverridesDefaultStyle" Value="True"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Button}">
+ <Border Name="bd"
+ Background="{TemplateBinding Background}" CornerRadius="2">
+ <ContentControl Margin="{TemplateBinding Padding}"
+ Content="{TemplateBinding Content}"/>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter TargetName="bd" Property="Background" Value="LightBlue"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style.Setters>
+ </Style>
+
+ <DataTemplate x:Key="TextBlockTemplate">
+ <TextBlock TextWrapping="Wrap" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
+ </DataTemplate>
+ </Grid.Resources>
+
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="1*"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="1*"/>
+ </Grid.RowDefinitions>
+
+ <StackPanel Grid.Row="0" Grid.Column="0"
+ Margin="0,0,4,0"
+ Orientation="Horizontal"
+ Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Provider.Count, Converter={StaticResource collapseIfSingleOverloadConverter}}">
+ <Button Name="PART_UP" Style="{StaticResource upDownButtonStyle}">
+ <Path Stroke="Black" Fill="Black" Data="M 0,0.866 L 1,0.866 L 0.5,0 Z" Stretch="UniformToFill" />
+ </Button>
+ <TextBlock Margin="2,0,2,0"
+ Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Provider.CurrentIndexText}"/>
+ <Button Name="PART_DOWN" Style="{StaticResource upDownButtonStyle}">
+ <Path Stroke="Black" Fill="Black" Data="M 0,0 L 1,0 L 0.5,0.866 Z" Stretch="UniformToFill" />
+ </Button>
+ </StackPanel>
+ <ContentPresenter Grid.Row="0" Grid.Column="1" ContentTemplateSelector="{StaticResource templateSelector}"
+ Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Provider.CurrentHeader}" />
+ <ContentPresenter Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ContentTemplateSelector="{StaticResource templateSelector}"
+ Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Provider.CurrentContent}" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadInsightWindow.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadInsightWindow.cs
new file mode 100644
index 000000000..5ed165aad
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadInsightWindow.cs
@@ -0,0 +1,58 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Input;
+
+using Tango.Scripting.Editors.Editing;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// Insight window that shows an OverloadViewer.
+ /// </summary>
+ public class OverloadInsightWindow : InsightWindow
+ {
+ OverloadViewer overloadViewer = new OverloadViewer();
+
+ /// <summary>
+ /// Creates a new OverloadInsightWindow.
+ /// </summary>
+ public OverloadInsightWindow(TextArea textArea) : base(textArea)
+ {
+ overloadViewer.Margin = new Thickness(2,0,0,0);
+ this.Content = overloadViewer;
+ }
+
+ /// <summary>
+ /// Gets/Sets the item provider.
+ /// </summary>
+ public IOverloadProvider Provider {
+ get { return overloadViewer.Provider; }
+ set { overloadViewer.Provider = value; }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ if (!e.Handled && this.Provider.Count > 1) {
+ switch (e.Key) {
+ case Key.Up:
+ e.Handled = true;
+ overloadViewer.ChangeIndex(-1);
+ break;
+ case Key.Down:
+ e.Handled = true;
+ overloadViewer.ChangeIndex(+1);
+ break;
+ }
+ if (e.Handled) {
+ UpdateLayout();
+ UpdatePosition();
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadViewer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadViewer.cs
new file mode 100644
index 000000000..f70229a8d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/CodeCompletion/OverloadViewer.cs
@@ -0,0 +1,101 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace Tango.Scripting.Editors.CodeCompletion
+{
+ /// <summary>
+ /// Represents a text between "Up" and "Down" buttons.
+ /// </summary>
+ public class OverloadViewer : Control
+ {
+ static OverloadViewer()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(OverloadViewer),
+ new FrameworkPropertyMetadata(typeof(OverloadViewer)));
+ }
+
+ /// <summary>
+ /// The text property.
+ /// </summary>
+ public static readonly DependencyProperty TextProperty =
+ DependencyProperty.Register("Text", typeof(string), typeof(OverloadViewer));
+
+ /// <summary>
+ /// Gets/Sets the text between the Up and Down buttons.
+ /// </summary>
+ public string Text {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ /// <inheritdoc/>
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ Button upButton = (Button)this.Template.FindName("PART_UP", this);
+ upButton.Click += (sender, e) => {
+ e.Handled = true;
+ ChangeIndex(-1);
+ };
+
+ Button downButton = (Button)this.Template.FindName("PART_DOWN", this);
+ downButton.Click += (sender, e) => {
+ e.Handled = true;
+ ChangeIndex(+1);
+ };
+ }
+
+ /// <summary>
+ /// The ItemProvider property.
+ /// </summary>
+ public static readonly DependencyProperty ProviderProperty =
+ DependencyProperty.Register("Provider", typeof(IOverloadProvider), typeof(OverloadViewer));
+
+ /// <summary>
+ /// Gets/Sets the item provider.
+ /// </summary>
+ public IOverloadProvider Provider {
+ get { return (IOverloadProvider)GetValue(ProviderProperty); }
+ set { SetValue(ProviderProperty, value); }
+ }
+
+ /// <summary>
+ /// Changes the selected index.
+ /// </summary>
+ /// <param name="relativeIndexChange">The relative index change - usual values are +1 or -1.</param>
+ public void ChangeIndex(int relativeIndexChange)
+ {
+ IOverloadProvider p = this.Provider;
+ if (p != null) {
+ int newIndex = p.SelectedIndex + relativeIndexChange;
+ if (newIndex < 0)
+ newIndex = p.Count - 1;
+ if (newIndex >= p.Count)
+ newIndex = 0;
+ p.SelectedIndex = newIndex;
+ }
+ }
+ }
+
+ sealed class CollapseIfSingleOverloadConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return ((int)value < 2) ? Visibility.Collapsed : Visibility.Visible;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ChangeTrackingCheckpoint.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ChangeTrackingCheckpoint.cs
new file mode 100644
index 000000000..283731da8
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ChangeTrackingCheckpoint.cs
@@ -0,0 +1,140 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// <para>A checkpoint that allows tracking changes to a TextDocument.</para>
+ /// <para>
+ /// Use <see cref="TextDocument.CreateSnapshot(out ChangeTrackingCheckpoint)"/> to create a checkpoint.
+ /// </para>
+ /// </summary>
+ /// <remarks>
+ /// <para>The <see cref="ChangeTrackingCheckpoint"/> class allows tracking document changes, even from background threads.</para>
+ /// <para>Once you have two checkpoints, you can call <see cref="ChangeTrackingCheckpoint.GetChangesTo"/> to retrieve the complete list
+ /// of document changes that happened between those versions of the document.</para>
+ /// </remarks>
+ public sealed class ChangeTrackingCheckpoint
+ {
+ // Object that is unique per document.
+ // Used to determine if two checkpoints belong to the same document.
+ // We don't use a reference to the document itself to allow the GC to reclaim the document memory
+ // even if there are still references to checkpoints.
+ readonly object documentIdentifier;
+
+ // 'value' is the change from the previous checkpoint to this checkpoint
+ // TODO: store the change in the older checkpoint instead - if only a reference to the
+ // newest document version exists, the GC should be able to collect all DocumentChangeEventArgs.
+ readonly DocumentChangeEventArgs value;
+ readonly int id;
+ ChangeTrackingCheckpoint next;
+
+ internal ChangeTrackingCheckpoint(object documentIdentifier)
+ {
+ this.documentIdentifier = documentIdentifier;
+ }
+
+ internal ChangeTrackingCheckpoint(object documentIdentifier, DocumentChangeEventArgs value, int id)
+ {
+ this.documentIdentifier = documentIdentifier;
+ this.value = value;
+ this.id = id;
+ }
+
+ internal ChangeTrackingCheckpoint Append(DocumentChangeEventArgs change)
+ {
+ Debug.Assert(this.next == null);
+ this.next = new ChangeTrackingCheckpoint(this.documentIdentifier, change, unchecked( this.id + 1 ));
+ return this.next;
+ }
+
+ /// <summary>
+ /// Creates a change tracking checkpoint for the specified document.
+ /// This method is thread-safe.
+ /// If you need a ChangeTrackingCheckpoint that's consistent with a snapshot of the document,
+ /// use <see cref="TextDocument.CreateSnapshot(out ChangeTrackingCheckpoint)"/>.
+ /// </summary>
+ public static ChangeTrackingCheckpoint Create(TextDocument document)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ return document.CreateChangeTrackingCheckpoint();
+ }
+
+ /// <summary>
+ /// Gets whether this checkpoint belongs to the same document as the other checkpoint.
+ /// </summary>
+ public bool BelongsToSameDocumentAs(ChangeTrackingCheckpoint other)
+ {
+ if (other == null)
+ throw new ArgumentNullException("other");
+ return documentIdentifier == other.documentIdentifier;
+ }
+
+ /// <summary>
+ /// Compares the age of this checkpoint to the other checkpoint.
+ /// </summary>
+ /// <remarks>This method is thread-safe.</remarks>
+ /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
+ /// <returns>-1 if this checkpoint is older than <paramref name="other"/>.
+ /// 0 if <c>this</c>==<paramref name="other"/>.
+ /// 1 if this checkpoint is newer than <paramref name="other"/>.</returns>
+ public int CompareAge(ChangeTrackingCheckpoint other)
+ {
+ if (other == null)
+ throw new ArgumentNullException("other");
+ if (other.documentIdentifier != this.documentIdentifier)
+ throw new ArgumentException("Checkpoints do not belong to the same document.");
+ // We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1.
+ // This is guaranteed on x86 because so many checkpoints don't fit into memory.
+ return Math.Sign(unchecked( this.id - other.id ));
+ }
+
+ /// <summary>
+ /// Gets the changes from this checkpoint to the other checkpoint.
+ /// If 'other' is older than this checkpoint, reverse changes are calculated.
+ /// </summary>
+ /// <remarks>This method is thread-safe.</remarks>
+ /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
+ public IEnumerable<DocumentChangeEventArgs> GetChangesTo(ChangeTrackingCheckpoint other)
+ {
+ int result = CompareAge(other);
+ if (result < 0)
+ return GetForwardChanges(other);
+ else if (result > 0)
+ return other.GetForwardChanges(this).Reverse().Select(change => change.Invert());
+ else
+ return Empty<DocumentChangeEventArgs>.Array;
+ }
+
+ IEnumerable<DocumentChangeEventArgs> GetForwardChanges(ChangeTrackingCheckpoint other)
+ {
+ // Return changes from this(exclusive) to other(inclusive).
+ ChangeTrackingCheckpoint node = this;
+ do {
+ node = node.next;
+ yield return node.value;
+ } while (node != other);
+ }
+
+ /// <summary>
+ /// Calculates where the offset has moved in the other buffer version.
+ /// </summary>
+ /// <remarks>This method is thread-safe.</remarks>
+ /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception>
+ public int MoveOffsetTo(ChangeTrackingCheckpoint other, int oldOffset, AnchorMovementType movement)
+ {
+ int offset = oldOffset;
+ foreach (DocumentChangeEventArgs e in GetChangesTo(other)) {
+ offset = e.GetNewOffset(offset, movement);
+ }
+ return offset;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeEventArgs.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeEventArgs.cs
new file mode 100644
index 000000000..17307680a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeEventArgs.cs
@@ -0,0 +1,131 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Describes a change of the document text.
+ /// This class is thread-safe.
+ /// </summary>
+ [Serializable]
+ public class DocumentChangeEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The offset at which the change occurs.
+ /// </summary>
+ public int Offset { get; private set; }
+
+ /// <summary>
+ /// The text that was removed.
+ /// </summary>
+ public string RemovedText { get; private set; }
+
+ /// <summary>
+ /// The number of characters removed.
+ /// </summary>
+ public int RemovalLength {
+ get { return RemovedText.Length; }
+ }
+
+ /// <summary>
+ /// The text that was inserted.
+ /// </summary>
+ public string InsertedText { get; private set; }
+
+ /// <summary>
+ /// The number of characters inserted.
+ /// </summary>
+ public int InsertionLength {
+ get { return InsertedText.Length; }
+ }
+
+ volatile OffsetChangeMap offsetChangeMap;
+
+ /// <summary>
+ /// Gets the OffsetChangeMap associated with this document change.
+ /// </summary>
+ /// <remarks>The OffsetChangeMap instance is guaranteed to be frozen and thus thread-safe.</remarks>
+ public OffsetChangeMap OffsetChangeMap {
+ get {
+ OffsetChangeMap map = offsetChangeMap;
+ if (map == null) {
+ // create OffsetChangeMap on demand
+ map = OffsetChangeMap.FromSingleElement(CreateSingleChangeMapEntry());
+ offsetChangeMap = map;
+ }
+ return map;
+ }
+ }
+
+ internal OffsetChangeMapEntry CreateSingleChangeMapEntry()
+ {
+ return new OffsetChangeMapEntry(this.Offset, this.RemovalLength, this.InsertionLength);
+ }
+
+ /// <summary>
+ /// Gets the OffsetChangeMap, or null if the default offset map (=single replacement) is being used.
+ /// </summary>
+ internal OffsetChangeMap OffsetChangeMapOrNull {
+ get {
+ return offsetChangeMap;
+ }
+ }
+
+ /// <summary>
+ /// Gets the new offset where the specified offset moves after this document change.
+ /// </summary>
+ public int GetNewOffset(int offset, AnchorMovementType movementType)
+ {
+ if (offsetChangeMap != null)
+ return offsetChangeMap.GetNewOffset(offset, movementType);
+ else
+ return CreateSingleChangeMapEntry().GetNewOffset(offset, movementType);
+ }
+
+ /// <summary>
+ /// Creates a new DocumentChangeEventArgs object.
+ /// </summary>
+ public DocumentChangeEventArgs(int offset, string removedText, string insertedText)
+ : this(offset, removedText, insertedText, null)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new DocumentChangeEventArgs object.
+ /// </summary>
+ public DocumentChangeEventArgs(int offset, string removedText, string insertedText, OffsetChangeMap offsetChangeMap)
+ {
+ ThrowUtil.CheckNotNegative(offset, "offset");
+ ThrowUtil.CheckNotNull(removedText, "removedText");
+ ThrowUtil.CheckNotNull(insertedText, "insertedText");
+
+ this.Offset = offset;
+ this.RemovedText = removedText;
+ this.InsertedText = insertedText;
+
+ if (offsetChangeMap != null) {
+ if (!offsetChangeMap.IsFrozen)
+ throw new ArgumentException("The OffsetChangeMap must be frozen before it can be used in DocumentChangeEventArgs");
+ if (!offsetChangeMap.IsValidForDocumentChange(offset, removedText.Length, insertedText.Length))
+ throw new ArgumentException("OffsetChangeMap is not valid for this document change", "offsetChangeMap");
+ this.offsetChangeMap = offsetChangeMap;
+ }
+ }
+
+ /// <summary>
+ /// Creates DocumentChangeEventArgs for the reverse change.
+ /// </summary>
+ public DocumentChangeEventArgs Invert()
+ {
+ OffsetChangeMap map = this.OffsetChangeMapOrNull;
+ if (map != null) {
+ map = map.Invert();
+ map.Freeze();
+ }
+ return new DocumentChangeEventArgs(this.Offset, this.InsertedText, this.RemovedText, map);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeOperation.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeOperation.cs
new file mode 100644
index 000000000..f24b7ed4d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentChangeOperation.cs
@@ -0,0 +1,52 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Describes a change to a TextDocument.
+ /// </summary>
+ sealed class DocumentChangeOperation : IUndoableOperationWithContext
+ {
+ TextDocument document;
+ DocumentChangeEventArgs change;
+
+ public DocumentChangeOperation(TextDocument document, DocumentChangeEventArgs change)
+ {
+ this.document = document;
+ this.change = change;
+ }
+
+ public void Undo(UndoStack stack)
+ {
+ Debug.Assert(stack.state == UndoStack.StatePlayback);
+ stack.RegisterAffectedDocument(document);
+ stack.state = UndoStack.StatePlaybackModifyDocument;
+ this.Undo();
+ stack.state = UndoStack.StatePlayback;
+ }
+
+ public void Redo(UndoStack stack)
+ {
+ Debug.Assert(stack.state == UndoStack.StatePlayback);
+ stack.RegisterAffectedDocument(document);
+ stack.state = UndoStack.StatePlaybackModifyDocument;
+ this.Redo();
+ stack.state = UndoStack.StatePlayback;
+ }
+
+ public void Undo()
+ {
+ OffsetChangeMap map = change.OffsetChangeMapOrNull;
+ document.Replace(change.Offset, change.InsertionLength, change.RemovedText, map != null ? map.Invert() : null);
+ }
+
+ public void Redo()
+ {
+ document.Replace(change.Offset, change.RemovalLength, change.InsertedText, change.OffsetChangeMapOrNull);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLine.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLine.cs
new file mode 100644
index 000000000..746f31637
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLine.cs
@@ -0,0 +1,242 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Represents a line inside a <see cref="TextDocument"/>.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The <see cref="TextDocument.Lines"/> collection contains one DocumentLine instance
+ /// for every line in the document. This collection is read-only to user code and is automatically
+ /// updated to reflect the current document content.
+ /// </para>
+ /// <para>
+ /// Internally, the DocumentLine instances are arranged in a binary tree that allows for both efficient updates and lookup.
+ /// Converting between offset and line number is possible in O(lg N) time,
+ /// and the data structure also updates all offsets in O(lg N) whenever a line is inserted or removed.
+ /// </para>
+ /// </remarks>
+ public sealed partial class DocumentLine : ISegment
+ {
+ #region Constructor
+ #if DEBUG
+ // Required for thread safety check which is done only in debug builds.
+ // To save space, we don't store the document reference in release builds as we don't need it there.
+ readonly TextDocument document;
+ #endif
+
+ internal bool isDeleted;
+
+ internal DocumentLine(TextDocument document)
+ {
+ #if DEBUG
+ Debug.Assert(document != null);
+ this.document = document;
+ #endif
+ }
+
+ [Conditional("DEBUG")]
+ void DebugVerifyAccess()
+ {
+ #if DEBUG
+ document.DebugVerifyAccess();
+ #endif
+ }
+ #endregion
+
+ #region Events
+// /// <summary>
+// /// Is raised when the line is deleted.
+// /// </summary>
+// public event EventHandler Deleted;
+//
+// /// <summary>
+// /// Is raised when the line's text changes.
+// /// </summary>
+// public event EventHandler TextChanged;
+//
+// /// <summary>
+// /// Raises the Deleted or TextChanged event.
+// /// </summary>
+// internal void RaiseChanged()
+// {
+// if (IsDeleted) {
+// if (Deleted != null)
+// Deleted(this, EventArgs.Empty);
+// } else {
+// if (TextChanged != null)
+// TextChanged(this, EventArgs.Empty);
+// }
+// }
+ #endregion
+
+ #region Properties stored in tree
+ /// <summary>
+ /// Gets if this line was deleted from the document.
+ /// </summary>
+ public bool IsDeleted {
+ get {
+ DebugVerifyAccess();
+ return isDeleted;
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of this line.
+ /// Runtime: O(log n)
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The line was deleted.</exception>
+ public int LineNumber {
+ get {
+ if (IsDeleted)
+ throw new InvalidOperationException();
+ return DocumentLineTree.GetIndexFromNode(this) + 1;
+ }
+ }
+
+ /// <summary>
+ /// Gets the starting offset of the line in the document's text.
+ /// Runtime: O(log n)
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The line was deleted.</exception>
+ public int Offset {
+ get {
+ if (IsDeleted)
+ throw new InvalidOperationException();
+ return DocumentLineTree.GetOffsetFromNode(this);
+ }
+ }
+
+ /// <summary>
+ /// Gets the end offset of the line in the document's text (the offset before the line delimiter).
+ /// Runtime: O(log n)
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The line was deleted.</exception>
+ /// <remarks>EndOffset = <see cref="Offset"/> + <see cref="Length"/>.</remarks>
+ public int EndOffset {
+ get { return this.Offset + this.Length; }
+ }
+ #endregion
+
+ #region Length
+ int totalLength;
+ byte delimiterLength;
+
+ /// <summary>
+ /// Gets the length of this line. The length does not include the line delimiter. O(1)
+ /// </summary>
+ /// <remarks>This property is still available even if the line was deleted;
+ /// in that case, it contains the line's length before the deletion.</remarks>
+ public int Length {
+ get {
+ DebugVerifyAccess();
+ return totalLength - delimiterLength;
+ }
+ }
+
+ /// <summary>
+ /// Gets the length of this line, including the line delimiter. O(1)
+ /// </summary>
+ /// <remarks>This property is still available even if the line was deleted;
+ /// in that case, it contains the line's length before the deletion.</remarks>
+ public int TotalLength {
+ get {
+ DebugVerifyAccess();
+ return totalLength;
+ }
+ internal set {
+ // this is set by DocumentLineTree
+ totalLength = value;
+ }
+ }
+
+ /// <summary>
+ /// <para>Gets the length of the line delimiter.</para>
+ /// <para>The value is 1 for single <c>"\r"</c> or <c>"\n"</c>, 2 for the <c>"\r\n"</c> sequence;
+ /// and 0 for the last line in the document.</para>
+ /// </summary>
+ /// <remarks>This property is still available even if the line was deleted;
+ /// in that case, it contains the line delimiter's length before the deletion.</remarks>
+ public int DelimiterLength {
+ get {
+ DebugVerifyAccess();
+ return delimiterLength;
+ }
+ internal set {
+ Debug.Assert(value >= 0 && value <= 2);
+ delimiterLength = (byte)value;
+ }
+ }
+ #endregion
+
+ #region Previous / Next Line
+ /// <summary>
+ /// Gets the next line in the document.
+ /// </summary>
+ /// <returns>The line following this line, or null if this is the last line.</returns>
+ public DocumentLine NextLine {
+ get {
+ DebugVerifyAccess();
+
+ if (right != null) {
+ return right.LeftMost;
+ } else {
+ DocumentLine node = this;
+ DocumentLine oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // we are on the way up from the right part, don't output node again
+ } while (node != null && node.right == oldNode);
+ return node;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the previous line in the document.
+ /// </summary>
+ /// <returns>The line before this line, or null if this is the first line.</returns>
+ public DocumentLine PreviousLine {
+ get {
+ DebugVerifyAccess();
+
+ if (left != null) {
+ return left.RightMost;
+ } else {
+ DocumentLine node = this;
+ DocumentLine oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // we are on the way up from the left part, don't output node again
+ } while (node != null && node.left == oldNode);
+ return node;
+ }
+ }
+ }
+ #endregion
+
+ #region ToString
+ /// <summary>
+ /// Gets a string with debug output showing the line number and offset.
+ /// Does not include the line's text.
+ /// </summary>
+ public override string ToString()
+ {
+ if (IsDeleted)
+ return "[DocumentLine deleted]";
+ else
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "[DocumentLine Number={0} Offset={1} Length={2}]", LineNumber, Offset, Length);
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLineTree.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLineTree.cs
new file mode 100644
index 000000000..927dc3e01
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/DocumentLineTree.cs
@@ -0,0 +1,712 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Document
+{
+ using LineNode = DocumentLine;
+
+ /// <summary>
+ /// Data structure for efficient management of the document lines (most operations are O(lg n)).
+ /// This implements an augmented red-black tree.
+ /// See <see cref="LineNode"/> for the augmented data.
+ ///
+ /// NOTE: The tree is never empty, initially it contains an empty line.
+ /// </summary>
+ sealed class DocumentLineTree : IList<DocumentLine>
+ {
+ #region Constructor
+ readonly TextDocument document;
+ LineNode root;
+
+ public DocumentLineTree(TextDocument document)
+ {
+ this.document = document;
+
+ DocumentLine emptyLine = new DocumentLine(document);
+ root = emptyLine.InitLineNode();
+ }
+ #endregion
+
+ #region Rotation callbacks
+ internal static void UpdateAfterChildrenChange(LineNode node)
+ {
+ int totalCount = 1;
+ int totalLength = node.TotalLength;
+ if (node.left != null) {
+ totalCount += node.left.nodeTotalCount;
+ totalLength += node.left.nodeTotalLength;
+ }
+ if (node.right != null) {
+ totalCount += node.right.nodeTotalCount;
+ totalLength += node.right.nodeTotalLength;
+ }
+ if (totalCount != node.nodeTotalCount
+ || totalLength != node.nodeTotalLength)
+ {
+ node.nodeTotalCount = totalCount;
+ node.nodeTotalLength = totalLength;
+ if (node.parent != null) UpdateAfterChildrenChange(node.parent);
+ }
+ }
+
+ static void UpdateAfterRotateLeft(LineNode node)
+ {
+ UpdateAfterChildrenChange(node);
+
+ // not required: rotations only happen on insertions/deletions
+ // -> totalCount changes -> the parent is always updated
+ //UpdateAfterChildrenChange(node.parent);
+ }
+
+ static void UpdateAfterRotateRight(LineNode node)
+ {
+ UpdateAfterChildrenChange(node);
+
+ // not required: rotations only happen on insertions/deletions
+ // -> totalCount changes -> the parent is always updated
+ //UpdateAfterChildrenChange(node.parent);
+ }
+ #endregion
+
+ #region RebuildDocument
+ /// <summary>
+ /// Rebuild the tree, in O(n).
+ /// </summary>
+ public void RebuildTree(List<DocumentLine> documentLines)
+ {
+ LineNode[] nodes = new LineNode[documentLines.Count];
+ for (int i = 0; i < documentLines.Count; i++) {
+ DocumentLine ls = documentLines[i];
+ LineNode node = ls.InitLineNode();
+ nodes[i] = node;
+ }
+ Debug.Assert(nodes.Length > 0);
+ // now build the corresponding balanced tree
+ int height = GetTreeHeight(nodes.Length);
+ Debug.WriteLine("DocumentLineTree will have height: " + height);
+ root = BuildTree(nodes, 0, nodes.Length, height);
+ root.color = BLACK;
+ #if DEBUG
+ CheckProperties();
+ #endif
+ }
+
+ internal static int GetTreeHeight(int size)
+ {
+ if (size == 0)
+ return 0;
+ else
+ return GetTreeHeight(size / 2) + 1;
+ }
+
+ /// <summary>
+ /// build a tree from a list of nodes
+ /// </summary>
+ LineNode BuildTree(LineNode[] nodes, int start, int end, int subtreeHeight)
+ {
+ Debug.Assert(start <= end);
+ if (start == end) {
+ return null;
+ }
+ int middle = (start + end) / 2;
+ LineNode node = nodes[middle];
+ node.left = BuildTree(nodes, start, middle, subtreeHeight - 1);
+ node.right = BuildTree(nodes, middle + 1, end, subtreeHeight - 1);
+ if (node.left != null) node.left.parent = node;
+ if (node.right != null) node.right.parent = node;
+ if (subtreeHeight == 1)
+ node.color = RED;
+ UpdateAfterChildrenChange(node);
+ return node;
+ }
+ #endregion
+
+ #region GetNodeBy... / Get...FromNode
+ LineNode GetNodeByIndex(int index)
+ {
+ Debug.Assert(index >= 0);
+ Debug.Assert(index < root.nodeTotalCount);
+ LineNode node = root;
+ while (true) {
+ if (node.left != null && index < node.left.nodeTotalCount) {
+ node = node.left;
+ } else {
+ if (node.left != null) {
+ index -= node.left.nodeTotalCount;
+ }
+ if (index == 0)
+ return node;
+ index--;
+ node = node.right;
+ }
+ }
+ }
+
+ internal static int GetIndexFromNode(LineNode node)
+ {
+ int index = (node.left != null) ? node.left.nodeTotalCount : 0;
+ while (node.parent != null) {
+ if (node == node.parent.right) {
+ if (node.parent.left != null)
+ index += node.parent.left.nodeTotalCount;
+ index++;
+ }
+ node = node.parent;
+ }
+ return index;
+ }
+
+ LineNode GetNodeByOffset(int offset)
+ {
+ Debug.Assert(offset >= 0);
+ Debug.Assert(offset <= root.nodeTotalLength);
+ if (offset == root.nodeTotalLength) {
+ return root.RightMost;
+ }
+ LineNode node = root;
+ while (true) {
+ if (node.left != null && offset < node.left.nodeTotalLength) {
+ node = node.left;
+ } else {
+ if (node.left != null) {
+ offset -= node.left.nodeTotalLength;
+ }
+ offset -= node.TotalLength;
+ if (offset < 0)
+ return node;
+ node = node.right;
+ }
+ }
+ }
+
+ internal static int GetOffsetFromNode(LineNode node)
+ {
+ int offset = (node.left != null) ? node.left.nodeTotalLength : 0;
+ while (node.parent != null) {
+ if (node == node.parent.right) {
+ if (node.parent.left != null)
+ offset += node.parent.left.nodeTotalLength;
+ offset += node.parent.TotalLength;
+ }
+ node = node.parent;
+ }
+ return offset;
+ }
+ #endregion
+
+ #region GetLineBy
+ public DocumentLine GetByNumber(int number)
+ {
+ return GetNodeByIndex(number - 1);
+ }
+
+ public DocumentLine GetByOffset(int offset)
+ {
+ return GetNodeByOffset(offset);
+ }
+ #endregion
+
+ #region LineCount
+ public int LineCount {
+ get {
+ return root.nodeTotalCount;
+ }
+ }
+ #endregion
+
+ #region CheckProperties
+ #if DEBUG
+ [Conditional("DATACONSISTENCYTEST")]
+ internal void CheckProperties()
+ {
+ Debug.Assert(root.nodeTotalLength == document.TextLength);
+ CheckProperties(root);
+
+ // check red-black property:
+ int blackCount = -1;
+ CheckNodeProperties(root, null, RED, 0, ref blackCount);
+ }
+
+ void CheckProperties(LineNode node)
+ {
+ int totalCount = 1;
+ int totalLength = node.TotalLength;
+ if (node.left != null) {
+ CheckProperties(node.left);
+ totalCount += node.left.nodeTotalCount;
+ totalLength += node.left.nodeTotalLength;
+ }
+ if (node.right != null) {
+ CheckProperties(node.right);
+ totalCount += node.right.nodeTotalCount;
+ totalLength += node.right.nodeTotalLength;
+ }
+ Debug.Assert(node.nodeTotalCount == totalCount);
+ Debug.Assert(node.nodeTotalLength == totalLength);
+ }
+
+ /*
+ 1. A node is either red or black.
+ 2. The root is black.
+ 3. All leaves are black. (The leaves are the NIL children.)
+ 4. Both children of every red node are black. (So every red node must have a black parent.)
+ 5. Every simple path from a node to a descendant leaf contains the same number of black nodes. (Not counting the leaf node.)
+ */
+ void CheckNodeProperties(LineNode node, LineNode parentNode, bool parentColor, int blackCount, ref int expectedBlackCount)
+ {
+ if (node == null) return;
+
+ Debug.Assert(node.parent == parentNode);
+
+ if (parentColor == RED) {
+ Debug.Assert(node.color == BLACK);
+ }
+ if (node.color == BLACK) {
+ blackCount++;
+ }
+ if (node.left == null && node.right == null) {
+ // node is a leaf node:
+ if (expectedBlackCount == -1)
+ expectedBlackCount = blackCount;
+ else
+ Debug.Assert(expectedBlackCount == blackCount);
+ }
+ CheckNodeProperties(node.left, node, node.color, blackCount, ref expectedBlackCount);
+ CheckNodeProperties(node.right, node, node.color, blackCount, ref expectedBlackCount);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public string GetTreeAsString()
+ {
+ StringBuilder b = new StringBuilder();
+ AppendTreeToString(root, b, 0);
+ return b.ToString();
+ }
+
+ static void AppendTreeToString(LineNode node, StringBuilder b, int indent)
+ {
+ if (node.color == RED)
+ b.Append("RED ");
+ else
+ b.Append("BLACK ");
+ b.AppendLine(node.ToString());
+ indent += 2;
+ if (node.left != null) {
+ b.Append(' ', indent);
+ b.Append("L: ");
+ AppendTreeToString(node.left, b, indent);
+ }
+ if (node.right != null) {
+ b.Append(' ', indent);
+ b.Append("R: ");
+ AppendTreeToString(node.right, b, indent);
+ }
+ }
+ #endif
+ #endregion
+
+ #region Insert/Remove lines
+ public void RemoveLine(DocumentLine line)
+ {
+ RemoveNode(line);
+ line.isDeleted = true;
+ }
+
+ public DocumentLine InsertLineAfter(DocumentLine line, int totalLength)
+ {
+ DocumentLine newLine = new DocumentLine(document);
+ newLine.TotalLength = totalLength;
+
+ InsertAfter(line, newLine);
+ return newLine;
+ }
+
+ void InsertAfter(LineNode node, DocumentLine newLine)
+ {
+ LineNode newNode = newLine.InitLineNode();
+ if (node.right == null) {
+ InsertAsRight(node, newNode);
+ } else {
+ InsertAsLeft(node.right.LeftMost, newNode);
+ }
+ }
+ #endregion
+
+ #region Red/Black Tree
+ internal const bool RED = true;
+ internal const bool BLACK = false;
+
+ void InsertAsLeft(LineNode parentNode, LineNode newNode)
+ {
+ Debug.Assert(parentNode.left == null);
+ parentNode.left = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAfterChildrenChange(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void InsertAsRight(LineNode parentNode, LineNode newNode)
+ {
+ Debug.Assert(parentNode.right == null);
+ parentNode.right = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAfterChildrenChange(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void FixTreeOnInsert(LineNode node)
+ {
+ Debug.Assert(node != null);
+ Debug.Assert(node.color == RED);
+ Debug.Assert(node.left == null || node.left.color == BLACK);
+ Debug.Assert(node.right == null || node.right.color == BLACK);
+
+ LineNode parentNode = node.parent;
+ if (parentNode == null) {
+ // we inserted in the root -> the node must be black
+ // since this is a root node, making the node black increments the number of black nodes
+ // on all paths by one, so it is still the same for all paths.
+ node.color = BLACK;
+ return;
+ }
+ if (parentNode.color == BLACK) {
+ // if the parent node where we inserted was black, our red node is placed correctly.
+ // since we inserted a red node, the number of black nodes on each path is unchanged
+ // -> the tree is still balanced
+ return;
+ }
+ // parentNode is red, so there is a conflict here!
+
+ // because the root is black, parentNode is not the root -> there is a grandparent node
+ LineNode grandparentNode = parentNode.parent;
+ LineNode uncleNode = Sibling(parentNode);
+ if (uncleNode != null && uncleNode.color == RED) {
+ parentNode.color = BLACK;
+ uncleNode.color = BLACK;
+ grandparentNode.color = RED;
+ FixTreeOnInsert(grandparentNode);
+ return;
+ }
+ // now we know: parent is red but uncle is black
+ // First rotation:
+ if (node == parentNode.right && parentNode == grandparentNode.left) {
+ RotateLeft(parentNode);
+ node = node.left;
+ } else if (node == parentNode.left && parentNode == grandparentNode.right) {
+ RotateRight(parentNode);
+ node = node.right;
+ }
+ // because node might have changed, reassign variables:
+ parentNode = node.parent;
+ grandparentNode = parentNode.parent;
+
+ // Now recolor a bit:
+ parentNode.color = BLACK;
+ grandparentNode.color = RED;
+ // Second rotation:
+ if (node == parentNode.left && parentNode == grandparentNode.left) {
+ RotateRight(grandparentNode);
+ } else {
+ // because of the first rotation, this is guaranteed:
+ Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right);
+ RotateLeft(grandparentNode);
+ }
+ }
+
+ void RemoveNode(LineNode removedNode)
+ {
+ if (removedNode.left != null && removedNode.right != null) {
+ // replace removedNode with it's in-order successor
+
+ LineNode leftMost = removedNode.right.LeftMost;
+ RemoveNode(leftMost); // remove leftMost from its current location
+
+ // and overwrite the removedNode with it
+ ReplaceNode(removedNode, leftMost);
+ leftMost.left = removedNode.left;
+ if (leftMost.left != null) leftMost.left.parent = leftMost;
+ leftMost.right = removedNode.right;
+ if (leftMost.right != null) leftMost.right.parent = leftMost;
+ leftMost.color = removedNode.color;
+
+ UpdateAfterChildrenChange(leftMost);
+ if (leftMost.parent != null) UpdateAfterChildrenChange(leftMost.parent);
+ return;
+ }
+
+ // now either removedNode.left or removedNode.right is null
+ // get the remaining child
+ LineNode parentNode = removedNode.parent;
+ LineNode childNode = removedNode.left ?? removedNode.right;
+ ReplaceNode(removedNode, childNode);
+ if (parentNode != null) UpdateAfterChildrenChange(parentNode);
+ if (removedNode.color == BLACK) {
+ if (childNode != null && childNode.color == RED) {
+ childNode.color = BLACK;
+ } else {
+ FixTreeOnDelete(childNode, parentNode);
+ }
+ }
+ }
+
+ void FixTreeOnDelete(LineNode node, LineNode parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (parentNode == null)
+ return;
+
+ // warning: node may be null
+ LineNode sibling = Sibling(node, parentNode);
+ if (sibling.color == RED) {
+ parentNode.color = RED;
+ sibling.color = BLACK;
+ if (node == parentNode.left) {
+ RotateLeft(parentNode);
+ } else {
+ RotateRight(parentNode);
+ }
+
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+ }
+
+ if (parentNode.color == BLACK
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ FixTreeOnDelete(parentNode, parentNode.parent);
+ return;
+ }
+
+ if (parentNode.color == RED
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ parentNode.color = BLACK;
+ return;
+ }
+
+ if (node == parentNode.left &&
+ sibling.color == BLACK &&
+ GetColor(sibling.left) == RED &&
+ GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.left.color = BLACK;
+ RotateRight(sibling);
+ }
+ else if (node == parentNode.right &&
+ sibling.color == BLACK &&
+ GetColor(sibling.right) == RED &&
+ GetColor(sibling.left) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.right.color = BLACK;
+ RotateLeft(sibling);
+ }
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+
+ sibling.color = parentNode.color;
+ parentNode.color = BLACK;
+ if (node == parentNode.left) {
+ if (sibling.right != null) {
+ Debug.Assert(sibling.right.color == RED);
+ sibling.right.color = BLACK;
+ }
+ RotateLeft(parentNode);
+ } else {
+ if (sibling.left != null) {
+ Debug.Assert(sibling.left.color == RED);
+ sibling.left.color = BLACK;
+ }
+ RotateRight(parentNode);
+ }
+ }
+
+ void ReplaceNode(LineNode replacedNode, LineNode newNode)
+ {
+ if (replacedNode.parent == null) {
+ Debug.Assert(replacedNode == root);
+ root = newNode;
+ } else {
+ if (replacedNode.parent.left == replacedNode)
+ replacedNode.parent.left = newNode;
+ else
+ replacedNode.parent.right = newNode;
+ }
+ if (newNode != null) {
+ newNode.parent = replacedNode.parent;
+ }
+ replacedNode.parent = null;
+ }
+
+ void RotateLeft(LineNode p)
+ {
+ // let q be p's right child
+ LineNode q = p.right;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's right child to be q's left child
+ p.right = q.left;
+ if (p.right != null) p.right.parent = p;
+ // set q's left child to be p
+ q.left = p;
+ p.parent = q;
+ UpdateAfterRotateLeft(p);
+ }
+
+ void RotateRight(LineNode p)
+ {
+ // let q be p's left child
+ LineNode q = p.left;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's left child to be q's right child
+ p.left = q.right;
+ if (p.left != null) p.left.parent = p;
+ // set q's right child to be p
+ q.right = p;
+ p.parent = q;
+ UpdateAfterRotateRight(p);
+ }
+
+ static LineNode Sibling(LineNode node)
+ {
+ if (node == node.parent.left)
+ return node.parent.right;
+ else
+ return node.parent.left;
+ }
+
+ static LineNode Sibling(LineNode node, LineNode parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (node == parentNode.left)
+ return parentNode.right;
+ else
+ return parentNode.left;
+ }
+
+ static bool GetColor(LineNode node)
+ {
+ return node != null ? node.color : BLACK;
+ }
+ #endregion
+
+ #region IList implementation
+ DocumentLine IList<DocumentLine>.this[int index] {
+ get {
+ document.VerifyAccess();
+ return GetByNumber(1 + index);
+ }
+ set {
+ throw new NotSupportedException();
+ }
+ }
+
+ int ICollection<DocumentLine>.Count {
+ get {
+ document.VerifyAccess();
+ return LineCount;
+ }
+ }
+
+ bool ICollection<DocumentLine>.IsReadOnly {
+ get { return true; }
+ }
+
+ int IList<DocumentLine>.IndexOf(DocumentLine item)
+ {
+ document.VerifyAccess();
+ if (item == null || item.IsDeleted)
+ return -1;
+ int index = item.LineNumber - 1;
+ if (index < LineCount && GetNodeByIndex(index) == item)
+ return index;
+ else
+ return -1;
+ }
+
+ void IList<DocumentLine>.Insert(int index, DocumentLine item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList<DocumentLine>.RemoveAt(int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ void ICollection<DocumentLine>.Add(DocumentLine item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void ICollection<DocumentLine>.Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ bool ICollection<DocumentLine>.Contains(DocumentLine item)
+ {
+ IList<DocumentLine> self = this;
+ return self.IndexOf(item) >= 0;
+ }
+
+ void ICollection<DocumentLine>.CopyTo(DocumentLine[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (array.Length < LineCount)
+ throw new ArgumentException("The array is too small", "array");
+ if (arrayIndex < 0 || arrayIndex + LineCount > array.Length)
+ throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, "Value must be between 0 and " + (array.Length - LineCount));
+ foreach (DocumentLine ls in this) {
+ array[arrayIndex++] = ls;
+ }
+ }
+
+ bool ICollection<DocumentLine>.Remove(DocumentLine item)
+ {
+ throw new NotSupportedException();
+ }
+
+ public IEnumerator<DocumentLine> GetEnumerator()
+ {
+ document.VerifyAccess();
+ return Enumerate();
+ }
+
+ IEnumerator<DocumentLine> Enumerate()
+ {
+ document.VerifyAccess();
+ DocumentLine line = root.LeftMost;
+ while (line != null) {
+ yield return line;
+ line = line.NextLine;
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/GapTextBuffer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/GapTextBuffer.cs
new file mode 100644
index 000000000..a617284a9
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/GapTextBuffer.cs
@@ -0,0 +1,192 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Text;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /*
+ /// <summary>
+ /// Implementation of a gap text buffer.
+ /// </summary>
+ sealed class GapTextBuffer
+ {
+ char[] buffer = Empty<char>.Array;
+
+ /// <summary>
+ /// The current text content.
+ /// Is set to null whenever the buffer changes, and gets a value only when the
+ /// full text content is requested.
+ /// </summary>
+ string textContent;
+
+ /// <summary>
+ /// last GetText result
+ /// </summary>
+ string lastGetTextResult;
+ int lastGetTextRequestOffset;
+
+ int gapBeginOffset;
+ int gapEndOffset;
+ int gapLength; // gapLength == gapEndOffset - gapBeginOffset
+
+ /// <summary>
+ /// when gap is too small for inserted text or gap is too large (exceeds maxGapLength),
+ /// a new buffer is reallocated with a new gap of at least this size.
+ /// </summary>
+ const int minGapLength = 128;
+
+ /// <summary>
+ /// when the gap exceeds this size, reallocate a smaller buffer
+ /// </summary>
+ const int maxGapLength = 4096;
+
+ public int Length {
+ get {
+ return buffer.Length - gapLength;
+ }
+ }
+
+ /// <summary>
+ /// Gets the buffer content.
+ /// </summary>
+ public string Text {
+ get {
+ if (textContent == null)
+ textContent = GetText(0, Length);
+ return textContent;
+ }
+ set {
+ Debug.Assert(value != null);
+ textContent = value; lastGetTextResult = null;
+ buffer = new char[value.Length + minGapLength];
+ value.CopyTo(0, buffer, 0, value.Length);
+ gapBeginOffset = value.Length;
+ gapEndOffset = buffer.Length;
+ gapLength = gapEndOffset - gapBeginOffset;
+ }
+ }
+
+ public char GetCharAt(int offset)
+ {
+ return offset < gapBeginOffset ? buffer[offset] : buffer[offset + gapLength];
+ }
+
+ public string GetText(int offset, int length)
+ {
+ if (length == 0)
+ return string.Empty;
+ if (lastGetTextRequestOffset == offset && lastGetTextResult != null && length == lastGetTextResult.Length)
+ return lastGetTextResult;
+
+ int end = offset + length;
+ string result;
+ if (end < gapBeginOffset) {
+ result = new string(buffer, offset, length);
+ } else if (offset > gapBeginOffset) {
+ result = new string(buffer, offset + gapLength, length);
+ } else {
+ int block1Size = gapBeginOffset - offset;
+ int block2Size = end - gapBeginOffset;
+
+ StringBuilder buf = new StringBuilder(block1Size + block2Size);
+ buf.Append(buffer, offset, block1Size);
+ buf.Append(buffer, gapEndOffset, block2Size);
+ result = buf.ToString();
+ }
+ lastGetTextRequestOffset = offset;
+ lastGetTextResult = result;
+ return result;
+ }
+
+ /// <summary>
+ /// Inserts text at the specified offset.
+ /// </summary>
+ public void Insert(int offset, string text)
+ {
+ Debug.Assert(offset >= 0 && offset <= Length);
+
+ if (text.Length == 0)
+ return;
+
+ textContent = null; lastGetTextResult = null;
+ PlaceGap(offset, text.Length);
+ text.CopyTo(0, buffer, gapBeginOffset, text.Length);
+ gapBeginOffset += text.Length;
+ gapLength = gapEndOffset - gapBeginOffset;
+ }
+
+ /// <summary>
+ /// Remove <paramref name="length"/> characters at <paramref name="offset"/>.
+ /// Leave a gap of at least <paramref name="reserveGapSize"/>.
+ /// </summary>
+ public void Remove(int offset, int length, int reserveGapSize)
+ {
+ Debug.Assert(offset >= 0 && offset <= Length);
+ Debug.Assert(length >= 0 && offset + length <= Length);
+ Debug.Assert(reserveGapSize >= 0);
+
+ if (length == 0)
+ return;
+
+ textContent = null; lastGetTextResult = null;
+ PlaceGap(offset, reserveGapSize - length);
+ gapEndOffset += length; // delete removed text
+ gapLength = gapEndOffset - gapBeginOffset;
+ if (gapLength - reserveGapSize > maxGapLength && gapLength - reserveGapSize > buffer.Length / 4) {
+ // shrink gap
+ MakeNewBuffer(gapBeginOffset, reserveGapSize + minGapLength);
+ }
+ }
+
+ void PlaceGap(int newGapOffset, int minRequiredGapLength)
+ {
+ if (gapLength < minRequiredGapLength) {
+ // enlarge gap
+ MakeNewBuffer(newGapOffset, minRequiredGapLength + Math.Max(minGapLength, buffer.Length / 8));
+ } else {
+ while (newGapOffset < gapBeginOffset) {
+ buffer[--gapEndOffset] = buffer[--gapBeginOffset];
+ }
+ while (newGapOffset > gapBeginOffset) {
+ buffer[gapBeginOffset++] = buffer[gapEndOffset++];
+ }
+ }
+ }
+
+ void MakeNewBuffer(int newGapOffset, int newGapLength)
+ {
+ char[] newBuffer = new char[Length + newGapLength];
+ Debug.WriteLine("GapTextBuffer was reallocated, new size=" + newBuffer.Length);
+ if (newGapOffset < gapBeginOffset) {
+ // gap is moving backwards
+
+ // first part:
+ Array.Copy(buffer, 0, newBuffer, 0, newGapOffset);
+ // moving middle part:
+ Array.Copy(buffer, newGapOffset, newBuffer, newGapOffset + newGapLength, gapBeginOffset - newGapOffset);
+ // last part:
+ Array.Copy(buffer, gapEndOffset, newBuffer, newBuffer.Length - (buffer.Length - gapEndOffset), buffer.Length - gapEndOffset);
+ } else {
+ // gap is moving forwards
+ // first part:
+ Array.Copy(buffer, 0, newBuffer, 0, gapBeginOffset);
+ // moving middle part:
+ Array.Copy(buffer, gapEndOffset, newBuffer, gapBeginOffset, newGapOffset - gapBeginOffset);
+ // last part:
+ int lastPartLength = newBuffer.Length - (newGapOffset + newGapLength);
+ Array.Copy(buffer, buffer.Length - lastPartLength, newBuffer, newGapOffset + newGapLength, lastPartLength);
+ }
+
+ gapBeginOffset = newGapOffset;
+ gapEndOffset = newGapOffset + newGapLength;
+ gapLength = newGapLength;
+ buffer = newBuffer;
+ }
+ }
+ */
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ILineTracker.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ILineTracker.cs
new file mode 100644
index 000000000..c16128d63
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ILineTracker.cs
@@ -0,0 +1,55 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Allows for low-level line tracking.
+ /// </summary>
+ /// <remarks>
+ /// The methods on this interface are called by the TextDocument's LineManager immediately after the document
+ /// has changed, *while* the DocumentLineTree is updating.
+ /// Thus, the DocumentLineTree may be in an invalid state when these methods are called.
+ /// This interface should only be used to update per-line data structures like the HeightTree.
+ /// Line trackers must not cause any events to be raised during an update to prevent other code from seeing
+ /// the invalid state.
+ /// Line trackers may be called while the TextDocument has taken a lock.
+ /// You must be careful not to dead-lock inside ILineTracker callbacks.
+ /// </remarks>
+ public interface ILineTracker
+ {
+ /// <summary>
+ /// Is called immediately before a document line is removed.
+ /// </summary>
+ void BeforeRemoveLine(DocumentLine line);
+
+// /// <summary>
+// /// Is called immediately after a document line is removed.
+// /// </summary>
+// void AfterRemoveLine(DocumentLine line);
+
+ /// <summary>
+ /// Is called immediately before a document line changes length.
+ /// This method will be called whenever the line is changed, even when the length stays as it is.
+ /// The method might be called multiple times for a single line because
+ /// a replacement is internally handled as removal followed by insertion.
+ /// </summary>
+ void SetLineLength(DocumentLine line, int newTotalLength);
+
+ /// <summary>
+ /// Is called immediately after a line was inserted.
+ /// </summary>
+ /// <param name="newLine">The new line</param>
+ /// <param name="insertionPos">The existing line before the new line</param>
+ void LineInserted(DocumentLine insertionPos, DocumentLine newLine);
+
+ /// <summary>
+ /// Indicates that there were changes to the document that the line tracker was not notified of.
+ /// The document is in a consistent state (but the line trackers aren't), and line trackers should
+ /// throw away their data and rebuild the document.
+ /// </summary>
+ void RebuildDocument();
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ISegment.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ISegment.cs
new file mode 100644
index 000000000..80eb63352
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ISegment.cs
@@ -0,0 +1,219 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using Tango.Scripting.Editors.Utils;
+using System.Globalization;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// An (Offset,Length)-pair.
+ /// </summary>
+ /// <seealso cref="TextSegment"/>
+ /// <seealso cref="AnchorSegment"/>
+ public interface ISegment
+ {
+ /// <summary>
+ /// Gets the start offset of the segment.
+ /// </summary>
+ int Offset { get; }
+
+ /// <summary>
+ /// Gets the length of the segment.
+ /// </summary>
+ /// <remarks>Must not be negative.</remarks>
+ int Length { get; }
+
+ /// <summary>
+ /// Gets the end offset of the segment.
+ /// </summary>
+ /// <remarks>EndOffset = Offset + Length;</remarks>
+ int EndOffset { get; }
+ }
+
+ static class SegmentExtensions
+ {
+ /// <summary>
+ /// Gets whether the segment contains the offset.
+ /// </summary>
+ /// <returns>
+ /// True, if offset is between segment.Start and segment.End (inclusive); otherwise, false.
+ /// </returns>
+ public static bool Contains(this ISegment segment, int offset)
+ {
+ int start = segment.Offset;
+ int end = start + segment.Length;
+ return offset >= start && offset <= end;
+ }
+
+ /// <summary>
+ /// Gets the overlapping portion of the segments.
+ /// Returns SimpleSegment.Invalid if the segments don't overlap.
+ /// </summary>
+ public static SimpleSegment GetOverlap(this ISegment segment, ISegment other)
+ {
+ int start = Math.Max(segment.Offset, other.Offset);
+ int end = Math.Min(segment.EndOffset, other.EndOffset);
+ if (end < start)
+ return SimpleSegment.Invalid;
+ else
+ return new SimpleSegment(start, end - start);
+ }
+ }
+
+ /// <summary>
+ /// Represents a simple segment (Offset,Length pair) that is not automatically updated
+ /// on document changes.
+ /// </summary>
+ struct SimpleSegment : IEquatable<SimpleSegment>, ISegment
+ {
+ public static readonly SimpleSegment Invalid = new SimpleSegment(-1, -1);
+
+ public readonly int Offset, Length;
+
+ int ISegment.Offset {
+ get { return Offset; }
+ }
+
+ int ISegment.Length {
+ get { return Length; }
+ }
+
+ public int EndOffset {
+ get {
+ return Offset + Length;
+ }
+ }
+
+ public SimpleSegment(int offset, int length)
+ {
+ this.Offset = offset;
+ this.Length = length;
+ }
+
+ public SimpleSegment(ISegment segment)
+ {
+ Debug.Assert(segment != null);
+ this.Offset = segment.Offset;
+ this.Length = segment.Length;
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked {
+ return Offset + 10301 * Length;
+ }
+ }
+
+ public override bool Equals(object obj)
+ {
+ return (obj is SimpleSegment) && Equals((SimpleSegment)obj);
+ }
+
+ public bool Equals(SimpleSegment other)
+ {
+ return this.Offset == other.Offset && this.Length == other.Length;
+ }
+
+ public static bool operator ==(SimpleSegment left, SimpleSegment right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(SimpleSegment left, SimpleSegment right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override string ToString()
+ {
+ return "[Offset=" + Offset.ToString(CultureInfo.InvariantCulture) + ", Length=" + Length.ToString(CultureInfo.InvariantCulture) + "]";
+ }
+ }
+
+ /// <summary>
+ /// A segment using <see cref="TextAnchor"/>s as start and end positions.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// For the constructors creating new anchors, the start position will be AfterInsertion and the end position will be BeforeInsertion.
+ /// Should the end position move before the start position, the segment will have length 0.
+ /// </para>
+ /// </remarks>
+ /// <seealso cref="ISegment"/>
+ /// <seealso cref="TextSegment"/>
+ public sealed class AnchorSegment : ISegment
+ {
+ readonly TextAnchor start, end;
+
+ /// <inheritdoc/>
+ public int Offset {
+ get { return start.Offset; }
+ }
+
+ /// <inheritdoc/>
+ public int Length {
+ get {
+ // Math.Max takes care of the fact that end.Offset might move before start.Offset.
+ return Math.Max(0, end.Offset - start.Offset);
+ }
+ }
+
+ /// <inheritdoc/>
+ public int EndOffset {
+ get {
+ // Math.Max takes care of the fact that end.Offset might move before start.Offset.
+ return Math.Max(start.Offset, end.Offset);
+ }
+ }
+
+ /// <summary>
+ /// Creates a new AnchorSegment using the specified anchors.
+ /// The anchors must have <see cref="TextAnchor.SurviveDeletion"/> set to true.
+ /// </summary>
+ public AnchorSegment(TextAnchor start, TextAnchor end)
+ {
+ if (start == null)
+ throw new ArgumentNullException("start");
+ if (end == null)
+ throw new ArgumentNullException("end");
+ if (!start.SurviveDeletion)
+ throw new ArgumentException("Anchors for AnchorSegment must use SurviveDeletion", "start");
+ if (!end.SurviveDeletion)
+ throw new ArgumentException("Anchors for AnchorSegment must use SurviveDeletion", "end");
+ this.start = start;
+ this.end = end;
+ }
+
+ /// <summary>
+ /// Creates a new AnchorSegment that creates new anchors.
+ /// </summary>
+ public AnchorSegment(TextDocument document, ISegment segment)
+ : this(document, ThrowUtil.CheckNotNull(segment, "segment").Offset, segment.Length)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new AnchorSegment that creates new anchors.
+ /// </summary>
+ public AnchorSegment(TextDocument document, int offset, int length)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ this.start = document.CreateAnchor(offset);
+ this.start.SurviveDeletion = true;
+ this.start.MovementType = AnchorMovementType.AfterInsertion;
+ this.end = document.CreateAnchor(offset + length);
+ this.end.SurviveDeletion = true;
+ this.end.MovementType = AnchorMovementType.BeforeInsertion;
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[Offset=" + Offset.ToString(CultureInfo.InvariantCulture) + ", EndOffset=" + EndOffset.ToString(CultureInfo.InvariantCulture) + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ITextSource.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ITextSource.cs
new file mode 100644
index 000000000..c52a333c5
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/ITextSource.cs
@@ -0,0 +1,320 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+using System.IO;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Interface for read-only access to a text source.
+ /// </summary>
+ /// <seealso cref="TextDocument"/>
+ /// <seealso cref="StringTextSource"/>
+ public interface ITextSource
+ {
+ /// <summary>
+ /// Gets the whole text as string.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
+ string Text { get; }
+
+ /// <summary>
+ /// Is raised when the Text property changes.
+ /// </summary>
+ event EventHandler TextChanged;
+
+ /// <summary>
+ /// Gets the total text length.
+ /// </summary>
+ /// <returns>The length of the text, in characters.</returns>
+ /// <remarks>This is the same as Text.Length, but is more efficient because
+ /// it doesn't require creating a String object.</remarks>
+ int TextLength { get; }
+
+ /// <summary>
+ /// Gets a character at the specified position in the document.
+ /// </summary>
+ /// <paramref name="offset">The index of the character to get.</paramref>
+ /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception>
+ /// <returns>The character at the specified position.</returns>
+ /// <remarks>This is the same as Text[offset], but is more efficient because
+ /// it doesn't require creating a String object.</remarks>
+ char GetCharAt(int offset);
+
+ /// <summary>
+ /// Gets the index of the first occurrence of any character in the specified array.
+ /// </summary>
+ /// <param name="anyOf"></param>
+ /// <param name="startIndex">Start index of the search.</param>
+ /// <param name="count">Length of the area to search.</param>
+ /// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns>
+ int IndexOfAny(char[] anyOf, int startIndex, int count);
+
+ /// <summary>
+ /// Retrieves the text for a portion of the document.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ /// <remarks>This is the same as Text.Substring, but is more efficient because
+ /// it doesn't require creating a String object for the whole document.</remarks>
+ string GetText(int offset, int length);
+
+ /// <summary>
+ /// Creates a snapshot of the current text.
+ /// </summary>
+ /// <remarks>
+ /// This method is generally not thread-safe when called on a mutable text buffer, but the resulting text buffer is immutable and thread-safe.
+ /// However, some implementing classes may provide additional thread-safety guarantees, see <see cref="TextDocument.CreateSnapshot()">TextDocument.CreateSnapshot</see>.
+ /// </remarks>
+ ITextSource CreateSnapshot();
+
+ /// <summary>
+ /// Creates a snapshot of a part of the current text.
+ /// </summary>
+ /// <remarks>
+ /// This method is generally not thread-safe when called on a mutable text buffer, but the resulting text buffer is immutable and thread-safe.
+ /// However, some implementing classes may provide additional thread-safety guarantees, see <see cref="TextDocument.CreateSnapshot()">TextDocument.CreateSnapshot</see>.
+ /// </remarks>
+ ITextSource CreateSnapshot(int offset, int length);
+
+ /// <summary>
+ /// Creates a text reader.
+ /// If the text is changed while a reader is active, the reader will continue to read from the old text version.
+ /// </summary>
+ TextReader CreateReader();
+ }
+
+ /// <summary>
+ /// Implements the ITextSource interface by wrapping another TextSource
+ /// and viewing only a part of the text.
+ /// </summary>
+ [Obsolete("This class will be removed in a future version of AvalonEdit")]
+ public sealed class TextSourceView : ITextSource
+ {
+ readonly ITextSource baseTextSource;
+ readonly ISegment viewedSegment;
+
+ /// <summary>
+ /// Creates a new TextSourceView object.
+ /// </summary>
+ /// <param name="baseTextSource">The base text source.</param>
+ /// <param name="viewedSegment">A text segment from the base text source</param>
+ public TextSourceView(ITextSource baseTextSource, ISegment viewedSegment)
+ {
+ if (baseTextSource == null)
+ throw new ArgumentNullException("baseTextSource");
+ if (viewedSegment == null)
+ throw new ArgumentNullException("viewedSegment");
+ this.baseTextSource = baseTextSource;
+ this.viewedSegment = viewedSegment;
+ }
+
+ /// <inheritdoc/>
+ public event EventHandler TextChanged {
+ add { baseTextSource.TextChanged += value; }
+ remove { baseTextSource.TextChanged -= value; }
+ }
+
+ /// <inheritdoc/>
+ public string Text {
+ get {
+ return baseTextSource.GetText(viewedSegment.Offset, viewedSegment.Length);
+ }
+ }
+
+ /// <inheritdoc/>
+ public int TextLength {
+ get { return viewedSegment.Length; }
+ }
+
+ /// <inheritdoc/>
+ public char GetCharAt(int offset)
+ {
+ return baseTextSource.GetCharAt(viewedSegment.Offset + offset);
+ }
+
+ /// <inheritdoc/>
+ public string GetText(int offset, int length)
+ {
+ return baseTextSource.GetText(viewedSegment.Offset + offset, length);
+ }
+
+ /// <inheritdoc/>
+ public ITextSource CreateSnapshot()
+ {
+ return baseTextSource.CreateSnapshot(viewedSegment.Offset, viewedSegment.Length);
+ }
+
+ /// <inheritdoc/>
+ public ITextSource CreateSnapshot(int offset, int length)
+ {
+ return baseTextSource.CreateSnapshot(viewedSegment.Offset + offset, length);
+ }
+
+ /// <inheritdoc/>
+ public TextReader CreateReader()
+ {
+ return CreateSnapshot().CreateReader();
+ }
+
+ /// <inheritdoc/>
+ public int IndexOfAny(char[] anyOf, int startIndex, int count)
+ {
+ int offset = viewedSegment.Offset;
+ int result = baseTextSource.IndexOfAny(anyOf, startIndex + offset, count);
+ return result >= 0 ? result - offset : result;
+ }
+ }
+
+ /// <summary>
+ /// Implements the ITextSource interface using a string.
+ /// </summary>
+ public sealed class StringTextSource : ITextSource
+ {
+ readonly string text;
+
+ /// <summary>
+ /// Creates a new StringTextSource.
+ /// </summary>
+ public StringTextSource(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.text = text;
+ }
+
+ // Text can never change
+ event EventHandler ITextSource.TextChanged { add {} remove {} }
+
+ /// <inheritdoc/>
+ public string Text {
+ get { return text; }
+ }
+
+ /// <inheritdoc/>
+ public int TextLength {
+ get { return text.Length; }
+ }
+
+ /// <inheritdoc/>
+ public char GetCharAt(int offset)
+ {
+ // GetCharAt must throw ArgumentOutOfRangeException, not IndexOutOfRangeException
+ if (offset < 0 || offset >= text.Length)
+ throw new ArgumentOutOfRangeException("offset", offset, "offset must be between 0 and " + (text.Length - 1));
+ return text[offset];
+ }
+
+ /// <inheritdoc/>
+ public string GetText(int offset, int length)
+ {
+ return text.Substring(offset, length);
+ }
+
+ /// <inheritdoc/>
+ public TextReader CreateReader()
+ {
+ return new StringReader(text);
+ }
+
+ /// <inheritdoc/>
+ public ITextSource CreateSnapshot()
+ {
+ return this; // StringTextSource already is immutable
+ }
+
+ /// <inheritdoc/>
+ public ITextSource CreateSnapshot(int offset, int length)
+ {
+ return new StringTextSource(text.Substring(offset, length));
+ }
+
+ /// <inheritdoc/>
+ public int IndexOfAny(char[] anyOf, int startIndex, int count)
+ {
+ return text.IndexOfAny(anyOf, startIndex, count);
+ }
+ }
+
+ /// <summary>
+ /// Implements the ITextSource interface using a rope.
+ /// </summary>
+ public sealed class RopeTextSource : ITextSource
+ {
+ readonly Rope<char> rope;
+
+ /// <summary>
+ /// Creates a new RopeTextSource.
+ /// </summary>
+ public RopeTextSource(Rope<char> rope)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ this.rope = rope;
+ }
+
+ /// <summary>
+ /// Returns a clone of the rope used for this text source.
+ /// </summary>
+ /// <remarks>
+ /// RopeTextSource only publishes a copy of the contained rope to ensure that the underlying rope cannot be modified.
+ /// Unless the creator of the RopeTextSource still has a reference on the rope, RopeTextSource is immutable.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification="Not a property because it creates a clone")]
+ public Rope<char> GetRope()
+ {
+ return rope.Clone();
+ }
+
+ // Change event is not supported
+ event EventHandler ITextSource.TextChanged { add {} remove {} }
+
+ /// <inheritdoc/>
+ public string Text {
+ get { return rope.ToString(); }
+ }
+
+ /// <inheritdoc/>
+ public int TextLength {
+ get { return rope.Length; }
+ }
+
+ /// <inheritdoc/>
+ public char GetCharAt(int offset)
+ {
+ return rope[offset];
+ }
+
+ /// <inheritdoc/>
+ public string GetText(int offset, int length)
+ {
+ return rope.ToString(offset, length);
+ }
+
+ /// <inheritdoc/>
+ public TextReader CreateReader()
+ {
+ return new RopeTextReader(rope);
+ }
+
+ /// <inheritdoc/>
+ public ITextSource CreateSnapshot()
+ {
+ // we clone the underlying rope because the creator of the RopeTextSource might be modifying it
+ return new RopeTextSource(rope.Clone());
+ }
+
+ /// <inheritdoc/>
+ public ITextSource CreateSnapshot(int offset, int length)
+ {
+ return new RopeTextSource(rope.GetRange(offset, length));
+ }
+
+ /// <inheritdoc/>
+ public int IndexOfAny(char[] anyOf, int startIndex, int count)
+ {
+ return rope.IndexOfAny(anyOf, startIndex, count);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/IUndoableOperation.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/IUndoableOperation.cs
new file mode 100644
index 000000000..2e19c462c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/IUndoableOperation.cs
@@ -0,0 +1,30 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// This Interface describes a the basic Undo/Redo operation
+ /// all Undo Operations must implement this interface.
+ /// </summary>
+ public interface IUndoableOperation
+ {
+ /// <summary>
+ /// Undo the last operation
+ /// </summary>
+ void Undo();
+
+ /// <summary>
+ /// Redo the last operation
+ /// </summary>
+ void Redo();
+ }
+
+ interface IUndoableOperationWithContext : IUndoableOperation
+ {
+ void Undo(UndoStack stack);
+ void Redo(UndoStack stack);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineManager.cs
new file mode 100644
index 000000000..ecab1fa48
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineManager.cs
@@ -0,0 +1,288 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Creates/Deletes lines when text is inserted/removed.
+ /// </summary>
+ sealed class LineManager
+ {
+ #region Constructor
+ readonly TextDocument document;
+ readonly DocumentLineTree documentLineTree;
+
+ /// <summary>
+ /// A copy of the line trackers. We need a copy so that line trackers may remove themselves
+ /// while being notified (used e.g. by WeakLineTracker)
+ /// </summary>
+ ILineTracker[] lineTrackers;
+
+ internal void UpdateListOfLineTrackers()
+ {
+ this.lineTrackers = document.LineTrackers.ToArray();
+ }
+
+ public LineManager(DocumentLineTree documentLineTree, TextDocument document)
+ {
+ this.document = document;
+ this.documentLineTree = documentLineTree;
+ UpdateListOfLineTrackers();
+
+ Rebuild();
+ }
+ #endregion
+
+ #region Change events
+ /*
+ HashSet<DocumentLine> deletedLines = new HashSet<DocumentLine>();
+ readonly HashSet<DocumentLine> changedLines = new HashSet<DocumentLine>();
+ HashSet<DocumentLine> deletedOrChangedLines = new HashSet<DocumentLine>();
+
+ /// <summary>
+ /// Gets the list of lines deleted since the last RetrieveChangedLines() call.
+ /// The returned list is unsorted.
+ /// </summary>
+ public ICollection<DocumentLine> RetrieveDeletedLines()
+ {
+ var r = deletedLines;
+ deletedLines = new HashSet<DocumentLine>();
+ return r;
+ }
+
+ /// <summary>
+ /// Gets the list of lines changed since the last RetrieveChangedLines() call.
+ /// The returned list is sorted by line number and does not contain deleted lines.
+ /// </summary>
+ public List<DocumentLine> RetrieveChangedLines()
+ {
+ var list = (from line in changedLines
+ where !line.IsDeleted
+ let number = line.LineNumber
+ orderby number
+ select line).ToList();
+ changedLines.Clear();
+ return list;
+ }
+
+ /// <summary>
+ /// Gets the list of lines changed since the last RetrieveDeletedOrChangedLines() call.
+ /// The returned list is not sorted.
+ /// </summary>
+ public ICollection<DocumentLine> RetrieveDeletedOrChangedLines()
+ {
+ var r = deletedOrChangedLines;
+ deletedOrChangedLines = new HashSet<DocumentLine>();
+ return r;
+ }
+ */
+ #endregion
+
+ #region Rebuild
+ public void Rebuild()
+ {
+ // keep the first document line
+ DocumentLine ls = documentLineTree.GetByNumber(1);
+ SimpleSegment ds = NewLineFinder.NextNewLine(document, 0);
+ List<DocumentLine> lines = new List<DocumentLine>();
+ int lastDelimiterEnd = 0;
+ while (ds != SimpleSegment.Invalid) {
+ ls.TotalLength = ds.Offset + ds.Length - lastDelimiterEnd;
+ ls.DelimiterLength = ds.Length;
+ lastDelimiterEnd = ds.Offset + ds.Length;
+ lines.Add(ls);
+
+ ls = new DocumentLine(document);
+ ds = NewLineFinder.NextNewLine(document, lastDelimiterEnd);
+ }
+ ls.ResetLine();
+ ls.TotalLength = document.TextLength - lastDelimiterEnd;
+ lines.Add(ls);
+ documentLineTree.RebuildTree(lines);
+ foreach (ILineTracker lineTracker in lineTrackers)
+ lineTracker.RebuildDocument();
+ }
+ #endregion
+
+ #region Remove
+ public void Remove(int offset, int length)
+ {
+ Debug.Assert(length >= 0);
+ if (length == 0) return;
+ DocumentLine startLine = documentLineTree.GetByOffset(offset);
+ int startLineOffset = startLine.Offset;
+
+ Debug.Assert(offset < startLineOffset + startLine.TotalLength);
+ if (offset > startLineOffset + startLine.Length) {
+ Debug.Assert(startLine.DelimiterLength == 2);
+ // we are deleting starting in the middle of a delimiter
+
+ // remove last delimiter part
+ SetLineLength(startLine, startLine.TotalLength - 1);
+ // remove remaining text
+ Remove(offset, length - 1);
+ return;
+ }
+
+ if (offset + length < startLineOffset + startLine.TotalLength) {
+ // just removing a part of this line
+ //startLine.RemovedLinePart(ref deferredEventList, offset - startLineOffset, length);
+ SetLineLength(startLine, startLine.TotalLength - length);
+ return;
+ }
+ // merge startLine with another line because startLine's delimiter was deleted
+ // possibly remove lines in between if multiple delimiters were deleted
+ int charactersRemovedInStartLine = startLineOffset + startLine.TotalLength - offset;
+ Debug.Assert(charactersRemovedInStartLine > 0);
+ //startLine.RemovedLinePart(ref deferredEventList, offset - startLineOffset, charactersRemovedInStartLine);
+
+
+ DocumentLine endLine = documentLineTree.GetByOffset(offset + length);
+ if (endLine == startLine) {
+ // special case: we are removing a part of the last line up to the
+ // end of the document
+ SetLineLength(startLine, startLine.TotalLength - length);
+ return;
+ }
+ int endLineOffset = endLine.Offset;
+ int charactersLeftInEndLine = endLineOffset + endLine.TotalLength - (offset + length);
+ //endLine.RemovedLinePart(ref deferredEventList, 0, endLine.TotalLength - charactersLeftInEndLine);
+ //startLine.MergedWith(endLine, offset - startLineOffset);
+
+ // remove all lines between startLine (excl.) and endLine (incl.)
+ DocumentLine tmp = startLine.NextLine;
+ DocumentLine lineToRemove;
+ do {
+ lineToRemove = tmp;
+ tmp = tmp.NextLine;
+ RemoveLine(lineToRemove);
+ } while (lineToRemove != endLine);
+
+ SetLineLength(startLine, startLine.TotalLength - charactersRemovedInStartLine + charactersLeftInEndLine);
+ }
+
+ void RemoveLine(DocumentLine lineToRemove)
+ {
+ foreach (ILineTracker lt in lineTrackers)
+ lt.BeforeRemoveLine(lineToRemove);
+ documentLineTree.RemoveLine(lineToRemove);
+// foreach (ILineTracker lt in lineTracker)
+// lt.AfterRemoveLine(lineToRemove);
+// deletedLines.Add(lineToRemove);
+// deletedOrChangedLines.Add(lineToRemove);
+ }
+
+ #endregion
+
+ #region Insert
+ public void Insert(int offset, string text)
+ {
+ DocumentLine line = documentLineTree.GetByOffset(offset);
+ int lineOffset = line.Offset;
+
+ Debug.Assert(offset <= lineOffset + line.TotalLength);
+ if (offset > lineOffset + line.Length) {
+ Debug.Assert(line.DelimiterLength == 2);
+ // we are inserting in the middle of a delimiter
+
+ // shorten line
+ SetLineLength(line, line.TotalLength - 1);
+ // add new line
+ line = InsertLineAfter(line, 1);
+ line = SetLineLength(line, 1);
+ }
+
+ SimpleSegment ds = NewLineFinder.NextNewLine(text, 0);
+ if (ds == SimpleSegment.Invalid) {
+ // no newline is being inserted, all text is inserted in a single line
+ //line.InsertedLinePart(offset - line.Offset, text.Length);
+ SetLineLength(line, line.TotalLength + text.Length);
+ return;
+ }
+ //DocumentLine firstLine = line;
+ //firstLine.InsertedLinePart(offset - firstLine.Offset, ds.Offset);
+ int lastDelimiterEnd = 0;
+ while (ds != SimpleSegment.Invalid) {
+ // split line segment at line delimiter
+ int lineBreakOffset = offset + ds.Offset + ds.Length;
+ lineOffset = line.Offset;
+ int lengthAfterInsertionPos = lineOffset + line.TotalLength - (offset + lastDelimiterEnd);
+ line = SetLineLength(line, lineBreakOffset - lineOffset);
+ DocumentLine newLine = InsertLineAfter(line, lengthAfterInsertionPos);
+ newLine = SetLineLength(newLine, lengthAfterInsertionPos);
+
+ line = newLine;
+ lastDelimiterEnd = ds.Offset + ds.Length;
+
+ ds = NewLineFinder.NextNewLine(text, lastDelimiterEnd);
+ }
+ //firstLine.SplitTo(line);
+ // insert rest after last delimiter
+ if (lastDelimiterEnd != text.Length) {
+ //line.InsertedLinePart(0, text.Length - lastDelimiterEnd);
+ SetLineLength(line, line.TotalLength + text.Length - lastDelimiterEnd);
+ }
+ }
+
+ DocumentLine InsertLineAfter(DocumentLine line, int length)
+ {
+ DocumentLine newLine = documentLineTree.InsertLineAfter(line, length);
+ foreach (ILineTracker lt in lineTrackers)
+ lt.LineInserted(line, newLine);
+ return newLine;
+ }
+ #endregion
+
+ #region SetLineLength
+ /// <summary>
+ /// Sets the total line length and checks the delimiter.
+ /// This method can cause line to be deleted when it contains a single '\n' character
+ /// and the previous line ends with '\r'.
+ /// </summary>
+ /// <returns>Usually returns <paramref name="line"/>, but if line was deleted due to
+ /// the "\r\n" merge, returns the previous line.</returns>
+ DocumentLine SetLineLength(DocumentLine line, int newTotalLength)
+ {
+// changedLines.Add(line);
+// deletedOrChangedLines.Add(line);
+ int delta = newTotalLength - line.TotalLength;
+ if (delta != 0) {
+ foreach (ILineTracker lt in lineTrackers)
+ lt.SetLineLength(line, newTotalLength);
+ line.TotalLength = newTotalLength;
+ DocumentLineTree.UpdateAfterChildrenChange(line);
+ }
+ // determine new DelimiterLength
+ if (newTotalLength == 0) {
+ line.DelimiterLength = 0;
+ } else {
+ int lineOffset = line.Offset;
+ char lastChar = document.GetCharAt(lineOffset + newTotalLength - 1);
+ if (lastChar == '\r') {
+ line.DelimiterLength = 1;
+ } else if (lastChar == '\n') {
+ if (newTotalLength >= 2 && document.GetCharAt(lineOffset + newTotalLength - 2) == '\r') {
+ line.DelimiterLength = 2;
+ } else if (newTotalLength == 1 && lineOffset > 0 && document.GetCharAt(lineOffset - 1) == '\r') {
+ // we need to join this line with the previous line
+ DocumentLine previousLine = line.PreviousLine;
+ RemoveLine(line);
+ return SetLineLength(previousLine, previousLine.TotalLength + 1);
+ } else {
+ line.DelimiterLength = 1;
+ }
+ } else {
+ line.DelimiterLength = 0;
+ }
+ }
+ return line;
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineNode.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineNode.cs
new file mode 100644
index 000000000..317202721
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/LineNode.cs
@@ -0,0 +1,83 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Document
+{
+ using LineNode = DocumentLine;
+
+ // A tree node in the document line tree.
+ // For the purpose of the invariants, "children", "descendents", "siblings" etc. include the DocumentLine object,
+ // it is treated as a third child node between left and right.
+
+ // Originally, this was a separate class, with a reference to the documentLine. The documentLine had a reference
+ // back to the node. To save memory, the same object is used for both the documentLine and the line node.
+ // This saves 16 bytes per line (8 byte object overhead + two pointers).
+// sealed class LineNode
+// {
+// internal readonly DocumentLine documentLine;
+ partial class DocumentLine
+ {
+ internal DocumentLine left, right, parent;
+ internal bool color;
+ // optimization note: I tried packing color and isDeleted into a single byte field, but that
+ // actually increased the memory requirements. The JIT packs two bools and a byte (delimiterSize)
+ // into a single DWORD, but two bytes get each their own DWORD. Three bytes end up in the same DWORD, so
+ // apparently the JIT only optimizes for memory when there are at least three small fields.
+ // Currently, DocumentLine takes 36 bytes on x86 (8 byte object overhead, 3 pointers, 3 ints, and another DWORD
+ // for the small fields).
+ // TODO: a possible optimization would be to combine 'totalLength' and the small fields into a single uint.
+ // delimiterSize takes only two bits, the two bools take another two bits; so there's still
+ // 28 bits left for totalLength. 268435455 characters per line should be enough for everyone :)
+
+ /// <summary>
+ /// Resets the line to enable its reuse after a document rebuild.
+ /// </summary>
+ internal void ResetLine()
+ {
+ totalLength = delimiterLength = 0;
+ isDeleted = color = false;
+ left = right = parent = null;
+ }
+
+ internal LineNode InitLineNode()
+ {
+ this.nodeTotalCount = 1;
+ this.nodeTotalLength = this.TotalLength;
+ return this;
+ }
+
+ internal LineNode LeftMost {
+ get {
+ LineNode node = this;
+ while (node.left != null)
+ node = node.left;
+ return node;
+ }
+ }
+
+ internal LineNode RightMost {
+ get {
+ LineNode node = this;
+ while (node.right != null)
+ node = node.right;
+ return node;
+ }
+ }
+
+ /// <summary>
+ /// The number of lines in this node and its child nodes.
+ /// Invariant:
+ /// nodeTotalCount = 1 + left.nodeTotalCount + right.nodeTotalCount
+ /// </summary>
+ internal int nodeTotalCount;
+
+ /// <summary>
+ /// The total text length of this node and its child nodes.
+ /// Invariant:
+ /// nodeTotalLength = left.nodeTotalLength + documentLine.TotalLength + right.nodeTotalLength
+ /// </summary>
+ internal int nodeTotalLength;
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/NewLineFinder.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/NewLineFinder.cs
new file mode 100644
index 000000000..6ecf8c1e5
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/NewLineFinder.cs
@@ -0,0 +1,134 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Document
+{
+ static class NewLineFinder
+ {
+ static readonly char[] newline = { '\r', '\n' };
+
+ internal static readonly string[] NewlineStrings = { "\r\n", "\r", "\n" };
+
+ /// <summary>
+ /// Gets the location of the next new line character, or SimpleSegment.Invalid
+ /// if none is found.
+ /// </summary>
+ internal static SimpleSegment NextNewLine(string text, int offset)
+ {
+ int pos = text.IndexOfAny(newline, offset);
+ if (pos >= 0) {
+ if (text[pos] == '\r') {
+ if (pos + 1 < text.Length && text[pos + 1] == '\n')
+ return new SimpleSegment(pos, 2);
+ }
+ return new SimpleSegment(pos, 1);
+ }
+ return SimpleSegment.Invalid;
+ }
+
+ /// <summary>
+ /// Gets the location of the next new line character, or SimpleSegment.Invalid
+ /// if none is found.
+ /// </summary>
+ internal static SimpleSegment NextNewLine(ITextSource text, int offset)
+ {
+ int textLength = text.TextLength;
+ int pos = text.IndexOfAny(newline, offset, textLength - offset);
+ if (pos >= 0) {
+ if (text.GetCharAt(pos) == '\r') {
+ if (pos + 1 < textLength && text.GetCharAt(pos + 1) == '\n')
+ return new SimpleSegment(pos, 2);
+ }
+ return new SimpleSegment(pos, 1);
+ }
+ return SimpleSegment.Invalid;
+ }
+ }
+
+ partial class TextUtilities
+ {
+ /// <summary>
+ /// Finds the next new line character starting at offset.
+ /// </summary>
+ /// <param name="text">The text source to search in.</param>
+ /// <param name="offset">The starting offset for the search.</param>
+ /// <param name="newLineType">The string representing the new line that was found, or null if no new line was found.</param>
+ /// <returns>The position of the first new line starting at or after <paramref name="offset"/>,
+ /// or -1 if no new line was found.</returns>
+ public static int FindNextNewLine(ITextSource text, int offset, out string newLineType)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ if (offset < 0 || offset > text.TextLength)
+ throw new ArgumentOutOfRangeException("offset", offset, "offset is outside of text source");
+ SimpleSegment s = NewLineFinder.NextNewLine(text, offset);
+ if (s == SimpleSegment.Invalid) {
+ newLineType = null;
+ return -1;
+ } else {
+ if (s.Length == 2) {
+ newLineType = "\r\n";
+ } else if (text.GetCharAt(s.Offset) == '\n') {
+ newLineType = "\n";
+ } else {
+ newLineType = "\r";
+ }
+ return s.Offset;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the specified string is a newline sequence.
+ /// </summary>
+ public static bool IsNewLine(string newLine)
+ {
+ return newLine == "\r\n" || newLine == "\n" || newLine == "\r";
+ }
+
+ /// <summary>
+ /// Normalizes all new lines in <paramref name="input"/> to be <paramref name="newLine"/>.
+ /// </summary>
+ public static string NormalizeNewLines(string input, string newLine)
+ {
+ if (input == null)
+ return null;
+ if (!IsNewLine(newLine))
+ throw new ArgumentException("newLine must be one of the known newline sequences");
+ SimpleSegment ds = NewLineFinder.NextNewLine(input, 0);
+ if (ds == SimpleSegment.Invalid) // text does not contain any new lines
+ return input;
+ StringBuilder b = new StringBuilder(input.Length);
+ int lastEndOffset = 0;
+ do {
+ b.Append(input, lastEndOffset, ds.Offset - lastEndOffset);
+ b.Append(newLine);
+ lastEndOffset = ds.EndOffset;
+ ds = NewLineFinder.NextNewLine(input, lastEndOffset);
+ } while (ds != SimpleSegment.Invalid);
+ // remaining string (after last newline)
+ b.Append(input, lastEndOffset, input.Length - lastEndOffset);
+ return b.ToString();
+ }
+
+ /// <summary>
+ /// Gets the newline sequence used in the document at the specified line.
+ /// </summary>
+ public static string GetNewLineFromDocument(TextDocument document, int lineNumber)
+ {
+ DocumentLine line = document.GetLineByNumber(lineNumber);
+ if (line.DelimiterLength == 0) {
+ // at the end of the document, there's no line delimiter, so use the delimiter
+ // from the previous line
+ line = line.PreviousLine;
+ if (line == null)
+ return Environment.NewLine;
+ }
+ return document.GetText(line.Offset + line.Length, line.DelimiterLength);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/OffsetChangeMap.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/OffsetChangeMap.cs
new file mode 100644
index 000000000..9494af56b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/OffsetChangeMap.cs
@@ -0,0 +1,347 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Contains predefined offset change mapping types.
+ /// </summary>
+ public enum OffsetChangeMappingType
+ {
+ /// <summary>
+ /// Normal replace.
+ /// Anchors in front of the replaced region will stay in front, anchors after the replaced region will stay after.
+ /// Anchors in the middle of the removed region will be deleted. If they survive deletion,
+ /// they move depending on their AnchorMovementType.
+ /// </summary>
+ /// <remarks>
+ /// This is the default implementation of DocumentChangeEventArgs when OffsetChangeMap is null,
+ /// so using this option usually works without creating an OffsetChangeMap instance.
+ /// This is equivalent to an OffsetChangeMap with a single entry describing the replace operation.
+ /// </remarks>
+ Normal,
+ /// <summary>
+ /// First the old text is removed, then the new text is inserted.
+ /// Anchors immediately in front (or after) the replaced region may move to the other side of the insertion,
+ /// depending on the AnchorMovementType.
+ /// </summary>
+ /// <remarks>
+ /// This is implemented as an OffsetChangeMap with two entries: the removal, and the insertion.
+ /// </remarks>
+ RemoveAndInsert,
+ /// <summary>
+ /// The text is replaced character-by-character.
+ /// Anchors keep their position inside the replaced text.
+ /// Anchors after the replaced region will move accordingly if the replacement text has a different length than the replaced text.
+ /// If the new text is shorter than the old text, anchors inside the old text that would end up behind the replacement text
+ /// will be moved so that they point to the end of the replacement text.
+ /// </summary>
+ /// <remarks>
+ /// On the OffsetChangeMap level, growing text is implemented by replacing the last character in the replaced text
+ /// with itself and the additional text segment. A simple insertion of the additional text would have the undesired
+ /// effect of moving anchors immediately after the replaced text into the replacement text if they used
+ /// AnchorMovementStyle.BeforeInsertion.
+ /// Shrinking text is implemented by removing the text segment that's too long; but in a special mode that
+ /// causes anchors to always survive irrespective of their <see cref="TextAnchor.SurviveDeletion"/> setting.
+ /// If the text keeps its old size, this is implemented as OffsetChangeMap.Empty.
+ /// </remarks>
+ CharacterReplace,
+ /// <summary>
+ /// Like 'Normal', but anchors with <see cref="TextAnchor.MovementType"/> = Default will stay in front of the
+ /// insertion instead of being moved behind it.
+ /// </summary>
+ KeepAnchorBeforeInsertion
+ }
+
+ /// <summary>
+ /// Describes a series of offset changes.
+ /// </summary>
+ [Serializable]
+ [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix",
+ Justification="It's a mapping old offsets -> new offsets")]
+ public sealed class OffsetChangeMap : Collection<OffsetChangeMapEntry>
+ {
+ /// <summary>
+ /// Immutable OffsetChangeMap that is empty.
+ /// </summary>
+ [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
+ Justification="The Empty instance is immutable")]
+ public static readonly OffsetChangeMap Empty = new OffsetChangeMap(Empty<OffsetChangeMapEntry>.Array, true);
+
+ /// <summary>
+ /// Creates a new OffsetChangeMap with a single element.
+ /// </summary>
+ /// <param name="entry">The entry.</param>
+ /// <returns>Returns a frozen OffsetChangeMap with a single entry.</returns>
+ public static OffsetChangeMap FromSingleElement(OffsetChangeMapEntry entry)
+ {
+ return new OffsetChangeMap(new OffsetChangeMapEntry[] { entry }, true);
+ }
+
+ bool isFrozen;
+
+ /// <summary>
+ /// Creates a new OffsetChangeMap instance.
+ /// </summary>
+ public OffsetChangeMap()
+ {
+ }
+
+ internal OffsetChangeMap(int capacity)
+ : base(new List<OffsetChangeMapEntry>(capacity))
+ {
+ }
+
+ private OffsetChangeMap(IList<OffsetChangeMapEntry> entries, bool isFrozen)
+ : base(entries)
+ {
+ this.isFrozen = isFrozen;
+ }
+
+ /// <summary>
+ /// Gets the new offset where the specified offset moves after this document change.
+ /// </summary>
+ public int GetNewOffset(int offset, AnchorMovementType movementType)
+ {
+ IList<OffsetChangeMapEntry> items = this.Items;
+ int count = items.Count;
+ for (int i = 0; i < count; i++) {
+ offset = items[i].GetNewOffset(offset, movementType);
+ }
+ return offset;
+ }
+
+ /// <summary>
+ /// Gets whether this OffsetChangeMap is a valid explanation for the specified document change.
+ /// </summary>
+ public bool IsValidForDocumentChange(int offset, int removalLength, int insertionLength)
+ {
+ int endOffset = offset + removalLength;
+ foreach (OffsetChangeMapEntry entry in this) {
+ // check that ChangeMapEntry is in valid range for this document change
+ if (entry.Offset < offset || entry.Offset + entry.RemovalLength > endOffset)
+ return false;
+ endOffset += entry.InsertionLength - entry.RemovalLength;
+ }
+ // check that the total delta matches
+ return endOffset == offset + insertionLength;
+ }
+
+ /// <summary>
+ /// Calculates the inverted OffsetChangeMap (used for the undo operation).
+ /// </summary>
+ public OffsetChangeMap Invert()
+ {
+ if (this == Empty)
+ return this;
+ OffsetChangeMap newMap = new OffsetChangeMap(this.Count);
+ for (int i = this.Count - 1; i >= 0; i--) {
+ OffsetChangeMapEntry entry = this[i];
+ // swap InsertionLength and RemovalLength
+ newMap.Add(new OffsetChangeMapEntry(entry.Offset, entry.InsertionLength, entry.RemovalLength));
+ }
+ return newMap;
+ }
+
+ /// <inheritdoc/>
+ protected override void ClearItems()
+ {
+ CheckFrozen();
+ base.ClearItems();
+ }
+
+ /// <inheritdoc/>
+ protected override void InsertItem(int index, OffsetChangeMapEntry item)
+ {
+ CheckFrozen();
+ base.InsertItem(index, item);
+ }
+
+ /// <inheritdoc/>
+ protected override void RemoveItem(int index)
+ {
+ CheckFrozen();
+ base.RemoveItem(index);
+ }
+
+ /// <inheritdoc/>
+ protected override void SetItem(int index, OffsetChangeMapEntry item)
+ {
+ CheckFrozen();
+ base.SetItem(index, item);
+ }
+
+ void CheckFrozen()
+ {
+ if (isFrozen)
+ throw new InvalidOperationException("This instance is frozen and cannot be modified.");
+ }
+
+ /// <summary>
+ /// Gets if this instance is frozen. Frozen instances are immutable and thus thread-safe.
+ /// </summary>
+ public bool IsFrozen {
+ get { return isFrozen; }
+ }
+
+ /// <summary>
+ /// Freezes this instance.
+ /// </summary>
+ public void Freeze()
+ {
+ isFrozen = true;
+ }
+ }
+
+ /// <summary>
+ /// An entry in the OffsetChangeMap.
+ /// This represents the offset of a document change (either insertion or removal, not both at once).
+ /// </summary>
+ [Serializable]
+ public struct OffsetChangeMapEntry : IEquatable<OffsetChangeMapEntry>
+ {
+ readonly int offset;
+
+ // MSB: DefaultAnchorMovementIsBeforeInsertion
+ readonly uint insertionLengthWithMovementFlag;
+
+ // MSB: RemovalNeverCausesAnchorDeletion; other 31 bits: RemovalLength
+ readonly uint removalLengthWithDeletionFlag;
+
+ /// <summary>
+ /// The offset at which the change occurs.
+ /// </summary>
+ public int Offset {
+ get { return offset; }
+ }
+
+ /// <summary>
+ /// The number of characters inserted.
+ /// Returns 0 if this entry represents a removal.
+ /// </summary>
+ public int InsertionLength {
+ get { return (int)(insertionLengthWithMovementFlag & 0x7fffffff); }
+ }
+
+ /// <summary>
+ /// The number of characters removed.
+ /// Returns 0 if this entry represents an insertion.
+ /// </summary>
+ public int RemovalLength {
+ get { return (int)(removalLengthWithDeletionFlag & 0x7fffffff); }
+ }
+
+ /// <summary>
+ /// Gets whether the removal should not cause any anchor deletions.
+ /// </summary>
+ public bool RemovalNeverCausesAnchorDeletion {
+ get { return (removalLengthWithDeletionFlag & 0x80000000) != 0; }
+ }
+
+ /// <summary>
+ /// Gets whether default anchor movement causes the anchor to stay in front of the caret.
+ /// </summary>
+ public bool DefaultAnchorMovementIsBeforeInsertion {
+ get { return (insertionLengthWithMovementFlag & 0x80000000) != 0; }
+ }
+
+ /// <summary>
+ /// Gets the new offset where the specified offset moves after this document change.
+ /// </summary>
+ public int GetNewOffset(int oldOffset, AnchorMovementType movementType)
+ {
+ int insertionLength = this.InsertionLength;
+ int removalLength = this.RemovalLength;
+ if (!(removalLength == 0 && oldOffset == offset)) {
+ // we're getting trouble (both if statements in here would apply)
+ // if there's no removal and we insert at the offset
+ // -> we'd need to disambiguate by movementType, which is handled after the if
+
+ // offset is before start of change: no movement
+ if (oldOffset <= offset)
+ return oldOffset;
+ // offset is after end of change: movement by normal delta
+ if (oldOffset >= offset + removalLength)
+ return oldOffset + insertionLength - removalLength;
+ }
+ // we reach this point if
+ // a) the oldOffset is inside the deleted segment
+ // b) there was no removal and we insert at the caret position
+ if (movementType == AnchorMovementType.AfterInsertion)
+ return offset + insertionLength;
+ else if (movementType == AnchorMovementType.BeforeInsertion)
+ return offset;
+ else
+ return this.DefaultAnchorMovementIsBeforeInsertion ? offset : offset + insertionLength;
+ }
+
+ /// <summary>
+ /// Creates a new OffsetChangeMapEntry instance.
+ /// </summary>
+ public OffsetChangeMapEntry(int offset, int removalLength, int insertionLength)
+ {
+ ThrowUtil.CheckNotNegative(offset, "offset");
+ ThrowUtil.CheckNotNegative(removalLength, "removalLength");
+ ThrowUtil.CheckNotNegative(insertionLength, "insertionLength");
+
+ this.offset = offset;
+ this.removalLengthWithDeletionFlag = (uint)removalLength;
+ this.insertionLengthWithMovementFlag = (uint)insertionLength;
+ }
+
+ /// <summary>
+ /// Creates a new OffsetChangeMapEntry instance.
+ /// </summary>
+ public OffsetChangeMapEntry(int offset, int removalLength, int insertionLength, bool removalNeverCausesAnchorDeletion, bool defaultAnchorMovementIsBeforeInsertion)
+ : this(offset, removalLength, insertionLength)
+ {
+ if (removalNeverCausesAnchorDeletion)
+ this.removalLengthWithDeletionFlag |= 0x80000000;
+ if (defaultAnchorMovementIsBeforeInsertion)
+ this.insertionLengthWithMovementFlag |= 0x80000000;
+ }
+
+ /// <inheritdoc/>
+ public override int GetHashCode()
+ {
+ unchecked {
+ return offset + 3559 * (int)insertionLengthWithMovementFlag + 3571 * (int)removalLengthWithDeletionFlag;
+ }
+ }
+
+ /// <inheritdoc/>
+ public override bool Equals(object obj)
+ {
+ return obj is OffsetChangeMapEntry && this.Equals((OffsetChangeMapEntry)obj);
+ }
+
+ /// <inheritdoc/>
+ public bool Equals(OffsetChangeMapEntry other)
+ {
+ return offset == other.offset && insertionLengthWithMovementFlag == other.insertionLengthWithMovementFlag && removalLengthWithDeletionFlag == other.removalLengthWithDeletionFlag;
+ }
+
+ /// <summary>
+ /// Tests the two entries for equality.
+ /// </summary>
+ public static bool operator ==(OffsetChangeMapEntry left, OffsetChangeMapEntry right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Tests the two entries for inequality.
+ /// </summary>
+ public static bool operator !=(OffsetChangeMapEntry left, OffsetChangeMapEntry right)
+ {
+ return !left.Equals(right);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchor.cs
new file mode 100644
index 000000000..a65c276f9
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchor.cs
@@ -0,0 +1,194 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// The TextAnchor class references an offset (a position between two characters).
+ /// It automatically updates the offset when text is inserted/removed in front of the anchor.
+ /// </summary>
+ /// <remarks>
+ /// <para>Use the <see cref="Offset"/> property to get the offset from a text anchor.
+ /// Use the <see cref="TextDocument.CreateAnchor"/> method to create an anchor from an offset.
+ /// </para>
+ /// <para>
+ /// The document will automatically update all text anchors; and because it uses weak references to do so,
+ /// the garbage collector can simply collect the anchor object when you don't need it anymore.
+ /// </para>
+ /// <para>Moreover, the document is able to efficiently update a large number of anchors without having to look
+ /// at each anchor object individually. Updating the offsets of all anchors usually only takes time logarithmic
+ /// to the number of anchors. Retrieving the <see cref="Offset"/> property also runs in O(lg N).</para>
+ /// <inheritdoc cref="IsDeleted" />
+ /// <inheritdoc cref="MovementType" />
+ /// <para>If you want to track a segment, you can use the <see cref="AnchorSegment"/> class which
+ /// implements <see cref="ISegment"/> using two text anchors.</para>
+ /// </remarks>
+ /// <example>
+ /// Usage:
+ /// <code>TextAnchor anchor = document.CreateAnchor(offset);
+ /// ChangeMyDocument();
+ /// int newOffset = anchor.Offset;
+ /// </code>
+ /// </example>
+ public sealed class TextAnchor
+ {
+ readonly TextDocument document;
+ internal TextAnchorNode node;
+
+ internal TextAnchor(TextDocument document)
+ {
+ this.document = document;
+ }
+
+ /// <summary>
+ /// Gets the document owning the anchor.
+ /// </summary>
+ public TextDocument Document {
+ get { return document; }
+ }
+
+ /// <summary>
+ /// Controls how the anchor moves.
+ /// </summary>
+ /// <remarks>Anchor movement is ambiguous if text is inserted exactly at the anchor's location.
+ /// Does the anchor stay before the inserted text, or does it move after it?
+ /// The property <see cref="MovementType"/> will be used to determine which of these two options the anchor will choose.
+ /// The default value is <see cref="AnchorMovementType.Default"/>.</remarks>
+ public AnchorMovementType MovementType { get; set; }
+
+ /// <summary>
+ /// <para>
+ /// Specifies whether the anchor survives deletion of the text containing it.
+ /// </para><para>
+ /// <c>false</c>: The anchor is deleted when the a selection that includes the anchor is deleted.
+ /// <c>true</c>: The anchor is not deleted.
+ /// </para>
+ /// </summary>
+ /// <remarks><inheritdoc cref="IsDeleted" /></remarks>
+ public bool SurviveDeletion { get; set; }
+
+ /// <summary>
+ /// Gets whether the anchor was deleted.
+ /// </summary>
+ /// <remarks>
+ /// <para>When a piece of text containing an anchor is removed, then that anchor will be deleted.
+ /// First, the <see cref="IsDeleted"/> property is set to true on all deleted anchors,
+ /// then the <see cref="Deleted"/> events are raised.
+ /// You cannot retrieve the offset from an anchor that has been deleted.</para>
+ /// <para>This deletion behavior might be useful when using anchors for building a bookmark feature,
+ /// but in other cases you want to still be able to use the anchor. For those cases, set <c><see cref="SurviveDeletion"/> = true</c>.</para>
+ /// </remarks>
+ public bool IsDeleted {
+ get {
+ document.DebugVerifyAccess();
+ return node == null;
+ }
+ }
+
+ /// <summary>
+ /// Occurs after the anchor was deleted.
+ /// </summary>
+ /// <remarks>
+ /// <inheritdoc cref="IsDeleted" />
+ /// <para>Due to the 'weak reference' nature of TextAnchor, you will receive the Deleted event only
+ /// while your code holds a reference to the TextAnchor object.</para>
+ /// </remarks>
+ public event EventHandler Deleted;
+
+ internal void OnDeleted(DelayedEvents delayedEvents)
+ {
+ node = null;
+ delayedEvents.DelayedRaise(Deleted, this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Gets the offset of the text anchor.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
+ public int Offset {
+ get {
+ document.DebugVerifyAccess();
+
+ TextAnchorNode n = this.node;
+ if (n == null)
+ throw new InvalidOperationException();
+
+ int offset = n.length;
+ if (n.left != null)
+ offset += n.left.totalLength;
+ while (n.parent != null) {
+ if (n == n.parent.right) {
+ if (n.parent.left != null)
+ offset += n.parent.left.totalLength;
+ offset += n.parent.length;
+ }
+ n = n.parent;
+ }
+ return offset;
+ }
+ }
+
+ /// <summary>
+ /// Gets the line number of the anchor.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
+ public int Line {
+ get {
+ return document.GetLineByOffset(this.Offset).LineNumber;
+ }
+ }
+
+ /// <summary>
+ /// Gets the column number of this anchor.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
+ public int Column {
+ get {
+ int offset = this.Offset;
+ return offset - document.GetLineByOffset(offset).Offset + 1;
+ }
+ }
+
+ /// <summary>
+ /// Gets the text location of this anchor.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown when trying to get the Offset from a deleted anchor.</exception>
+ public TextLocation Location {
+ get {
+ return document.GetLocation(this.Offset);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[TextAnchor Offset=" + Offset + "]";
+ }
+ }
+
+ /// <summary>
+ /// Defines how a text anchor moves.
+ /// </summary>
+ public enum AnchorMovementType
+ {
+ /// <summary>
+ /// When text is inserted at the anchor position, the type of the insertion
+ /// determines where the caret moves to. For normal insertions, the anchor will stay
+ /// behind the inserted text.
+ /// </summary>
+ Default,
+ /// <summary>
+ /// When text is inserted at the anchor position, the anchor will stay
+ /// before the inserted text.
+ /// </summary>
+ BeforeInsertion,
+ /// <summary>
+ /// When text is insered at the anchor position, the anchor will move
+ /// after the inserted text.
+ /// </summary>
+ AfterInsertion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorNode.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorNode.cs
new file mode 100644
index 000000000..bfa7c4eef
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorNode.cs
@@ -0,0 +1,87 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// A TextAnchorNode is placed in the TextAnchorTree.
+ /// It describes a section of text with a text anchor at the end of the section.
+ /// A weak reference is used to refer to the TextAnchor. (to save memory, we derive from WeakReference instead of referencing it)
+ /// </summary>
+ sealed class TextAnchorNode : WeakReference
+ {
+ internal TextAnchorNode left, right, parent;
+ internal bool color;
+ internal int length;
+ internal int totalLength; // totalLength = length + left.totalLength + right.totalLength
+
+ public TextAnchorNode(TextAnchor anchor) : base(anchor)
+ {
+ }
+
+ internal TextAnchorNode LeftMost {
+ get {
+ TextAnchorNode node = this;
+ while (node.left != null)
+ node = node.left;
+ return node;
+ }
+ }
+
+ internal TextAnchorNode RightMost {
+ get {
+ TextAnchorNode node = this;
+ while (node.right != null)
+ node = node.right;
+ return node;
+ }
+ }
+
+ /// <summary>
+ /// Gets the inorder successor of the node.
+ /// </summary>
+ internal TextAnchorNode Successor {
+ get {
+ if (right != null) {
+ return right.LeftMost;
+ } else {
+ TextAnchorNode node = this;
+ TextAnchorNode oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // go up until we are coming out of a left subtree
+ } while (node != null && node.right == oldNode);
+ return node;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the inorder predecessor of the node.
+ /// </summary>
+ internal TextAnchorNode Predecessor {
+ get {
+ if (left != null) {
+ return left.RightMost;
+ } else {
+ TextAnchorNode node = this;
+ TextAnchorNode oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // go up until we are coming out of a right subtree
+ } while (node != null && node.left == oldNode);
+ return node;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return "[TextAnchorNode Length=" + length + " TotalLength=" + totalLength + " Target=" + Target + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorTree.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorTree.cs
new file mode 100644
index 000000000..d07a33b9d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextAnchorTree.cs
@@ -0,0 +1,753 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// A tree of TextAnchorNodes.
+ /// </summary>
+ sealed class TextAnchorTree
+ {
+ // The text anchor tree has difficult requirements:
+ // - it must QUICKLY update the offset in all anchors whenever there is a document change
+ // - it must not reference text anchors directly, using weak references instead
+
+ // Clearly, we cannot afford updating an Offset property on all anchors (that would be O(N)).
+ // So instead, the anchors need to be able to calculate their offset from a data structure
+ // that can be efficiently updated.
+
+ // This implementation is built using an augmented red-black-tree.
+ // There is a 'TextAnchorNode' for each text anchor.
+ // Such a node represents a section of text (just the length is stored) with a (weakly referenced) text anchor at the end.
+
+ // Basically, you can imagine the list of text anchors as a sorted list of text anchors, where each anchor
+ // just stores the distance to the previous anchor.
+ // (next node = TextAnchorNode.Successor, distance = TextAnchorNode.length)
+ // Distances are never negative, so this representation means anchors are always sorted by offset
+ // (the order of anchors at the same offset is undefined)
+
+ // Of course, a linked list of anchors would be way too slow (one would need to traverse the whole list
+ // every time the offset of an anchor is being looked up).
+ // Instead, we use a red-black-tree. We aren't actually using the tree for sorting - it's just a binary tree
+ // as storage format for what's conceptually a list, the red-black properties are used to keep the tree balanced.
+ // Other balanced binary trees would work, too.
+
+ // What makes the tree-form efficient is that is augments the data by a 'totalLength'. Where 'length'
+ // represents the distance to the previous node, 'totalLength' is the sum of all 'length' values in the subtree
+ // under that node.
+ // This allows computing the Offset from an anchor by walking up the list of parent nodes instead of going
+ // through all predecessor nodes. So computing the Offset runs in O(log N).
+
+ readonly TextDocument document;
+ readonly List<TextAnchorNode> nodesToDelete = new List<TextAnchorNode>();
+ TextAnchorNode root;
+
+ public TextAnchorTree(TextDocument document)
+ {
+ this.document = document;
+ }
+
+ [Conditional("DEBUG")]
+ static void Log(string text)
+ {
+ Debug.WriteLine("TextAnchorTree: " + text);
+ }
+
+ #region Insert Text
+ void InsertText(int offset, int length, bool defaultAnchorMovementIsBeforeInsertion)
+ {
+ if (length == 0 || root == null || offset > root.totalLength)
+ return;
+
+ // find the range of nodes that are placed exactly at offset
+ // beginNode is inclusive, endNode is exclusive
+ if (offset == root.totalLength) {
+ PerformInsertText(FindActualBeginNode(root.RightMost), null, length, defaultAnchorMovementIsBeforeInsertion);
+ } else {
+ TextAnchorNode endNode = FindNode(ref offset);
+ Debug.Assert(endNode.length > 0);
+
+ if (offset > 0) {
+ // there are no nodes exactly at offset
+ endNode.length += length;
+ UpdateAugmentedData(endNode);
+ } else {
+ PerformInsertText(FindActualBeginNode(endNode.Predecessor), endNode, length, defaultAnchorMovementIsBeforeInsertion);
+ }
+ }
+ DeleteMarkedNodes();
+ }
+
+ TextAnchorNode FindActualBeginNode(TextAnchorNode node)
+ {
+ // now find the actual beginNode
+ while (node != null && node.length == 0)
+ node = node.Predecessor;
+ if (node == null) {
+ // no predecessor = beginNode is first node in tree
+ node = root.LeftMost;
+ }
+ return node;
+ }
+
+ // Sorts the nodes in the range [beginNode, endNode) by MovementType
+ // and inserts the length between the BeforeInsertion and the AfterInsertion nodes.
+ void PerformInsertText(TextAnchorNode beginNode, TextAnchorNode endNode, int length, bool defaultAnchorMovementIsBeforeInsertion)
+ {
+ Debug.Assert(beginNode != null);
+ // endNode may be null at the end of the anchor tree
+
+ // now we need to sort the nodes in the range [beginNode, endNode); putting those with
+ // MovementType.BeforeInsertion in front of those with MovementType.AfterInsertion
+ List<TextAnchorNode> beforeInsert = new List<TextAnchorNode>();
+ //List<TextAnchorNode> afterInsert = new List<TextAnchorNode>();
+ TextAnchorNode temp = beginNode;
+ while (temp != endNode) {
+ TextAnchor anchor = (TextAnchor)temp.Target;
+ if (anchor == null) {
+ // afterInsert.Add(temp);
+ MarkNodeForDelete(temp);
+ } else if (defaultAnchorMovementIsBeforeInsertion
+ ? anchor.MovementType != AnchorMovementType.AfterInsertion
+ : anchor.MovementType == AnchorMovementType.BeforeInsertion)
+ {
+ beforeInsert.Add(temp);
+// } else {
+// afterInsert.Add(temp);
+ }
+ temp = temp.Successor;
+ }
+ // now again go through the range and swap the nodes with those in the beforeInsert list
+ temp = beginNode;
+ foreach (TextAnchorNode node in beforeInsert) {
+ SwapAnchors(node, temp);
+ temp = temp.Successor;
+ }
+ // now temp is pointing to the first node that is afterInsert,
+ // or to endNode, if there is no afterInsert node at the offset
+ // So add the length to temp
+ if (temp == null) {
+ // temp might be null if endNode==null and no afterInserts
+ Debug.Assert(endNode == null);
+ } else {
+ temp.length += length;
+ UpdateAugmentedData(temp);
+ }
+ }
+
+ /// <summary>
+ /// Swaps the anchors stored in the two nodes.
+ /// </summary>
+ void SwapAnchors(TextAnchorNode n1, TextAnchorNode n2)
+ {
+ if (n1 != n2) {
+ TextAnchor anchor1 = (TextAnchor)n1.Target;
+ TextAnchor anchor2 = (TextAnchor)n2.Target;
+ if (anchor1 == null && anchor2 == null) {
+ // -> no swap required
+ return;
+ }
+ n1.Target = anchor2;
+ n2.Target = anchor1;
+ if (anchor1 == null) {
+ // unmark n1 from deletion, mark n2 for deletion
+ nodesToDelete.Remove(n1);
+ MarkNodeForDelete(n2);
+ anchor2.node = n1;
+ } else if (anchor2 == null) {
+ // unmark n2 from deletion, mark n1 for deletion
+ nodesToDelete.Remove(n2);
+ MarkNodeForDelete(n1);
+ anchor1.node = n2;
+ } else {
+ anchor1.node = n2;
+ anchor2.node = n1;
+ }
+ }
+ }
+ #endregion
+
+ #region Remove or Replace text
+ public void HandleTextChange(OffsetChangeMapEntry entry, DelayedEvents delayedEvents)
+ {
+ //Log("HandleTextChange(" + entry + ")");
+ if (entry.RemovalLength == 0) {
+ // This is a pure insertion.
+ // Unlike a replace with removal, a pure insertion can result in nodes at the same location
+ // to split depending on their MovementType.
+ // Thus, we handle this case on a separate code path
+ // (the code below looks like it does something similar, but it can only split
+ // the set of deletion survivors, not all nodes at an offset)
+ InsertText(entry.Offset, entry.InsertionLength, entry.DefaultAnchorMovementIsBeforeInsertion);
+ return;
+ }
+ // When handling a replacing text change, we need to:
+ // - find all anchors in the deleted segment and delete them / move them to the appropriate
+ // surviving side.
+ // - adjust the segment size between the left and right side
+
+ int offset = entry.Offset;
+ int remainingRemovalLength = entry.RemovalLength;
+ // if the text change is happening after the last anchor, we don't have to do anything
+ if (root == null || offset >= root.totalLength)
+ return;
+ TextAnchorNode node = FindNode(ref offset);
+ TextAnchorNode firstDeletionSurvivor = null;
+ // go forward through the tree and delete all nodes in the removal segment
+ while (node != null && offset + remainingRemovalLength > node.length) {
+ TextAnchor anchor = (TextAnchor)node.Target;
+ if (anchor != null && (anchor.SurviveDeletion || entry.RemovalNeverCausesAnchorDeletion)) {
+ if (firstDeletionSurvivor == null)
+ firstDeletionSurvivor = node;
+ // This node should be deleted, but it wants to survive.
+ // We'll just remove the deleted length segment, so the node will be positioned
+ // in front of the removed segment.
+ remainingRemovalLength -= node.length - offset;
+ node.length = offset;
+ offset = 0;
+ UpdateAugmentedData(node);
+ node = node.Successor;
+ } else {
+ // delete node
+ TextAnchorNode s = node.Successor;
+ remainingRemovalLength -= node.length;
+ RemoveNode(node);
+ // we already deleted the node, don't delete it twice
+ nodesToDelete.Remove(node);
+ if (anchor != null)
+ anchor.OnDeleted(delayedEvents);
+ node = s;
+ }
+ }
+ // 'node' now is the first anchor after the deleted segment.
+ // If there are no anchors after the deleted segment, 'node' is null.
+
+ // firstDeletionSurvivor was set to the first node surviving deletion.
+ // Because all non-surviving nodes up to 'node' were deleted, the node range
+ // [firstDeletionSurvivor, node) now refers to the set of all deletion survivors.
+
+ // do the remaining job of the removal
+ if (node != null) {
+ node.length -= remainingRemovalLength;
+ Debug.Assert(node.length >= 0);
+ }
+ if (entry.InsertionLength > 0) {
+ // we are performing a replacement
+ if (firstDeletionSurvivor != null) {
+ // We got deletion survivors which need to be split into BeforeInsertion
+ // and AfterInsertion groups.
+ // Take care that we don't regroup everything at offset, but only the deletion
+ // survivors - from firstDeletionSurvivor (inclusive) to node (exclusive).
+ // This ensures that nodes immediately before or after the replaced segment
+ // stay where they are (independent from their MovementType)
+ PerformInsertText(firstDeletionSurvivor, node, entry.InsertionLength, entry.DefaultAnchorMovementIsBeforeInsertion);
+ } else if (node != null) {
+ // No deletion survivors:
+ // just perform the insertion
+ node.length += entry.InsertionLength;
+ }
+ }
+ if (node != null) {
+ UpdateAugmentedData(node);
+ }
+ DeleteMarkedNodes();
+ }
+ #endregion
+
+ #region Node removal when TextAnchor was GC'ed
+ void MarkNodeForDelete(TextAnchorNode node)
+ {
+ if (!nodesToDelete.Contains(node))
+ nodesToDelete.Add(node);
+ }
+
+ void DeleteMarkedNodes()
+ {
+ CheckProperties();
+ while (nodesToDelete.Count > 0) {
+ int pos = nodesToDelete.Count - 1;
+ TextAnchorNode n = nodesToDelete[pos];
+ // combine section of n with the following section
+ TextAnchorNode s = n.Successor;
+ if (s != null) {
+ s.length += n.length;
+ }
+ RemoveNode(n);
+ if (s != null) {
+ UpdateAugmentedData(s);
+ }
+ nodesToDelete.RemoveAt(pos);
+ CheckProperties();
+ }
+ CheckProperties();
+ }
+ #endregion
+
+ #region FindNode
+ /// <summary>
+ /// Finds the node at the specified offset.
+ /// After the method has run, offset is relative to the beginning of the returned node.
+ /// </summary>
+ TextAnchorNode FindNode(ref int offset)
+ {
+ TextAnchorNode n = root;
+ while (true) {
+ if (n.left != null) {
+ if (offset < n.left.totalLength) {
+ n = n.left; // descend into left subtree
+ continue;
+ } else {
+ offset -= n.left.totalLength; // skip left subtree
+ }
+ }
+ if (!n.IsAlive)
+ MarkNodeForDelete(n);
+ if (offset < n.length) {
+ return n; // found correct node
+ } else {
+ offset -= n.length; // skip this node
+ }
+ if (n.right != null) {
+ n = n.right; // descend into right subtree
+ } else {
+ // didn't find any node containing the offset
+ return null;
+ }
+ }
+ }
+ #endregion
+
+ #region UpdateAugmentedData
+ void UpdateAugmentedData(TextAnchorNode n)
+ {
+ if (!n.IsAlive)
+ MarkNodeForDelete(n);
+
+ int totalLength = n.length;
+ if (n.left != null)
+ totalLength += n.left.totalLength;
+ if (n.right != null)
+ totalLength += n.right.totalLength;
+ if (n.totalLength != totalLength) {
+ n.totalLength = totalLength;
+ if (n.parent != null)
+ UpdateAugmentedData(n.parent);
+ }
+ }
+ #endregion
+
+ #region CreateAnchor
+ public TextAnchor CreateAnchor(int offset)
+ {
+ Log("CreateAnchor(" + offset + ")");
+ TextAnchor anchor = new TextAnchor(document);
+ anchor.node = new TextAnchorNode(anchor);
+ if (root == null) {
+ // creating the first text anchor
+ root = anchor.node;
+ root.totalLength = root.length = offset;
+ } else if (offset >= root.totalLength) {
+ // append anchor at end of tree
+ anchor.node.totalLength = anchor.node.length = offset - root.totalLength;
+ InsertAsRight(root.RightMost, anchor.node);
+ } else {
+ // insert anchor in middle of tree
+ TextAnchorNode n = FindNode(ref offset);
+ Debug.Assert(offset < n.length);
+ // split segment 'n' at offset
+ anchor.node.totalLength = anchor.node.length = offset;
+ n.length -= offset;
+ InsertBefore(n, anchor.node);
+ }
+ DeleteMarkedNodes();
+ return anchor;
+ }
+
+ void InsertBefore(TextAnchorNode node, TextAnchorNode newNode)
+ {
+ if (node.left == null) {
+ InsertAsLeft(node, newNode);
+ } else {
+ InsertAsRight(node.left.RightMost, newNode);
+ }
+ }
+ #endregion
+
+ #region Red/Black Tree
+ internal const bool RED = true;
+ internal const bool BLACK = false;
+
+ void InsertAsLeft(TextAnchorNode parentNode, TextAnchorNode newNode)
+ {
+ Debug.Assert(parentNode.left == null);
+ parentNode.left = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAugmentedData(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void InsertAsRight(TextAnchorNode parentNode, TextAnchorNode newNode)
+ {
+ Debug.Assert(parentNode.right == null);
+ parentNode.right = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAugmentedData(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void FixTreeOnInsert(TextAnchorNode node)
+ {
+ Debug.Assert(node != null);
+ Debug.Assert(node.color == RED);
+ Debug.Assert(node.left == null || node.left.color == BLACK);
+ Debug.Assert(node.right == null || node.right.color == BLACK);
+
+ TextAnchorNode parentNode = node.parent;
+ if (parentNode == null) {
+ // we inserted in the root -> the node must be black
+ // since this is a root node, making the node black increments the number of black nodes
+ // on all paths by one, so it is still the same for all paths.
+ node.color = BLACK;
+ return;
+ }
+ if (parentNode.color == BLACK) {
+ // if the parent node where we inserted was black, our red node is placed correctly.
+ // since we inserted a red node, the number of black nodes on each path is unchanged
+ // -> the tree is still balanced
+ return;
+ }
+ // parentNode is red, so there is a conflict here!
+
+ // because the root is black, parentNode is not the root -> there is a grandparent node
+ TextAnchorNode grandparentNode = parentNode.parent;
+ TextAnchorNode uncleNode = Sibling(parentNode);
+ if (uncleNode != null && uncleNode.color == RED) {
+ parentNode.color = BLACK;
+ uncleNode.color = BLACK;
+ grandparentNode.color = RED;
+ FixTreeOnInsert(grandparentNode);
+ return;
+ }
+ // now we know: parent is red but uncle is black
+ // First rotation:
+ if (node == parentNode.right && parentNode == grandparentNode.left) {
+ RotateLeft(parentNode);
+ node = node.left;
+ } else if (node == parentNode.left && parentNode == grandparentNode.right) {
+ RotateRight(parentNode);
+ node = node.right;
+ }
+ // because node might have changed, reassign variables:
+ parentNode = node.parent;
+ grandparentNode = parentNode.parent;
+
+ // Now recolor a bit:
+ parentNode.color = BLACK;
+ grandparentNode.color = RED;
+ // Second rotation:
+ if (node == parentNode.left && parentNode == grandparentNode.left) {
+ RotateRight(grandparentNode);
+ } else {
+ // because of the first rotation, this is guaranteed:
+ Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right);
+ RotateLeft(grandparentNode);
+ }
+ }
+
+ void RemoveNode(TextAnchorNode removedNode)
+ {
+ if (removedNode.left != null && removedNode.right != null) {
+ // replace removedNode with it's in-order successor
+
+ TextAnchorNode leftMost = removedNode.right.LeftMost;
+ RemoveNode(leftMost); // remove leftMost from its current location
+
+ // and overwrite the removedNode with it
+ ReplaceNode(removedNode, leftMost);
+ leftMost.left = removedNode.left;
+ if (leftMost.left != null) leftMost.left.parent = leftMost;
+ leftMost.right = removedNode.right;
+ if (leftMost.right != null) leftMost.right.parent = leftMost;
+ leftMost.color = removedNode.color;
+
+ UpdateAugmentedData(leftMost);
+ if (leftMost.parent != null) UpdateAugmentedData(leftMost.parent);
+ return;
+ }
+
+ // now either removedNode.left or removedNode.right is null
+ // get the remaining child
+ TextAnchorNode parentNode = removedNode.parent;
+ TextAnchorNode childNode = removedNode.left ?? removedNode.right;
+ ReplaceNode(removedNode, childNode);
+ if (parentNode != null) UpdateAugmentedData(parentNode);
+ if (removedNode.color == BLACK) {
+ if (childNode != null && childNode.color == RED) {
+ childNode.color = BLACK;
+ } else {
+ FixTreeOnDelete(childNode, parentNode);
+ }
+ }
+ }
+
+ void FixTreeOnDelete(TextAnchorNode node, TextAnchorNode parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (parentNode == null)
+ return;
+
+ // warning: node may be null
+ TextAnchorNode sibling = Sibling(node, parentNode);
+ if (sibling.color == RED) {
+ parentNode.color = RED;
+ sibling.color = BLACK;
+ if (node == parentNode.left) {
+ RotateLeft(parentNode);
+ } else {
+ RotateRight(parentNode);
+ }
+
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+ }
+
+ if (parentNode.color == BLACK
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ FixTreeOnDelete(parentNode, parentNode.parent);
+ return;
+ }
+
+ if (parentNode.color == RED
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ parentNode.color = BLACK;
+ return;
+ }
+
+ if (node == parentNode.left &&
+ sibling.color == BLACK &&
+ GetColor(sibling.left) == RED &&
+ GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.left.color = BLACK;
+ RotateRight(sibling);
+ }
+ else if (node == parentNode.right &&
+ sibling.color == BLACK &&
+ GetColor(sibling.right) == RED &&
+ GetColor(sibling.left) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.right.color = BLACK;
+ RotateLeft(sibling);
+ }
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+
+ sibling.color = parentNode.color;
+ parentNode.color = BLACK;
+ if (node == parentNode.left) {
+ if (sibling.right != null) {
+ Debug.Assert(sibling.right.color == RED);
+ sibling.right.color = BLACK;
+ }
+ RotateLeft(parentNode);
+ } else {
+ if (sibling.left != null) {
+ Debug.Assert(sibling.left.color == RED);
+ sibling.left.color = BLACK;
+ }
+ RotateRight(parentNode);
+ }
+ }
+
+ void ReplaceNode(TextAnchorNode replacedNode, TextAnchorNode newNode)
+ {
+ if (replacedNode.parent == null) {
+ Debug.Assert(replacedNode == root);
+ root = newNode;
+ } else {
+ if (replacedNode.parent.left == replacedNode)
+ replacedNode.parent.left = newNode;
+ else
+ replacedNode.parent.right = newNode;
+ }
+ if (newNode != null) {
+ newNode.parent = replacedNode.parent;
+ }
+ replacedNode.parent = null;
+ }
+
+ void RotateLeft(TextAnchorNode p)
+ {
+ // let q be p's right child
+ TextAnchorNode q = p.right;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's right child to be q's left child
+ p.right = q.left;
+ if (p.right != null) p.right.parent = p;
+ // set q's left child to be p
+ q.left = p;
+ p.parent = q;
+ UpdateAugmentedData(p);
+ UpdateAugmentedData(q);
+ }
+
+ void RotateRight(TextAnchorNode p)
+ {
+ // let q be p's left child
+ TextAnchorNode q = p.left;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's left child to be q's right child
+ p.left = q.right;
+ if (p.left != null) p.left.parent = p;
+ // set q's right child to be p
+ q.right = p;
+ p.parent = q;
+ UpdateAugmentedData(p);
+ UpdateAugmentedData(q);
+ }
+
+ static TextAnchorNode Sibling(TextAnchorNode node)
+ {
+ if (node == node.parent.left)
+ return node.parent.right;
+ else
+ return node.parent.left;
+ }
+
+ static TextAnchorNode Sibling(TextAnchorNode node, TextAnchorNode parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (node == parentNode.left)
+ return parentNode.right;
+ else
+ return parentNode.left;
+ }
+
+ static bool GetColor(TextAnchorNode node)
+ {
+ return node != null ? node.color : BLACK;
+ }
+ #endregion
+
+ #region CheckProperties
+ [Conditional("DATACONSISTENCYTEST")]
+ internal void CheckProperties()
+ {
+ #if DEBUG
+ if (root != null) {
+ CheckProperties(root);
+
+ // check red-black property:
+ int blackCount = -1;
+ CheckNodeProperties(root, null, RED, 0, ref blackCount);
+ }
+ #endif
+ }
+
+ #if DEBUG
+ void CheckProperties(TextAnchorNode node)
+ {
+ int totalLength = node.length;
+ if (node.left != null) {
+ CheckProperties(node.left);
+ totalLength += node.left.totalLength;
+ }
+ if (node.right != null) {
+ CheckProperties(node.right);
+ totalLength += node.right.totalLength;
+ }
+ Debug.Assert(node.totalLength == totalLength);
+ }
+
+ /*
+ 1. A node is either red or black.
+ 2. The root is black.
+ 3. All leaves are black. (The leaves are the NIL children.)
+ 4. Both children of every red node are black. (So every red node must have a black parent.)
+ 5. Every simple path from a node to a descendant leaf contains the same number of black nodes. (Not counting the leaf node.)
+ */
+ void CheckNodeProperties(TextAnchorNode node, TextAnchorNode parentNode, bool parentColor, int blackCount, ref int expectedBlackCount)
+ {
+ if (node == null) return;
+
+ Debug.Assert(node.parent == parentNode);
+
+ if (parentColor == RED) {
+ Debug.Assert(node.color == BLACK);
+ }
+ if (node.color == BLACK) {
+ blackCount++;
+ }
+ if (node.left == null && node.right == null) {
+ // node is a leaf node:
+ if (expectedBlackCount == -1)
+ expectedBlackCount = blackCount;
+ else
+ Debug.Assert(expectedBlackCount == blackCount);
+ }
+ CheckNodeProperties(node.left, node, node.color, blackCount, ref expectedBlackCount);
+ CheckNodeProperties(node.right, node, node.color, blackCount, ref expectedBlackCount);
+ }
+ #endif
+ #endregion
+
+ #region GetTreeAsString
+ #if DEBUG
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public string GetTreeAsString()
+ {
+ if (root == null)
+ return "<empty tree>";
+ StringBuilder b = new StringBuilder();
+ AppendTreeToString(root, b, 0);
+ return b.ToString();
+ }
+
+ static void AppendTreeToString(TextAnchorNode node, StringBuilder b, int indent)
+ {
+ if (node.color == RED)
+ b.Append("RED ");
+ else
+ b.Append("BLACK ");
+ b.AppendLine(node.ToString());
+ indent += 2;
+ if (node.left != null) {
+ b.Append(' ', indent);
+ b.Append("L: ");
+ AppendTreeToString(node.left, b, indent);
+ }
+ if (node.right != null) {
+ b.Append(' ', indent);
+ b.Append("R: ");
+ AppendTreeToString(node.right, b, indent);
+ }
+ }
+ #endif
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocument.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocument.cs
new file mode 100644
index 000000000..84fc86f44
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocument.cs
@@ -0,0 +1,836 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Globalization;
+using System.Linq.Expressions;
+using System.Threading;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// This class is the main class of the text model. Basically, it is a <see cref="System.Text.StringBuilder"/> with events.
+ /// </summary>
+ /// <remarks>
+ /// <b>Thread safety:</b>
+ /// <inheritdoc cref="VerifyAccess"/>
+ /// <para>However, there is a single method that is thread-safe: <see cref="CreateSnapshot()"/> (and its overloads).</para>
+ /// </remarks>
+ public sealed class TextDocument : ITextSource, INotifyPropertyChanged
+ {
+ #region Thread ownership
+ readonly object lockObject = new object();
+ Thread owner = Thread.CurrentThread;
+
+ /// <summary>
+ /// Verifies that the current thread is the documents owner thread.
+ /// Throws an <see cref="InvalidOperationException"/> if the wrong thread accesses the TextDocument.
+ /// </summary>
+ /// <remarks>
+ /// <para>The TextDocument class is not thread-safe. A document instance expects to have a single owner thread
+ /// and will throw an <see cref="InvalidOperationException"/> when accessed from another thread.
+ /// It is possible to change the owner thread using the <see cref="SetOwnerThread"/> method.</para>
+ /// </remarks>
+ public void VerifyAccess()
+ {
+ if (Thread.CurrentThread != owner)
+ throw new InvalidOperationException("TextDocument can be accessed only from the thread that owns it.");
+ }
+
+ /// <summary>
+ /// Transfers ownership of the document to another thread. This method can be used to load
+ /// a file into a TextDocument on a background thread and then transfer ownership to the UI thread
+ /// for displaying the document.
+ /// </summary>
+ /// <remarks>
+ /// <inheritdoc cref="VerifyAccess"/>
+ /// <para>
+ /// The owner can be set to null, which means that no thread can access the document. But, if the document
+ /// has no owner thread, any thread may take ownership by calling <see cref="SetOwnerThread"/>.
+ /// </para>
+ /// </remarks>
+ public void SetOwnerThread(Thread newOwner)
+ {
+ // We need to lock here to ensure that in the null owner case,
+ // only one thread succeeds in taking ownership.
+ lock (lockObject) {
+ if (owner != null) {
+ VerifyAccess();
+ }
+ owner = newOwner;
+ }
+ }
+ #endregion
+
+ #region Fields + Constructor
+ readonly Rope<char> rope;
+ readonly DocumentLineTree lineTree;
+ readonly LineManager lineManager;
+ readonly TextAnchorTree anchorTree;
+ ChangeTrackingCheckpoint currentCheckpoint;
+
+ /// <summary>
+ /// Create an empty text document.
+ /// </summary>
+ public TextDocument()
+ : this(string.Empty)
+ {
+ }
+
+ /// <summary>
+ /// Create a new text document with the specified initial text.
+ /// </summary>
+ public TextDocument(IEnumerable<char> initialText)
+ {
+ if (initialText == null)
+ throw new ArgumentNullException("initialText");
+ rope = new Rope<char>(initialText);
+ lineTree = new DocumentLineTree(this);
+ lineManager = new LineManager(lineTree, this);
+ lineTrackers.CollectionChanged += delegate {
+ lineManager.UpdateListOfLineTrackers();
+ };
+
+ anchorTree = new TextAnchorTree(this);
+ undoStack = new UndoStack();
+ FireChangeEvents();
+ }
+
+ /// <summary>
+ /// Create a new text document with the specified initial text.
+ /// </summary>
+ public TextDocument(ITextSource initialText)
+ : this(GetTextFromTextSource(initialText))
+ {
+ }
+
+ // gets the text from a text source, directly retrieving the underlying rope where possible
+ static IEnumerable<char> GetTextFromTextSource(ITextSource textSource)
+ {
+ if (textSource == null)
+ throw new ArgumentNullException("textSource");
+
+ RopeTextSource rts = textSource as RopeTextSource;
+ if (rts != null)
+ return rts.GetRope();
+
+ TextDocument doc = textSource as TextDocument;
+ if (doc != null)
+ return doc.rope;
+
+ return textSource.Text;
+ }
+ #endregion
+
+ #region Text
+ void ThrowIfRangeInvalid(int offset, int length)
+ {
+ if (offset < 0 || offset > rope.Length) {
+ throw new ArgumentOutOfRangeException("offset", offset, "0 <= offset <= " + rope.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (length < 0 || offset + length > rope.Length) {
+ throw new ArgumentOutOfRangeException("length", length, "0 <= length, offset(" + offset + ")+length <= " + rope.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+
+ /// <inheritdoc/>
+ public string GetText(int offset, int length)
+ {
+ VerifyAccess();
+ return rope.ToString(offset, length);
+ }
+
+ /// <summary>
+ /// Retrieves the text for a portion of the document.
+ /// </summary>
+ public string GetText(ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+ return GetText(segment.Offset, segment.Length);
+ }
+
+ int ITextSource.IndexOfAny(char[] anyOf, int startIndex, int count)
+ {
+ DebugVerifyAccess(); // frequently called (NewLineFinder), so must be fast in release builds
+ return rope.IndexOfAny(anyOf, startIndex, count);
+ }
+
+ /// <inheritdoc/>
+ public char GetCharAt(int offset)
+ {
+ DebugVerifyAccess(); // frequently called, so must be fast in release builds
+ return rope[offset];
+ }
+
+ WeakReference cachedText;
+
+ /// <summary>
+ /// Gets/Sets the text of the whole document.
+ /// </summary>
+ public string Text {
+ get {
+ VerifyAccess();
+ string completeText = cachedText != null ? (cachedText.Target as string) : null;
+ if (completeText == null) {
+ completeText = rope.ToString();
+ cachedText = new WeakReference(completeText);
+ }
+ return completeText;
+ }
+ set {
+ VerifyAccess();
+ if (value == null)
+ throw new ArgumentNullException("value");
+ Replace(0, rope.Length, value);
+ }
+ }
+
+ /// <inheritdoc/>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ public event EventHandler TextChanged;
+
+ /// <inheritdoc/>
+ public int TextLength {
+ get {
+ VerifyAccess();
+ return rope.Length;
+ }
+ }
+
+ /// <summary>
+ /// Is raised when the TextLength property changes.
+ /// </summary>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ [Obsolete("This event will be removed in a future version; use the PropertyChanged event instead")]
+ public event EventHandler TextLengthChanged;
+
+ /// <summary>
+ /// Is raised when one of the properties <see cref="Text"/>, <see cref="TextLength"/>, <see cref="LineCount"/>,
+ /// <see cref="UndoStack"/> changes.
+ /// </summary>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ /// <summary>
+ /// Is raised before the document changes.
+ /// </summary>
+ /// <remarks>
+ /// <para>Here is the order in which events are raised during a document update:</para>
+ /// <list type="bullet">
+ /// <item><description><b><see cref="BeginUpdate">BeginUpdate()</see></b></description>
+ /// <list type="bullet">
+ /// <item><description>Start of change group (on undo stack)</description></item>
+ /// <item><description><see cref="UpdateStarted"/> event is raised</description></item>
+ /// </list></item>
+ /// <item><description><b><see cref="Insert(int,string)">Insert()</see> / <see cref="Remove(int,int)">Remove()</see> / <see cref="Replace(int,int,string)">Replace()</see></b></description>
+ /// <list type="bullet">
+ /// <item><description><see cref="Changing"/> event is raised</description></item>
+ /// <item><description>The document is changed</description></item>
+ /// <item><description><see cref="TextAnchor.Deleted">TextAnchor.Deleted</see> event is raised if anchors were
+ /// in the deleted text portion</description></item>
+ /// <item><description><see cref="Changed"/> event is raised</description></item>
+ /// </list></item>
+ /// <item><description><b><see cref="EndUpdate">EndUpdate()</see></b></description>
+ /// <list type="bullet">
+ /// <item><description><see cref="TextChanged"/> event is raised</description></item>
+ /// <item><description><see cref="PropertyChanged"/> event is raised (for the Text, TextLength, LineCount properties, in that order)</description></item>
+ /// <item><description>End of change group (on undo stack)</description></item>
+ /// <item><description><see cref="UpdateFinished"/> event is raised</description></item>
+ /// </list></item>
+ /// </list>
+ /// <para>
+ /// If the insert/remove/replace methods are called without a call to <c>BeginUpdate()</c>,
+ /// they will call <c>BeginUpdate()</c> and <c>EndUpdate()</c> to ensure no change happens outside of <c>UpdateStarted</c>/<c>UpdateFinished</c>.
+ /// </para><para>
+ /// There can be multiple document changes between the <c>BeginUpdate()</c> and <c>EndUpdate()</c> calls.
+ /// In this case, the events associated with EndUpdate will be raised only once after the whole document update is done.
+ /// </para><para>
+ /// The <see cref="UndoStack"/> listens to the <c>UpdateStarted</c> and <c>UpdateFinished</c> events to group all changes into a single undo step.
+ /// </para>
+ /// </remarks>
+ public event EventHandler<DocumentChangeEventArgs> Changing;
+
+ /// <summary>
+ /// Is raised after the document has changed.
+ /// </summary>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ public event EventHandler<DocumentChangeEventArgs> Changed;
+
+ /// <summary>
+ /// Creates a snapshot of the current text.
+ /// </summary>
+ /// <remarks>
+ /// <para>This method returns an immutable snapshot of the document, and may be safely called even when
+ /// the document's owner thread is concurrently modifying the document.
+ /// </para><para>
+ /// This special thread-safety guarantee is valid only for TextDocument.CreateSnapshot(), not necessarily for other
+ /// classes implementing ITextSource.CreateSnapshot().
+ /// </para><para>
+ /// </para>
+ /// </remarks>
+ public ITextSource CreateSnapshot()
+ {
+ lock (lockObject) {
+ return new RopeTextSource(rope.Clone());
+ }
+ }
+
+ /// <summary>
+ /// Creates a snapshot of the current text.
+ /// Additionally, creates a checkpoint that allows tracking document changes.
+ /// </summary>
+ /// <remarks><inheritdoc cref="CreateSnapshot()"/><inheritdoc cref="ChangeTrackingCheckpoint"/></remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", Justification = "Need to return snapshot and checkpoint together to ensure thread-safety")]
+ public ITextSource CreateSnapshot(out ChangeTrackingCheckpoint checkpoint)
+ {
+ lock (lockObject) {
+ if (currentCheckpoint == null)
+ currentCheckpoint = new ChangeTrackingCheckpoint(lockObject);
+ checkpoint = currentCheckpoint;
+ return new RopeTextSource(rope.Clone());
+ }
+ }
+
+ internal ChangeTrackingCheckpoint CreateChangeTrackingCheckpoint()
+ {
+ lock (lockObject) {
+ if (currentCheckpoint == null)
+ currentCheckpoint = new ChangeTrackingCheckpoint(lockObject);
+ return currentCheckpoint;
+ }
+ }
+
+ /// <summary>
+ /// Creates a snapshot of a part of the current text.
+ /// </summary>
+ /// <remarks><inheritdoc cref="CreateSnapshot()"/></remarks>
+ public ITextSource CreateSnapshot(int offset, int length)
+ {
+ lock (lockObject) {
+ return new RopeTextSource(rope.GetRange(offset, length));
+ }
+ }
+
+ /// <inheritdoc/>
+ public System.IO.TextReader CreateReader()
+ {
+ lock (lockObject) {
+ return new RopeTextReader(rope);
+ }
+ }
+ #endregion
+
+ #region BeginUpdate / EndUpdate
+ int beginUpdateCount;
+
+ /// <summary>
+ /// Gets if an update is running.
+ /// </summary>
+ /// <remarks><inheritdoc cref="BeginUpdate"/></remarks>
+ public bool IsInUpdate {
+ get {
+ VerifyAccess();
+ return beginUpdateCount > 0;
+ }
+ }
+
+ /// <summary>
+ /// Immediately calls <see cref="BeginUpdate()"/>,
+ /// and returns an IDisposable that calls <see cref="EndUpdate()"/>.
+ /// </summary>
+ /// <remarks><inheritdoc cref="BeginUpdate"/></remarks>
+ public IDisposable RunUpdate()
+ {
+ BeginUpdate();
+ return new CallbackOnDispose(EndUpdate);
+ }
+
+ /// <summary>
+ /// <para>Begins a group of document changes.</para>
+ /// <para>Some events are suspended until EndUpdate is called, and the <see cref="UndoStack"/> will
+ /// group all changes into a single action.</para>
+ /// <para>Calling BeginUpdate several times increments a counter, only after the appropriate number
+ /// of EndUpdate calls the events resume their work.</para>
+ /// </summary>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ public void BeginUpdate()
+ {
+ VerifyAccess();
+ if (inDocumentChanging)
+ throw new InvalidOperationException("Cannot change document within another document change.");
+ beginUpdateCount++;
+ if (beginUpdateCount == 1) {
+ undoStack.StartUndoGroup();
+ if (UpdateStarted != null)
+ UpdateStarted(this, EventArgs.Empty);
+ }
+ }
+
+ /// <summary>
+ /// Ends a group of document changes.
+ /// </summary>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ public void EndUpdate()
+ {
+ VerifyAccess();
+ if (inDocumentChanging)
+ throw new InvalidOperationException("Cannot end update within document change.");
+ if (beginUpdateCount == 0)
+ throw new InvalidOperationException("No update is active.");
+ if (beginUpdateCount == 1) {
+ // fire change events inside the change group - event handlers might add additional
+ // document changes to the change group
+ FireChangeEvents();
+ undoStack.EndUndoGroup();
+ beginUpdateCount = 0;
+ if (UpdateFinished != null)
+ UpdateFinished(this, EventArgs.Empty);
+ } else {
+ beginUpdateCount -= 1;
+ }
+ }
+
+ /// <summary>
+ /// Occurs when a document change starts.
+ /// </summary>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ public event EventHandler UpdateStarted;
+
+ /// <summary>
+ /// Occurs when a document change is finished.
+ /// </summary>
+ /// <remarks><inheritdoc cref="Changing"/></remarks>
+ public event EventHandler UpdateFinished;
+ #endregion
+
+ #region Fire events after update
+ int oldTextLength;
+ int oldLineCount;
+ bool fireTextChanged;
+
+ /// <summary>
+ /// Fires TextChanged, TextLengthChanged, LineCountChanged if required.
+ /// </summary>
+ internal void FireChangeEvents()
+ {
+ // it may be necessary to fire the event multiple times if the document is changed
+ // from inside the event handlers
+ while (fireTextChanged) {
+ fireTextChanged = false;
+ if (TextChanged != null)
+ TextChanged(this, EventArgs.Empty);
+ OnPropertyChanged("Text");
+
+ int textLength = rope.Length;
+ if (textLength != oldTextLength) {
+ oldTextLength = textLength;
+ if (TextLengthChanged != null)
+ TextLengthChanged(this, EventArgs.Empty);
+ OnPropertyChanged("TextLength");
+ }
+ int lineCount = lineTree.LineCount;
+ if (lineCount != oldLineCount) {
+ oldLineCount = lineCount;
+ if (LineCountChanged != null)
+ LineCountChanged(this, EventArgs.Empty);
+ OnPropertyChanged("LineCount");
+ }
+ }
+ }
+
+ void OnPropertyChanged(string propertyName)
+ {
+ if (PropertyChanged != null)
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ #endregion
+
+ #region Insert / Remove / Replace
+ /// <summary>
+ /// Inserts text.
+ /// </summary>
+ public void Insert(int offset, string text)
+ {
+ Replace(offset, 0, text);
+ }
+
+ /// <summary>
+ /// Removes text.
+ /// </summary>
+ public void Remove(ISegment segment)
+ {
+ Replace(segment, string.Empty);
+ }
+
+ /// <summary>
+ /// Removes text.
+ /// </summary>
+ public void Remove(int offset, int length)
+ {
+ Replace(offset, length, string.Empty);
+ }
+
+ internal bool inDocumentChanging;
+
+ /// <summary>
+ /// Replaces text.
+ /// </summary>
+ public void Replace(ISegment segment, string text)
+ {
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+ Replace(segment.Offset, segment.Length, text, null);
+ }
+
+ /// <summary>
+ /// Replaces text.
+ /// </summary>
+ public void Replace(int offset, int length, string text)
+ {
+ Replace(offset, length, text, null);
+ }
+
+ /// <summary>
+ /// Replaces text.
+ /// </summary>
+ /// <param name="offset">The starting offset of the text to be replaced.</param>
+ /// <param name="length">The length of the text to be replaced.</param>
+ /// <param name="text">The new text.</param>
+ /// <param name="offsetChangeMappingType">The offsetChangeMappingType determines how offsets inside the old text are mapped to the new text.
+ /// This affects how the anchors and segments inside the replaced region behave.</param>
+ public void Replace(int offset, int length, string text, OffsetChangeMappingType offsetChangeMappingType)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ // Please see OffsetChangeMappingType XML comments for details on how these modes work.
+ switch (offsetChangeMappingType) {
+ case OffsetChangeMappingType.Normal:
+ Replace(offset, length, text, null);
+ break;
+ case OffsetChangeMappingType.KeepAnchorBeforeInsertion:
+ Replace(offset, length, text, OffsetChangeMap.FromSingleElement(
+ new OffsetChangeMapEntry(offset, length, text.Length, false, true)));
+ break;
+ case OffsetChangeMappingType.RemoveAndInsert:
+ if (length == 0 || text.Length == 0) {
+ // only insertion or only removal?
+ // OffsetChangeMappingType doesn't matter, just use Normal.
+ Replace(offset, length, text, null);
+ } else {
+ OffsetChangeMap map = new OffsetChangeMap(2);
+ map.Add(new OffsetChangeMapEntry(offset, length, 0));
+ map.Add(new OffsetChangeMapEntry(offset, 0, text.Length));
+ map.Freeze();
+ Replace(offset, length, text, map);
+ }
+ break;
+ case OffsetChangeMappingType.CharacterReplace:
+ if (length == 0 || text.Length == 0) {
+ // only insertion or only removal?
+ // OffsetChangeMappingType doesn't matter, just use Normal.
+ Replace(offset, length, text, null);
+ } else if (text.Length > length) {
+ // look at OffsetChangeMappingType.CharacterReplace XML comments on why we need to replace
+ // the last character
+ OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + length - 1, 1, 1 + text.Length - length);
+ Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry));
+ } else if (text.Length < length) {
+ OffsetChangeMapEntry entry = new OffsetChangeMapEntry(offset + text.Length, length - text.Length, 0, true, false);
+ Replace(offset, length, text, OffsetChangeMap.FromSingleElement(entry));
+ } else {
+ Replace(offset, length, text, OffsetChangeMap.Empty);
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("offsetChangeMappingType", offsetChangeMappingType, "Invalid enum value");
+ }
+ }
+
+ /// <summary>
+ /// Replaces text.
+ /// </summary>
+ /// <param name="offset">The starting offset of the text to be replaced.</param>
+ /// <param name="length">The length of the text to be replaced.</param>
+ /// <param name="text">The new text.</param>
+ /// <param name="offsetChangeMap">The offsetChangeMap determines how offsets inside the old text are mapped to the new text.
+ /// This affects how the anchors and segments inside the replaced region behave.
+ /// If you pass null (the default when using one of the other overloads), the offsets are changed as
+ /// in OffsetChangeMappingType.Normal mode.
+ /// If you pass OffsetChangeMap.Empty, then everything will stay in its old place (OffsetChangeMappingType.CharacterReplace mode).
+ /// The offsetChangeMap must be a valid 'explanation' for the document change. See <see cref="OffsetChangeMap.IsValidForDocumentChange"/>.
+ /// Passing an OffsetChangeMap to the Replace method will automatically freeze it to ensure the thread safety of the resulting
+ /// DocumentChangeEventArgs instance.
+ /// </param>
+ public void Replace(int offset, int length, string text, OffsetChangeMap offsetChangeMap)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+
+ if (offsetChangeMap != null)
+ offsetChangeMap.Freeze();
+
+ // Ensure that all changes take place inside an update group.
+ // Will also take care of throwing an exception if inDocumentChanging is set.
+ BeginUpdate();
+ try {
+ // protect document change against corruption by other changes inside the event handlers
+ inDocumentChanging = true;
+ try {
+ // The range verification must wait until after the BeginUpdate() call because the document
+ // might be modified inside the UpdateStarted event.
+ ThrowIfRangeInvalid(offset, length);
+
+ DoReplace(offset, length, text, offsetChangeMap);
+ } finally {
+ inDocumentChanging = false;
+ }
+ } finally {
+ EndUpdate();
+ }
+ }
+
+ void DoReplace(int offset, int length, string newText, OffsetChangeMap offsetChangeMap)
+ {
+ if (length == 0 && newText.Length == 0)
+ return;
+
+ // trying to replace a single character in 'Normal' mode?
+ // for single characters, 'CharacterReplace' mode is equivalent, but more performant
+ // (we don't have to touch the anchorTree at all in 'CharacterReplace' mode)
+ if (length == 1 && newText.Length == 1 && offsetChangeMap == null)
+ offsetChangeMap = OffsetChangeMap.Empty;
+
+ string removedText = rope.ToString(offset, length);
+ DocumentChangeEventArgs args = new DocumentChangeEventArgs(offset, removedText, newText, offsetChangeMap);
+
+ // fire DocumentChanging event
+ if (Changing != null)
+ Changing(this, args);
+
+ undoStack.Push(this, args);
+
+ cachedText = null; // reset cache of complete document text
+ fireTextChanged = true;
+ DelayedEvents delayedEvents = new DelayedEvents();
+
+ lock (lockObject) {
+ // create linked list of checkpoints, if required
+ if (currentCheckpoint != null) {
+ currentCheckpoint = currentCheckpoint.Append(args);
+ }
+
+ // now update the textBuffer and lineTree
+ if (offset == 0 && length == rope.Length) {
+ // optimize replacing the whole document
+ rope.Clear();
+ rope.InsertText(0, newText);
+ lineManager.Rebuild();
+ } else {
+ rope.RemoveRange(offset, length);
+ lineManager.Remove(offset, length);
+ #if DEBUG
+ lineTree.CheckProperties();
+ #endif
+ rope.InsertText(offset, newText);
+ lineManager.Insert(offset, newText);
+ #if DEBUG
+ lineTree.CheckProperties();
+ #endif
+ }
+ }
+
+ // update text anchors
+ if (offsetChangeMap == null) {
+ anchorTree.HandleTextChange(args.CreateSingleChangeMapEntry(), delayedEvents);
+ } else {
+ foreach (OffsetChangeMapEntry entry in offsetChangeMap) {
+ anchorTree.HandleTextChange(entry, delayedEvents);
+ }
+ }
+
+ // raise delayed events after our data structures are consistent again
+ delayedEvents.RaiseEvents();
+
+ // fire DocumentChanged event
+ if (Changed != null)
+ Changed(this, args);
+ }
+ #endregion
+
+ #region GetLineBy...
+ /// <summary>
+ /// Gets a read-only list of lines.
+ /// </summary>
+ /// <remarks><inheritdoc cref="DocumentLine"/></remarks>
+ public IList<DocumentLine> Lines {
+ get { return lineTree; }
+ }
+
+ /// <summary>
+ /// Gets a line by the line number: O(log n)
+ /// </summary>
+ public DocumentLine GetLineByNumber(int number)
+ {
+ VerifyAccess();
+ if (number < 1 || number > lineTree.LineCount)
+ throw new ArgumentOutOfRangeException("number", number, "Value must be between 1 and " + lineTree.LineCount);
+ return lineTree.GetByNumber(number);
+ }
+
+ /// <summary>
+ /// Gets a document lines by offset.
+ /// Runtime: O(log n)
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Int32.ToString")]
+ public DocumentLine GetLineByOffset(int offset)
+ {
+ VerifyAccess();
+ if (offset < 0 || offset > rope.Length) {
+ throw new ArgumentOutOfRangeException("offset", offset, "0 <= offset <= " + rope.Length.ToString());
+ }
+ return lineTree.GetByOffset(offset);
+ }
+ #endregion
+
+ /// <summary>
+ /// Gets the offset from a text location.
+ /// </summary>
+ /// <seealso cref="GetLocation"/>
+ public int GetOffset(TextLocation location)
+ {
+ return GetOffset(location.Line, location.Column);
+ }
+
+ /// <summary>
+ /// Gets the offset from a text location.
+ /// </summary>
+ /// <seealso cref="GetLocation"/>
+ public int GetOffset(int line, int column)
+ {
+ DocumentLine docLine = GetLineByNumber(line);
+ if (column <= 0)
+ return docLine.Offset;
+ if (column > docLine.Length)
+ return docLine.EndOffset;
+ return docLine.Offset + column - 1;
+ }
+
+ /// <summary>
+ /// Gets the location from an offset.
+ /// </summary>
+ /// <seealso cref="GetOffset(TextLocation)"/>
+ public TextLocation GetLocation(int offset)
+ {
+ DocumentLine line = GetLineByOffset(offset);
+ return new TextLocation(line.LineNumber, offset - line.Offset + 1);
+ }
+
+ readonly ObservableCollection<ILineTracker> lineTrackers = new ObservableCollection<ILineTracker>();
+
+ /// <summary>
+ /// Gets the list of <see cref="ILineTracker"/>s attached to this document.
+ /// You can add custom line trackers to this list.
+ /// </summary>
+ public IList<ILineTracker> LineTrackers {
+ get {
+ VerifyAccess();
+ return lineTrackers;
+ }
+ }
+
+ UndoStack undoStack;
+
+ /// <summary>
+ /// Gets the <see cref="UndoStack"/> of the document.
+ /// </summary>
+ /// <remarks>This property can also be used to set the undo stack, e.g. for sharing a common undo stack between multiple documents.</remarks>
+ public UndoStack UndoStack {
+ get { return undoStack; }
+ set {
+ if (value == null)
+ throw new ArgumentNullException();
+ if (value != undoStack) {
+ undoStack.ClearAll(); // first clear old undo stack, so that it can't be used to perform unexpected changes on this document
+ // ClearAll() will also throw an exception when it's not safe to replace the undo stack (e.g. update is currently in progress)
+ undoStack = value;
+ OnPropertyChanged("UndoStack");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="TextAnchor"/> at the specified offset.
+ /// </summary>
+ /// <inheritdoc cref="TextAnchor" select="remarks|example"/>
+ public TextAnchor CreateAnchor(int offset)
+ {
+ VerifyAccess();
+ if (offset < 0 || offset > rope.Length) {
+ throw new ArgumentOutOfRangeException("offset", offset, "0 <= offset <= " + rope.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ return anchorTree.CreateAnchor(offset);
+ }
+
+ #region LineCount
+ /// <summary>
+ /// Gets the total number of lines in the document.
+ /// Runtime: O(1).
+ /// </summary>
+ public int LineCount {
+ get {
+ VerifyAccess();
+ return lineTree.LineCount;
+ }
+ }
+
+ /// <summary>
+ /// Is raised when the LineCount property changes.
+ /// </summary>
+ [Obsolete("This event will be removed in a future version; use the PropertyChanged event instead")]
+ public event EventHandler LineCountChanged;
+ #endregion
+
+ #region Debugging
+ [Conditional("DEBUG")]
+ internal void DebugVerifyAccess()
+ {
+ VerifyAccess();
+ }
+
+ /// <summary>
+ /// Gets the document lines tree in string form.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ internal string GetLineTreeAsString()
+ {
+ #if DEBUG
+ return lineTree.GetTreeAsString();
+ #else
+ return "Not available in release build.";
+ #endif
+ }
+
+ /// <summary>
+ /// Gets the text anchor tree in string form.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ internal string GetTextAnchorTreeAsString()
+ {
+ #if DEBUG
+ return anchorTree.GetTreeAsString();
+ #else
+ return "Not available in release build.";
+ #endif
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocumentWeakEventManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocumentWeakEventManager.cs
new file mode 100644
index 000000000..de304b722
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextDocumentWeakEventManager.cs
@@ -0,0 +1,149 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Contains weak event managers for the TextDocument events.
+ /// </summary>
+ public static class TextDocumentWeakEventManager
+ {
+ /// <summary>
+ /// Weak event manager for the <see cref="TextDocument.UpdateStarted"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class UpdateStarted : WeakEventManagerBase<UpdateStarted, TextDocument>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextDocument source)
+ {
+ source.UpdateStarted += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextDocument source)
+ {
+ source.UpdateStarted -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextDocument.UpdateFinished"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class UpdateFinished : WeakEventManagerBase<UpdateFinished, TextDocument>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextDocument source)
+ {
+ source.UpdateFinished += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextDocument source)
+ {
+ source.UpdateFinished -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextDocument.Changing"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class Changing : WeakEventManagerBase<Changing, TextDocument>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextDocument source)
+ {
+ source.Changing += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextDocument source)
+ {
+ source.Changing -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextDocument.Changed"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class Changed : WeakEventManagerBase<Changed, TextDocument>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextDocument source)
+ {
+ source.Changed += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextDocument source)
+ {
+ source.Changed -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextDocument.LineCountChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ [Obsolete("The TextDocument.LineCountChanged event will be removed in a future version. Use PropertyChangedEventManager instead.")]
+ public sealed class LineCountChanged : WeakEventManagerBase<LineCountChanged, TextDocument>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextDocument source)
+ {
+ source.LineCountChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextDocument source)
+ {
+ source.LineCountChanged -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextDocument.TextLengthChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ [Obsolete("The TextDocument.TextLengthChanged event will be removed in a future version. Use PropertyChangedEventManager instead.")]
+ public sealed class TextLengthChanged : WeakEventManagerBase<TextLengthChanged, TextDocument>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextDocument source)
+ {
+ source.TextLengthChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextDocument source)
+ {
+ source.TextLengthChanged -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextDocument.TextChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class TextChanged : WeakEventManagerBase<TextChanged, TextDocument>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextDocument source)
+ {
+ source.TextChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextDocument source)
+ {
+ source.TextChanged -= DeliverEvent;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextLocation.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextLocation.cs
new file mode 100644
index 000000000..1d3c2564c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextLocation.cs
@@ -0,0 +1,166 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// A line/column position.
+ /// Text editor lines/columns are counted started from one.
+ /// </summary>
+ /// <remarks>
+ /// The document provides the methods <see cref="TextDocument.GetLocation"/> and
+ /// <see cref="TextDocument.GetOffset(TextLocation)"/> to convert between offsets and TextLocations.
+ /// </remarks>
+ public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation>
+ {
+ /// <summary>
+ /// Represents no text location (0, 0).
+ /// </summary>
+ public static readonly TextLocation Empty = new TextLocation(0, 0);
+
+ /// <summary>
+ /// Creates a TextLocation instance.
+ /// <para>
+ /// Warning: the parameters are (line, column).
+ /// Not (column, line) as in ICSharpCode.TextEditor!
+ /// </para>
+ /// </summary>
+ public TextLocation(int line, int column)
+ {
+ y = line;
+ x = column;
+ }
+
+ int x, y;
+
+ /// <summary>
+ /// Gets the line number.
+ /// </summary>
+ public int Line {
+ get { return y; }
+ }
+
+ /// <summary>
+ /// Gets the column number.
+ /// </summary>
+ public int Column {
+ get { return x; }
+ }
+
+ /// <summary>
+ /// Gets whether the TextLocation instance is empty.
+ /// </summary>
+ public bool IsEmpty {
+ get {
+ return x <= 0 && y <= 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets a string representation for debugging purposes.
+ /// </summary>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "(Line {1}, Col {0})", this.x, this.y);
+ }
+
+ /// <summary>
+ /// Gets a hash code.
+ /// </summary>
+ public override int GetHashCode()
+ {
+ return unchecked (87 * x.GetHashCode() ^ y.GetHashCode());
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public override bool Equals(object obj)
+ {
+ if (!(obj is TextLocation)) return false;
+ return (TextLocation)obj == this;
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public bool Equals(TextLocation other)
+ {
+ return this == other;
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public static bool operator ==(TextLocation left, TextLocation right)
+ {
+ return left.x == right.x && left.y == right.y;
+ }
+
+ /// <summary>
+ /// Inequality test.
+ /// </summary>
+ public static bool operator !=(TextLocation left, TextLocation right)
+ {
+ return left.x != right.x || left.y != right.y;
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator <(TextLocation left, TextLocation right)
+ {
+ if (left.y < right.y)
+ return true;
+ else if (left.y == right.y)
+ return left.x < right.x;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator >(TextLocation left, TextLocation right)
+ {
+ if (left.y > right.y)
+ return true;
+ else if (left.y == right.y)
+ return left.x > right.x;
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator <=(TextLocation left, TextLocation right)
+ {
+ return !(left > right);
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public static bool operator >=(TextLocation left, TextLocation right)
+ {
+ return !(left < right);
+ }
+
+ /// <summary>
+ /// Compares two text locations.
+ /// </summary>
+ public int CompareTo(TextLocation other)
+ {
+ if (this == other)
+ return 0;
+ if (this < other)
+ return -1;
+ else
+ return 1;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegment.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegment.cs
new file mode 100644
index 000000000..d1834c214
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegment.cs
@@ -0,0 +1,248 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// A segment that can be put into a <see cref="TextSegmentCollection{T}"/>.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// A <see cref="TextSegment"/> can be stand-alone or part of a <see cref="TextSegmentCollection{T}"/>.
+ /// If the segment is stored inside a TextSegmentCollection, its Offset and Length will be updated by that collection.
+ /// </para>
+ /// <para>
+ /// When the document changes, the offsets of all text segments in the TextSegmentCollection will be adjusted accordingly.
+ /// Start offsets move like <see cref="AnchorMovementType">AnchorMovementType.AfterInsertion</see>,
+ /// end offsets move like <see cref="AnchorMovementType">AnchorMovementType.BeforeInsertion</see>
+ /// (i.e. the segment will always stay as small as possible).</para>
+ /// <para>
+ /// If a document change causes a segment to be deleted completely, it will be reduced to length 0, but segments are
+ /// never automatically removed from the collection.
+ /// Segments with length 0 will never expand due to document changes, and they move as <c>AfterInsertion</c>.
+ /// </para>
+ /// <para>
+ /// Thread-safety: a TextSegmentCollection that is connected to a <see cref="TextDocument"/> may only be used on that document's owner thread.
+ /// A disconnected TextSegmentCollection is safe for concurrent reads, but concurrent access is not safe when there are writes.
+ /// Keep in mind that reading the Offset properties of a text segment inside the collection is a read access on the
+ /// collection; and setting an Offset property of a text segment is a write access on the collection.
+ /// </para>
+ /// </remarks>
+ /// <seealso cref="ISegment"/>
+ /// <seealso cref="AnchorSegment"/>
+ /// <seealso cref="TextSegmentCollection{T}"/>
+ public class TextSegment : ISegment
+ {
+ internal ISegmentTree ownerTree;
+ internal TextSegment left, right, parent;
+
+ /// <summary>
+ /// The color of the segment in the red/black tree.
+ /// </summary>
+ internal bool color;
+
+ /// <summary>
+ /// The "length" of the node (distance to previous node)
+ /// </summary>
+ internal int nodeLength;
+
+ /// <summary>
+ /// The total "length" of this subtree.
+ /// </summary>
+ internal int totalNodeLength; // totalNodeLength = nodeLength + left.totalNodeLength + right.totalNodeLength
+
+ /// <summary>
+ /// The length of the segment (do not confuse with nodeLength).
+ /// </summary>
+ internal int segmentLength;
+
+ /// <summary>
+ /// distanceToMaxEnd = Max(segmentLength,
+ /// left.distanceToMaxEnd + left.Offset - Offset,
+ /// left.distanceToMaxEnd + right.Offset - Offset)
+ /// </summary>
+ internal int distanceToMaxEnd;
+
+ int ISegment.Offset {
+ get { return StartOffset; }
+ }
+
+ /// <summary>
+ /// Gets whether this segment is connected to a TextSegmentCollection and will automatically
+ /// update its offsets.
+ /// </summary>
+ protected bool IsConnectedToCollection {
+ get {
+ return ownerTree != null;
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the start offset of the segment.
+ /// </summary>
+ /// <remarks>
+ /// When setting the start offset, the end offset will change, too: the Length of the segment will stay constant.
+ /// </remarks>
+ public int StartOffset {
+ get {
+ // If the segment is not connected to a tree, we store the offset in "nodeLength".
+ // Otherwise, "nodeLength" contains the distance to the start offset of the previous node
+ Debug.Assert(!(ownerTree == null && parent != null));
+ Debug.Assert(!(ownerTree == null && left != null));
+
+ TextSegment n = this;
+ int offset = n.nodeLength;
+ if (n.left != null)
+ offset += n.left.totalNodeLength;
+ while (n.parent != null) {
+ if (n == n.parent.right) {
+ if (n.parent.left != null)
+ offset += n.parent.left.totalNodeLength;
+ offset += n.parent.nodeLength;
+ }
+ n = n.parent;
+ }
+ return offset;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException("value", "Offset must not be negative");
+ if (this.StartOffset != value) {
+ // need a copy of the variable because ownerTree.Remove() sets this.ownerTree to null
+ ISegmentTree ownerTree = this.ownerTree;
+ if (ownerTree != null) {
+ ownerTree.Remove(this);
+ nodeLength = value;
+ ownerTree.Add(this);
+ } else {
+ nodeLength = value;
+ }
+ OnSegmentChanged();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the end offset of the segment.
+ /// </summary>
+ /// <remarks>
+ /// Setting the end offset will change the length, the start offset will stay constant.
+ /// </remarks>
+ public int EndOffset {
+ get {
+ return StartOffset + Length;
+ }
+ set {
+ int newLength = value - StartOffset;
+ if (newLength < 0)
+ throw new ArgumentOutOfRangeException("value", "EndOffset must be greater or equal to StartOffset");
+ Length = newLength;
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the length of the segment.
+ /// </summary>
+ /// <remarks>
+ /// Setting the length will change the end offset, the start offset will stay constant.
+ /// </remarks>
+ public int Length {
+ get {
+ return segmentLength;
+ }
+ set {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException("value", "Length must not be negative");
+ if (segmentLength != value) {
+ segmentLength = value;
+ if (ownerTree != null)
+ ownerTree.UpdateAugmentedData(this);
+ OnSegmentChanged();
+ }
+ }
+ }
+
+ /// <summary>
+ /// This method gets called when the StartOffset/Length/EndOffset properties are set.
+ /// It is not called when StartOffset/Length/EndOffset change due to document changes
+ /// </summary>
+ protected virtual void OnSegmentChanged()
+ {
+ }
+
+ internal TextSegment LeftMost {
+ get {
+ TextSegment node = this;
+ while (node.left != null)
+ node = node.left;
+ return node;
+ }
+ }
+
+ internal TextSegment RightMost {
+ get {
+ TextSegment node = this;
+ while (node.right != null)
+ node = node.right;
+ return node;
+ }
+ }
+
+ /// <summary>
+ /// Gets the inorder successor of the node.
+ /// </summary>
+ internal TextSegment Successor {
+ get {
+ if (right != null) {
+ return right.LeftMost;
+ } else {
+ TextSegment node = this;
+ TextSegment oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // go up until we are coming out of a left subtree
+ } while (node != null && node.right == oldNode);
+ return node;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the inorder predecessor of the node.
+ /// </summary>
+ internal TextSegment Predecessor {
+ get {
+ if (left != null) {
+ return left.RightMost;
+ } else {
+ TextSegment node = this;
+ TextSegment oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // go up until we are coming out of a right subtree
+ } while (node != null && node.left == oldNode);
+ return node;
+ }
+ }
+ }
+
+ #if DEBUG
+ internal string ToDebugString()
+ {
+ return "[nodeLength=" + nodeLength + " totalNodeLength=" + totalNodeLength
+ + " distanceToMaxEnd=" + distanceToMaxEnd + " MaxEndOffset=" + (StartOffset + distanceToMaxEnd) + "]";
+ }
+ #endif
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[" + GetType().Name + " Offset=" + StartOffset + " Length=" + Length + " EndOffset=" + EndOffset + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegmentCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegmentCollection.cs
new file mode 100644
index 000000000..549dbb77d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextSegmentCollection.cs
@@ -0,0 +1,971 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Windows;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Interface to allow TextSegments to access the TextSegmentCollection - we cannot use a direct reference
+ /// because TextSegmentCollection is generic.
+ /// </summary>
+ interface ISegmentTree
+ {
+ void Add(TextSegment s);
+ void Remove(TextSegment s);
+ void UpdateAugmentedData(TextSegment s);
+ }
+
+ /// <summary>
+ /// <para>
+ /// A collection of text segments that supports efficient lookup of segments
+ /// intersecting with another segment.
+ /// </para>
+ /// </summary>
+ /// <remarks><inheritdoc cref="TextSegment"/></remarks>
+ /// <see cref="TextSegment"/>
+ public sealed class TextSegmentCollection<T> : ICollection<T>, ISegmentTree, IWeakEventListener where T : TextSegment
+ {
+ // Implementation: this is basically a mixture of an augmented interval tree
+ // and the TextAnchorTree.
+
+ // WARNING: you need to understand interval trees (the version with the augmented 'high'/'max' field)
+ // and how the TextAnchorTree works before you have any chance of understanding this code.
+
+ // This means that every node holds two "segments":
+ // one like the segments in the text anchor tree to support efficient offset changes
+ // and another that is the interval as seen by the user
+
+ // So basically, the tree contains a list of contiguous node segments of the first kind,
+ // with interval segments starting at the end of every node segment.
+
+ // Performance:
+ // Add is O(lg n)
+ // Remove is O(lg n)
+ // DocumentChanged is O(m * lg n), with m the number of segments that intersect with the changed document section
+ // FindFirstSegmentWithStartAfter is O(m + lg n) with m being the number of segments at the same offset as the result segment
+ // FindIntersectingSegments is O(m + lg n) with m being the number of intersecting segments.
+
+ int count;
+ TextSegment root;
+ bool isConnectedToDocument;
+
+ #region Constructor
+ /// <summary>
+ /// Creates a new TextSegmentCollection that needs manual calls to <see cref="UpdateOffsets(DocumentChangeEventArgs)"/>.
+ /// </summary>
+ public TextSegmentCollection()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new TextSegmentCollection that updates the offsets automatically.
+ /// </summary>
+ /// <param name="textDocument">The document to which the text segments
+ /// that will be added to the tree belong. When the document changes, the
+ /// position of the text segments will be updated accordingly.</param>
+ public TextSegmentCollection(TextDocument textDocument)
+ {
+ if (textDocument == null)
+ throw new ArgumentNullException("textDocument");
+
+ textDocument.VerifyAccess();
+ isConnectedToDocument = true;
+ TextDocumentWeakEventManager.Changed.AddListener(textDocument, this);
+ }
+ #endregion
+
+ #region OnDocumentChanged / UpdateOffsets
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(TextDocumentWeakEventManager.Changed)) {
+ OnDocumentChanged((DocumentChangeEventArgs)e);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Updates the start and end offsets of all segments stored in this collection.
+ /// </summary>
+ /// <param name="e">DocumentChangeEventArgs instance describing the change to the document.</param>
+ public void UpdateOffsets(DocumentChangeEventArgs e)
+ {
+ if (e == null)
+ throw new ArgumentNullException("e");
+ if (isConnectedToDocument)
+ throw new InvalidOperationException("This TextSegmentCollection will automatically update offsets; do not call UpdateOffsets manually!");
+ OnDocumentChanged(e);
+ CheckProperties();
+ }
+
+ void OnDocumentChanged(DocumentChangeEventArgs e)
+ {
+ OffsetChangeMap map = e.OffsetChangeMapOrNull;
+ if (map != null) {
+ foreach (OffsetChangeMapEntry entry in map) {
+ UpdateOffsetsInternal(entry);
+ }
+ } else {
+ UpdateOffsetsInternal(e.CreateSingleChangeMapEntry());
+ }
+ }
+
+ /// <summary>
+ /// Updates the start and end offsets of all segments stored in this collection.
+ /// </summary>
+ /// <param name="change">OffsetChangeMapEntry instance describing the change to the document.</param>
+ public void UpdateOffsets(OffsetChangeMapEntry change)
+ {
+ if (isConnectedToDocument)
+ throw new InvalidOperationException("This TextSegmentCollection will automatically update offsets; do not call UpdateOffsets manually!");
+ UpdateOffsetsInternal(change);
+ CheckProperties();
+ }
+ #endregion
+
+ #region UpdateOffsets (implementation)
+ void UpdateOffsetsInternal(OffsetChangeMapEntry change)
+ {
+ // Special case pure insertions, because they don't always cause a text segment to increase in size when the replaced region
+ // is inside a segment (when offset is at start or end of a text semgent).
+ if (change.RemovalLength == 0) {
+ InsertText(change.Offset, change.InsertionLength);
+ } else {
+ ReplaceText(change);
+ }
+ }
+
+ void InsertText(int offset, int length)
+ {
+ if (length == 0)
+ return;
+
+ // enlarge segments that contain offset (excluding those that have offset as endpoint)
+ foreach (TextSegment segment in FindSegmentsContaining(offset)) {
+ if (segment.StartOffset < offset && offset < segment.EndOffset) {
+ segment.Length += length;
+ }
+ }
+
+ // move start offsets of all segments >= offset
+ TextSegment node = FindFirstSegmentWithStartAfter(offset);
+ if (node != null) {
+ node.nodeLength += length;
+ UpdateAugmentedData(node);
+ }
+ }
+
+ void ReplaceText(OffsetChangeMapEntry change)
+ {
+ Debug.Assert(change.RemovalLength > 0);
+ int offset = change.Offset;
+ foreach (TextSegment segment in FindOverlappingSegments(offset, change.RemovalLength)) {
+ if (segment.StartOffset <= offset) {
+ if (segment.EndOffset >= offset + change.RemovalLength) {
+ // Replacement inside segment: adjust segment length
+ segment.Length += change.InsertionLength - change.RemovalLength;
+ } else {
+ // Replacement starting inside segment and ending after segment end: set segment end to removal position
+ //segment.EndOffset = offset;
+ segment.Length = offset - segment.StartOffset;
+ }
+ } else {
+ // Replacement starting in front of text segment and running into segment.
+ // Keep segment.EndOffset constant and move segment.StartOffset to the end of the replacement
+ int remainingLength = segment.EndOffset - (offset + change.RemovalLength);
+ RemoveSegment(segment);
+ segment.StartOffset = offset + change.RemovalLength;
+ segment.Length = Math.Max(0, remainingLength);
+ AddSegment(segment);
+ }
+ }
+ // move start offsets of all segments > offset
+ TextSegment node = FindFirstSegmentWithStartAfter(offset + 1);
+ if (node != null) {
+ Debug.Assert(node.nodeLength >= change.RemovalLength);
+ node.nodeLength += change.InsertionLength - change.RemovalLength;
+ UpdateAugmentedData(node);
+ }
+ }
+ #endregion
+
+ #region Add
+ /// <summary>
+ /// Adds the specified segment to the tree. This will cause the segment to update when the
+ /// document changes.
+ /// </summary>
+ public void Add(T item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+ if (item.ownerTree != null)
+ throw new ArgumentException("The segment is already added to a SegmentCollection.");
+ AddSegment(item);
+ }
+
+ void ISegmentTree.Add(TextSegment s)
+ {
+ AddSegment(s);
+ }
+
+ void AddSegment(TextSegment node)
+ {
+ int insertionOffset = node.StartOffset;
+ node.distanceToMaxEnd = node.segmentLength;
+ if (root == null) {
+ root = node;
+ node.totalNodeLength = node.nodeLength;
+ } else if (insertionOffset >= root.totalNodeLength) {
+ // append segment at end of tree
+ node.nodeLength = node.totalNodeLength = insertionOffset - root.totalNodeLength;
+ InsertAsRight(root.RightMost, node);
+ } else {
+ // insert in middle of tree
+ TextSegment n = FindNode(ref insertionOffset);
+ Debug.Assert(insertionOffset < n.nodeLength);
+ // split node segment 'n' at offset
+ node.totalNodeLength = node.nodeLength = insertionOffset;
+ n.nodeLength -= insertionOffset;
+ InsertBefore(n, node);
+ }
+ node.ownerTree = this;
+ count++;
+ CheckProperties();
+ }
+
+ void InsertBefore(TextSegment node, TextSegment newNode)
+ {
+ if (node.left == null) {
+ InsertAsLeft(node, newNode);
+ } else {
+ InsertAsRight(node.left.RightMost, newNode);
+ }
+ }
+ #endregion
+
+ #region GetNextSegment / GetPreviousSegment
+ /// <summary>
+ /// Gets the next segment after the specified segment.
+ /// Segments are sorted by their start offset.
+ /// Returns null if segment is the last segment.
+ /// </summary>
+ public T GetNextSegment(T segment)
+ {
+ if (!Contains(segment))
+ throw new ArgumentException("segment is not inside the segment tree");
+ return (T)segment.Successor;
+ }
+
+ /// <summary>
+ /// Gets the previous segment before the specified segment.
+ /// Segments are sorted by their start offset.
+ /// Returns null if segment is the first segment.
+ /// </summary>
+ public T GetPreviousSegment(T segment)
+ {
+ if (!Contains(segment))
+ throw new ArgumentException("segment is not inside the segment tree");
+ return (T)segment.Predecessor;
+ }
+ #endregion
+
+ #region FirstSegment/LastSegment
+ /// <summary>
+ /// Returns the first segment in the collection or null, if the collection is empty.
+ /// </summary>
+ public T FirstSegment {
+ get {
+ return root == null ? null : (T)root.LeftMost;
+ }
+ }
+
+ /// <summary>
+ /// Returns the last segment in the collection or null, if the collection is empty.
+ /// </summary>
+ public T LastSegment {
+ get {
+ return root == null ? null : (T)root.RightMost;
+ }
+ }
+ #endregion
+
+ #region FindFirstSegmentWithStartAfter
+ /// <summary>
+ /// Gets the first segment with a start offset greater or equal to <paramref name="startOffset"/>.
+ /// Returns null if no such segment is found.
+ /// </summary>
+ public T FindFirstSegmentWithStartAfter(int startOffset)
+ {
+ if (root == null)
+ return null;
+ if (startOffset <= 0)
+ return (T)root.LeftMost;
+ TextSegment s = FindNode(ref startOffset);
+ // startOffset means that the previous segment is starting at the offset we were looking for
+ while (startOffset == 0) {
+ TextSegment p = (s == null) ? root.RightMost : s.Predecessor;
+ // There must always be a predecessor: if we were looking for the first node, we would have already
+ // returned it as root.LeftMost above.
+ Debug.Assert(p != null);
+ startOffset += p.nodeLength;
+ s = p;
+ }
+ return (T)s;
+ }
+
+ /// <summary>
+ /// Finds the node at the specified offset.
+ /// After the method has run, offset is relative to the beginning of the returned node.
+ /// </summary>
+ TextSegment FindNode(ref int offset)
+ {
+ TextSegment n = root;
+ while (true) {
+ if (n.left != null) {
+ if (offset < n.left.totalNodeLength) {
+ n = n.left; // descend into left subtree
+ continue;
+ } else {
+ offset -= n.left.totalNodeLength; // skip left subtree
+ }
+ }
+ if (offset < n.nodeLength) {
+ return n; // found correct node
+ } else {
+ offset -= n.nodeLength; // skip this node
+ }
+ if (n.right != null) {
+ n = n.right; // descend into right subtree
+ } else {
+ // didn't find any node containing the offset
+ return null;
+ }
+ }
+ }
+ #endregion
+
+ #region FindOverlappingSegments
+ /// <summary>
+ /// Finds all segments that contain the given offset.
+ /// (StartOffset &lt;= offset &lt;= EndOffset)
+ /// Segments are returned in the order given by GetNextSegment/GetPreviousSegment.
+ /// </summary>
+ /// <returns>Returns a new collection containing the results of the query.
+ /// This means it is safe to modify the TextSegmentCollection while iterating through the result collection.</returns>
+ public ReadOnlyCollection<T> FindSegmentsContaining(int offset)
+ {
+ return FindOverlappingSegments(offset, 0);
+ }
+
+ /// <summary>
+ /// Finds all segments that overlap with the given segment (including touching segments).
+ /// </summary>
+ /// <returns>Returns a new collection containing the results of the query.
+ /// This means it is safe to modify the TextSegmentCollection while iterating through the result collection.</returns>
+ public ReadOnlyCollection<T> FindOverlappingSegments(ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+ return FindOverlappingSegments(segment.Offset, segment.Length);
+ }
+
+ /// <summary>
+ /// Finds all segments that overlap with the given segment (including touching segments).
+ /// Segments are returned in the order given by GetNextSegment/GetPreviousSegment.
+ /// </summary>
+ /// <returns>Returns a new collection containing the results of the query.
+ /// This means it is safe to modify the TextSegmentCollection while iterating through the result collection.</returns>
+ public ReadOnlyCollection<T> FindOverlappingSegments(int offset, int length)
+ {
+ ThrowUtil.CheckNotNegative(length, "length");
+ List<T> results = new List<T>();
+ if (root != null) {
+ FindOverlappingSegments(results, root, offset, offset + length);
+ }
+ return results.AsReadOnly();
+ }
+
+ void FindOverlappingSegments(List<T> results, TextSegment node, int low, int high)
+ {
+ // low and high are relative to node.LeftMost startpos (not node.LeftMost.Offset)
+ if (high < 0) {
+ // node is irrelevant for search because all intervals in node are after high
+ return;
+ }
+
+ // find values relative to node.Offset
+ int nodeLow = low - node.nodeLength;
+ int nodeHigh = high - node.nodeLength;
+ if (node.left != null) {
+ nodeLow -= node.left.totalNodeLength;
+ nodeHigh -= node.left.totalNodeLength;
+ }
+
+ if (node.distanceToMaxEnd < nodeLow) {
+ // node is irrelevant for search because all intervals in node are before low
+ return;
+ }
+
+ if (node.left != null)
+ FindOverlappingSegments(results, node.left, low, high);
+
+ if (nodeHigh < 0) {
+ // node and everything in node.right is before low
+ return;
+ }
+
+ if (nodeLow <= node.segmentLength) {
+ results.Add((T)node);
+ }
+
+ if (node.right != null)
+ FindOverlappingSegments(results, node.right, nodeLow, nodeHigh);
+ }
+ #endregion
+
+ #region UpdateAugmentedData
+ void UpdateAugmentedData(TextSegment node)
+ {
+ int totalLength = node.nodeLength;
+ int distanceToMaxEnd = node.segmentLength;
+ if (node.left != null) {
+ totalLength += node.left.totalNodeLength;
+
+ int leftDTME = node.left.distanceToMaxEnd;
+ // dtme is relative, so convert it to the coordinates of node:
+ if (node.left.right != null)
+ leftDTME -= node.left.right.totalNodeLength;
+ leftDTME -= node.nodeLength;
+ if (leftDTME > distanceToMaxEnd)
+ distanceToMaxEnd = leftDTME;
+ }
+ if (node.right != null) {
+ totalLength += node.right.totalNodeLength;
+
+ int rightDTME = node.right.distanceToMaxEnd;
+ // dtme is relative, so convert it to the coordinates of node:
+ rightDTME += node.right.nodeLength;
+ if (node.right.left != null)
+ rightDTME += node.right.left.totalNodeLength;
+ if (rightDTME > distanceToMaxEnd)
+ distanceToMaxEnd = rightDTME;
+ }
+ if (node.totalNodeLength != totalLength
+ || node.distanceToMaxEnd != distanceToMaxEnd)
+ {
+ node.totalNodeLength = totalLength;
+ node.distanceToMaxEnd = distanceToMaxEnd;
+ if (node.parent != null)
+ UpdateAugmentedData(node.parent);
+ }
+ }
+
+ void ISegmentTree.UpdateAugmentedData(TextSegment node)
+ {
+ UpdateAugmentedData(node);
+ }
+ #endregion
+
+ #region Remove
+ /// <summary>
+ /// Removes the specified segment from the tree. This will cause the segment to not update
+ /// anymore when the document changes.
+ /// </summary>
+ public bool Remove(T item)
+ {
+ if (!Contains(item))
+ return false;
+ RemoveSegment(item);
+ return true;
+ }
+
+ void ISegmentTree.Remove(TextSegment s)
+ {
+ RemoveSegment(s);
+ }
+
+ void RemoveSegment(TextSegment s)
+ {
+ int oldOffset = s.StartOffset;
+ TextSegment successor = s.Successor;
+ if (successor != null)
+ successor.nodeLength += s.nodeLength;
+ RemoveNode(s);
+ if (successor != null)
+ UpdateAugmentedData(successor);
+ Disconnect(s, oldOffset);
+ CheckProperties();
+ }
+
+ void Disconnect(TextSegment s, int offset)
+ {
+ s.left = s.right = s.parent = null;
+ s.ownerTree = null;
+ s.nodeLength = offset;
+ count--;
+ }
+
+ /// <summary>
+ /// Removes all segments from the tree.
+ /// </summary>
+ public void Clear()
+ {
+ T[] segments = this.ToArray();
+ root = null;
+ int offset = 0;
+ foreach (TextSegment s in segments) {
+ offset += s.nodeLength;
+ Disconnect(s, offset);
+ }
+ CheckProperties();
+ }
+ #endregion
+
+ #region CheckProperties
+ [Conditional("DATACONSISTENCYTEST")]
+ internal void CheckProperties()
+ {
+ #if DEBUG
+ if (root != null) {
+ CheckProperties(root);
+
+ // check red-black property:
+ int blackCount = -1;
+ CheckNodeProperties(root, null, RED, 0, ref blackCount);
+ }
+
+ int expectedCount = 0;
+ // we cannot trust LINQ not to call ICollection.Count, so we need this loop
+ // to count the elements in the tree
+ using (IEnumerator<T> en = GetEnumerator()) {
+ while (en.MoveNext()) expectedCount++;
+ }
+ Debug.Assert(count == expectedCount);
+ #endif
+ }
+
+ #if DEBUG
+ void CheckProperties(TextSegment node)
+ {
+ int totalLength = node.nodeLength;
+ int distanceToMaxEnd = node.segmentLength;
+ if (node.left != null) {
+ CheckProperties(node.left);
+ totalLength += node.left.totalNodeLength;
+ distanceToMaxEnd = Math.Max(distanceToMaxEnd,
+ node.left.distanceToMaxEnd + node.left.StartOffset - node.StartOffset);
+ }
+ if (node.right != null) {
+ CheckProperties(node.right);
+ totalLength += node.right.totalNodeLength;
+ distanceToMaxEnd = Math.Max(distanceToMaxEnd,
+ node.right.distanceToMaxEnd + node.right.StartOffset - node.StartOffset);
+ }
+ Debug.Assert(node.totalNodeLength == totalLength);
+ Debug.Assert(node.distanceToMaxEnd == distanceToMaxEnd);
+ }
+
+ /*
+ 1. A node is either red or black.
+ 2. The root is black.
+ 3. All leaves are black. (The leaves are the NIL children.)
+ 4. Both children of every red node are black. (So every red node must have a black parent.)
+ 5. Every simple path from a node to a descendant leaf contains the same number of black nodes. (Not counting the leaf node.)
+ */
+ void CheckNodeProperties(TextSegment node, TextSegment parentNode, bool parentColor, int blackCount, ref int expectedBlackCount)
+ {
+ if (node == null) return;
+
+ Debug.Assert(node.parent == parentNode);
+
+ if (parentColor == RED) {
+ Debug.Assert(node.color == BLACK);
+ }
+ if (node.color == BLACK) {
+ blackCount++;
+ }
+ if (node.left == null && node.right == null) {
+ // node is a leaf node:
+ if (expectedBlackCount == -1)
+ expectedBlackCount = blackCount;
+ else
+ Debug.Assert(expectedBlackCount == blackCount);
+ }
+ CheckNodeProperties(node.left, node, node.color, blackCount, ref expectedBlackCount);
+ CheckNodeProperties(node.right, node, node.color, blackCount, ref expectedBlackCount);
+ }
+
+ static void AppendTreeToString(TextSegment node, StringBuilder b, int indent)
+ {
+ if (node.color == RED)
+ b.Append("RED ");
+ else
+ b.Append("BLACK ");
+ b.AppendLine(node.ToString() + node.ToDebugString());
+ indent += 2;
+ if (node.left != null) {
+ b.Append(' ', indent);
+ b.Append("L: ");
+ AppendTreeToString(node.left, b, indent);
+ }
+ if (node.right != null) {
+ b.Append(' ', indent);
+ b.Append("R: ");
+ AppendTreeToString(node.right, b, indent);
+ }
+ }
+ #endif
+
+ internal string GetTreeAsString()
+ {
+ #if DEBUG
+ StringBuilder b = new StringBuilder();
+ if (root != null)
+ AppendTreeToString(root, b, 0);
+ return b.ToString();
+ #else
+ return "Not available in release build.";
+ #endif
+ }
+ #endregion
+
+ #region Red/Black Tree
+ internal const bool RED = true;
+ internal const bool BLACK = false;
+
+ void InsertAsLeft(TextSegment parentNode, TextSegment newNode)
+ {
+ Debug.Assert(parentNode.left == null);
+ parentNode.left = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAugmentedData(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void InsertAsRight(TextSegment parentNode, TextSegment newNode)
+ {
+ Debug.Assert(parentNode.right == null);
+ parentNode.right = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAugmentedData(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void FixTreeOnInsert(TextSegment node)
+ {
+ Debug.Assert(node != null);
+ Debug.Assert(node.color == RED);
+ Debug.Assert(node.left == null || node.left.color == BLACK);
+ Debug.Assert(node.right == null || node.right.color == BLACK);
+
+ TextSegment parentNode = node.parent;
+ if (parentNode == null) {
+ // we inserted in the root -> the node must be black
+ // since this is a root node, making the node black increments the number of black nodes
+ // on all paths by one, so it is still the same for all paths.
+ node.color = BLACK;
+ return;
+ }
+ if (parentNode.color == BLACK) {
+ // if the parent node where we inserted was black, our red node is placed correctly.
+ // since we inserted a red node, the number of black nodes on each path is unchanged
+ // -> the tree is still balanced
+ return;
+ }
+ // parentNode is red, so there is a conflict here!
+
+ // because the root is black, parentNode is not the root -> there is a grandparent node
+ TextSegment grandparentNode = parentNode.parent;
+ TextSegment uncleNode = Sibling(parentNode);
+ if (uncleNode != null && uncleNode.color == RED) {
+ parentNode.color = BLACK;
+ uncleNode.color = BLACK;
+ grandparentNode.color = RED;
+ FixTreeOnInsert(grandparentNode);
+ return;
+ }
+ // now we know: parent is red but uncle is black
+ // First rotation:
+ if (node == parentNode.right && parentNode == grandparentNode.left) {
+ RotateLeft(parentNode);
+ node = node.left;
+ } else if (node == parentNode.left && parentNode == grandparentNode.right) {
+ RotateRight(parentNode);
+ node = node.right;
+ }
+ // because node might have changed, reassign variables:
+ parentNode = node.parent;
+ grandparentNode = parentNode.parent;
+
+ // Now recolor a bit:
+ parentNode.color = BLACK;
+ grandparentNode.color = RED;
+ // Second rotation:
+ if (node == parentNode.left && parentNode == grandparentNode.left) {
+ RotateRight(grandparentNode);
+ } else {
+ // because of the first rotation, this is guaranteed:
+ Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right);
+ RotateLeft(grandparentNode);
+ }
+ }
+
+ void RemoveNode(TextSegment removedNode)
+ {
+ if (removedNode.left != null && removedNode.right != null) {
+ // replace removedNode with it's in-order successor
+
+ TextSegment leftMost = removedNode.right.LeftMost;
+ RemoveNode(leftMost); // remove leftMost from its current location
+
+ // and overwrite the removedNode with it
+ ReplaceNode(removedNode, leftMost);
+ leftMost.left = removedNode.left;
+ if (leftMost.left != null) leftMost.left.parent = leftMost;
+ leftMost.right = removedNode.right;
+ if (leftMost.right != null) leftMost.right.parent = leftMost;
+ leftMost.color = removedNode.color;
+
+ UpdateAugmentedData(leftMost);
+ if (leftMost.parent != null) UpdateAugmentedData(leftMost.parent);
+ return;
+ }
+
+ // now either removedNode.left or removedNode.right is null
+ // get the remaining child
+ TextSegment parentNode = removedNode.parent;
+ TextSegment childNode = removedNode.left ?? removedNode.right;
+ ReplaceNode(removedNode, childNode);
+ if (parentNode != null) UpdateAugmentedData(parentNode);
+ if (removedNode.color == BLACK) {
+ if (childNode != null && childNode.color == RED) {
+ childNode.color = BLACK;
+ } else {
+ FixTreeOnDelete(childNode, parentNode);
+ }
+ }
+ }
+
+ void FixTreeOnDelete(TextSegment node, TextSegment parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (parentNode == null)
+ return;
+
+ // warning: node may be null
+ TextSegment sibling = Sibling(node, parentNode);
+ if (sibling.color == RED) {
+ parentNode.color = RED;
+ sibling.color = BLACK;
+ if (node == parentNode.left) {
+ RotateLeft(parentNode);
+ } else {
+ RotateRight(parentNode);
+ }
+
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+ }
+
+ if (parentNode.color == BLACK
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ FixTreeOnDelete(parentNode, parentNode.parent);
+ return;
+ }
+
+ if (parentNode.color == RED
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ parentNode.color = BLACK;
+ return;
+ }
+
+ if (node == parentNode.left &&
+ sibling.color == BLACK &&
+ GetColor(sibling.left) == RED &&
+ GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.left.color = BLACK;
+ RotateRight(sibling);
+ }
+ else if (node == parentNode.right &&
+ sibling.color == BLACK &&
+ GetColor(sibling.right) == RED &&
+ GetColor(sibling.left) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.right.color = BLACK;
+ RotateLeft(sibling);
+ }
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+
+ sibling.color = parentNode.color;
+ parentNode.color = BLACK;
+ if (node == parentNode.left) {
+ if (sibling.right != null) {
+ Debug.Assert(sibling.right.color == RED);
+ sibling.right.color = BLACK;
+ }
+ RotateLeft(parentNode);
+ } else {
+ if (sibling.left != null) {
+ Debug.Assert(sibling.left.color == RED);
+ sibling.left.color = BLACK;
+ }
+ RotateRight(parentNode);
+ }
+ }
+
+ void ReplaceNode(TextSegment replacedNode, TextSegment newNode)
+ {
+ if (replacedNode.parent == null) {
+ Debug.Assert(replacedNode == root);
+ root = newNode;
+ } else {
+ if (replacedNode.parent.left == replacedNode)
+ replacedNode.parent.left = newNode;
+ else
+ replacedNode.parent.right = newNode;
+ }
+ if (newNode != null) {
+ newNode.parent = replacedNode.parent;
+ }
+ replacedNode.parent = null;
+ }
+
+ void RotateLeft(TextSegment p)
+ {
+ // let q be p's right child
+ TextSegment q = p.right;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's right child to be q's left child
+ p.right = q.left;
+ if (p.right != null) p.right.parent = p;
+ // set q's left child to be p
+ q.left = p;
+ p.parent = q;
+ UpdateAugmentedData(p);
+ UpdateAugmentedData(q);
+ }
+
+ void RotateRight(TextSegment p)
+ {
+ // let q be p's left child
+ TextSegment q = p.left;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's left child to be q's right child
+ p.left = q.right;
+ if (p.left != null) p.left.parent = p;
+ // set q's right child to be p
+ q.right = p;
+ p.parent = q;
+ UpdateAugmentedData(p);
+ UpdateAugmentedData(q);
+ }
+
+ static TextSegment Sibling(TextSegment node)
+ {
+ if (node == node.parent.left)
+ return node.parent.right;
+ else
+ return node.parent.left;
+ }
+
+ static TextSegment Sibling(TextSegment node, TextSegment parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (node == parentNode.left)
+ return parentNode.right;
+ else
+ return parentNode.left;
+ }
+
+ static bool GetColor(TextSegment node)
+ {
+ return node != null ? node.color : BLACK;
+ }
+ #endregion
+
+ #region ICollection<T> implementation
+ /// <summary>
+ /// Gets the number of segments in the tree.
+ /// </summary>
+ public int Count {
+ get { return count; }
+ }
+
+ bool ICollection<T>.IsReadOnly {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Gets whether this tree contains the specified item.
+ /// </summary>
+ public bool Contains(T item)
+ {
+ return item != null && item.ownerTree == this;
+ }
+
+ /// <summary>
+ /// Copies all segments in this SegmentTree to the specified array.
+ /// </summary>
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (array.Length < this.Count)
+ throw new ArgumentException("The array is too small", "array");
+ if (arrayIndex < 0 || arrayIndex + count > array.Length)
+ throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, "Value must be between 0 and " + (array.Length - count));
+ foreach (T s in this) {
+ array[arrayIndex++] = s;
+ }
+ }
+
+ /// <summary>
+ /// Gets an enumerator to enumerate the segments.
+ /// </summary>
+ public IEnumerator<T> GetEnumerator()
+ {
+ if (root != null) {
+ TextSegment current = root.LeftMost;
+ while (current != null) {
+ yield return (T)current;
+ // TODO: check if collection was modified during enumeration
+ current = current.Successor;
+ }
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextUtilities.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextUtilities.cs
new file mode 100644
index 000000000..a0428c4e3
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/TextUtilities.cs
@@ -0,0 +1,332 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+using System.Windows.Documents;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Specifies the mode for getting the next caret position.
+ /// </summary>
+ public enum CaretPositioningMode
+ {
+ /// <summary>
+ /// Normal positioning (stop at every caret position)
+ /// </summary>
+ Normal,
+ /// <summary>
+ /// Stop only on word borders.
+ /// </summary>
+ WordBorder,
+ /// <summary>
+ /// Stop only at the beginning of words. This is used for Ctrl+Left/Ctrl+Right.
+ /// </summary>
+ WordStart,
+ /// <summary>
+ /// Stop only at the beginning of words, and anywhere in the middle of symbols.
+ /// </summary>
+ WordStartOrSymbol,
+ /// <summary>
+ /// Stop only on word borders, and anywhere in the middle of symbols.
+ /// </summary>
+ WordBorderOrSymbol
+ }
+
+ /// <summary>
+ /// Static helper methods for working with text.
+ /// </summary>
+ public static partial class TextUtilities
+ {
+ #region GetControlCharacterName
+ // the names of the first 32 ASCII characters = Unicode C0 block
+ static readonly string[] c0Table = {
+ "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT",
+ "LF", "VT", "FF", "CR", "SO", "SI", "DLE", "DC1", "DC2", "DC3",
+ "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS",
+ "RS", "US"
+ };
+
+ // DEL (ASCII 127) and
+ // the names of the control characters in the C1 block (Unicode 128 to 159)
+ static readonly string[] delAndC1Table = {
+ "DEL",
+ "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA", "HTS", "HTJ",
+ "VTS", "PLD", "PLU", "RI", "SS2", "SS3", "DCS", "PU1", "PU2", "STS",
+ "CCH", "MW", "SPA", "EPA", "SOS", "SGCI", "SCI", "CSI", "ST", "OSC",
+ "PM", "APC"
+ };
+
+ /// <summary>
+ /// Gets the name of the control character.
+ /// For unknown characters, the unicode codepoint is returned as 4-digit hexadecimal value.
+ /// </summary>
+ public static string GetControlCharacterName(char controlCharacter)
+ {
+ int num = (int)controlCharacter;
+ if (num < c0Table.Length)
+ return c0Table[num];
+ else if (num >= 127 && num <= 159)
+ return delAndC1Table[num - 127];
+ else
+ return num.ToString("x4", CultureInfo.InvariantCulture);
+ }
+ #endregion
+
+ #region GetWhitespace
+ /// <summary>
+ /// Gets all whitespace (' ' and '\t', but no newlines) after offset.
+ /// </summary>
+ /// <param name="textSource">The text source.</param>
+ /// <param name="offset">The offset where the whitespace starts.</param>
+ /// <returns>The segment containing the whitespace.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "WPF uses 'Whitespace'")]
+ public static ISegment GetWhitespaceAfter(ITextSource textSource, int offset)
+ {
+ if (textSource == null)
+ throw new ArgumentNullException("textSource");
+ int pos;
+ for (pos = offset; pos < textSource.TextLength; pos++) {
+ char c = textSource.GetCharAt(pos);
+ if (c != ' ' && c != '\t')
+ break;
+ }
+ return new SimpleSegment(offset, pos - offset);
+ }
+
+ /// <summary>
+ /// Gets all whitespace (' ' and '\t', but no newlines) before offset.
+ /// </summary>
+ /// <param name="textSource">The text source.</param>
+ /// <param name="offset">The offset where the whitespace ends.</param>
+ /// <returns>The segment containing the whitespace.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "WPF uses 'Whitespace'")]
+ public static ISegment GetWhitespaceBefore(ITextSource textSource, int offset)
+ {
+ if (textSource == null)
+ throw new ArgumentNullException("textSource");
+ int pos;
+ for (pos = offset - 1; pos >= 0; pos--) {
+ char c = textSource.GetCharAt(pos);
+ if (c != ' ' && c != '\t')
+ break;
+ }
+ pos++; // go back the one character that isn't whitespace
+ return new SimpleSegment(pos, offset - pos);
+ }
+
+ /// <summary>
+ /// Gets the leading whitespace segment on the document line.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "WPF uses 'Whitespace'")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
+ Justification = "Parameter cannot be ITextSource because it must belong to the DocumentLine")]
+ public static ISegment GetLeadingWhitespace(TextDocument document, DocumentLine documentLine)
+ {
+ if (documentLine == null)
+ throw new ArgumentNullException("documentLine");
+ return GetWhitespaceAfter(document, documentLine.Offset);
+ }
+
+ /// <summary>
+ /// Gets the trailing whitespace segment on the document line.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "WPF uses 'Whitespace'")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
+ Justification = "Parameter cannot be ITextSource because it must belong to the DocumentLine")]
+ public static ISegment GetTrailingWhitespace(TextDocument document, DocumentLine documentLine)
+ {
+ if (documentLine == null)
+ throw new ArgumentNullException("documentLine");
+ ISegment segment = GetWhitespaceBefore(document, documentLine.EndOffset);
+ // If the whole line consists of whitespace, we consider all of it as leading whitespace,
+ // so return an empty segment as trailing whitespace.
+ if (segment.Offset == documentLine.Offset)
+ return new SimpleSegment(documentLine.EndOffset, 0);
+ else
+ return segment;
+ }
+ #endregion
+
+ #region GetSingleIndentationSegment
+ /// <summary>
+ /// Gets a single indentation segment starting at <paramref name="offset"/> - at most one tab
+ /// or <paramref name="indentationSize"/> spaces.
+ /// </summary>
+ /// <param name="textSource">The text source.</param>
+ /// <param name="offset">The offset where the indentation segment starts.</param>
+ /// <param name="indentationSize">The size of an indentation unit. See <see cref="TextEditorOptions.IndentationSize"/>.</param>
+ /// <returns>The indentation segment.
+ /// If there is no indentation character at the specified <paramref name="offset"/>,
+ /// an empty segment is returned.</returns>
+ public static ISegment GetSingleIndentationSegment(ITextSource textSource, int offset, int indentationSize)
+ {
+ if (textSource == null)
+ throw new ArgumentNullException("textSource");
+ int pos = offset;
+ while (pos < textSource.TextLength) {
+ char c = textSource.GetCharAt(pos);
+ if (c == '\t') {
+ if (pos == offset)
+ return new SimpleSegment(offset, 1);
+ else
+ break;
+ } else if (c == ' ') {
+ if (pos - offset >= indentationSize)
+ break;
+ } else {
+ break;
+ }
+ // continue only if c==' ' and (pos-offset)<tabSize
+ pos++;
+ }
+ return new SimpleSegment(offset, pos - offset);
+ }
+ #endregion
+
+ #region GetCharacterClass
+ /// <summary>
+ /// Gets whether the character is whitespace, part of an identifier, or line terminator.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c")]
+ public static CharacterClass GetCharacterClass(char c)
+ {
+ if (c == '\r' || c == '\n')
+ return CharacterClass.LineTerminator;
+ else if (char.IsWhiteSpace(c))
+ return CharacterClass.Whitespace;
+ else if (char.IsLetterOrDigit(c) || c == '_')
+ return CharacterClass.IdentifierPart;
+ else
+ return CharacterClass.Other;
+ }
+ #endregion
+
+ #region GetNextCaretPosition
+ /// <summary>
+ /// Gets the next caret position.
+ /// </summary>
+ /// <param name="textSource">The text source.</param>
+ /// <param name="offset">The start offset inside the text source.</param>
+ /// <param name="direction">The search direction (forwards or backwards).</param>
+ /// <param name="mode">The mode for caret positioning.</param>
+ /// <returns>The offset of the next caret position, or -1 if there is no further caret position
+ /// in the text source.</returns>
+ /// <remarks>
+ /// This method is NOT equivalent to the actual caret movement when using VisualLine.GetNextCaretPosition.
+ /// In real caret movement, there are additional caret stops at line starts and ends. This method
+ /// treats linefeeds as simple whitespace.
+ /// </remarks>
+ public static int GetNextCaretPosition(ITextSource textSource, int offset, LogicalDirection direction, CaretPositioningMode mode)
+ {
+ if (textSource == null)
+ throw new ArgumentNullException("textSource");
+ if (mode != CaretPositioningMode.Normal
+ && mode != CaretPositioningMode.WordBorder
+ && mode != CaretPositioningMode.WordStart
+ && mode != CaretPositioningMode.WordBorderOrSymbol
+ && mode != CaretPositioningMode.WordStartOrSymbol)
+ {
+ throw new ArgumentException("Unsupported CaretPositioningMode: " + mode, "mode");
+ }
+ if (direction != LogicalDirection.Backward
+ && direction != LogicalDirection.Forward)
+ {
+ throw new ArgumentException("Invalid LogicalDirection: " + direction, "direction");
+ }
+ int textLength = textSource.TextLength;
+ if (textLength <= 0) {
+ // empty document? has a normal caret position at 0, though no word borders
+ if (mode == CaretPositioningMode.Normal) {
+ if (offset > 0 && direction == LogicalDirection.Backward) return 0;
+ if (offset < 0 && direction == LogicalDirection.Forward) return 0;
+ }
+ return -1;
+ }
+ while (true) {
+ int nextPos = (direction == LogicalDirection.Backward) ? offset - 1 : offset + 1;
+
+ // return -1 if there is no further caret position in the text source
+ // we also need this to handle offset values outside the valid range
+ if (nextPos < 0 || nextPos > textLength)
+ return -1;
+
+ // stop at every caret position? we can stop immediately.
+ if (mode == CaretPositioningMode.Normal)
+ return nextPos;
+ // not normal mode? we're looking for word borders...
+
+ // check if we've run against the textSource borders.
+ // a 'textSource' usually isn't the whole document, but a single VisualLineElement.
+ if (nextPos == 0) {
+ // at the document start, there's only a word border
+ // if the first character is not whitespace
+ if (!char.IsWhiteSpace(textSource.GetCharAt(0)))
+ return nextPos;
+ } else if (nextPos == textLength) {
+ // at the document end, there's never a word start
+ if (mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol) {
+ // at the document end, there's only a word border
+ // if the last character is not whitespace
+ if (!char.IsWhiteSpace(textSource.GetCharAt(textLength - 1)))
+ return nextPos;
+ }
+ } else {
+ CharacterClass charBefore = GetCharacterClass(textSource.GetCharAt(nextPos - 1));
+ CharacterClass charAfter = GetCharacterClass(textSource.GetCharAt(nextPos));
+ if (charBefore == charAfter) {
+ if (charBefore == CharacterClass.Other &&
+ (mode == CaretPositioningMode.WordBorderOrSymbol || mode == CaretPositioningMode.WordStartOrSymbol))
+ {
+ // With the "OrSymbol" modes, there's a word border and start between any two unknown characters
+ return nextPos;
+ }
+ } else {
+ // this looks like a possible border
+
+ // if we're looking for word starts, check that this is a word start (and not a word end)
+ // if we're just checking for word borders, accept unconditionally
+ if (!((mode == CaretPositioningMode.WordStart || mode == CaretPositioningMode.WordStartOrSymbol)
+ && (charAfter == CharacterClass.Whitespace || charAfter == CharacterClass.LineTerminator)))
+ {
+ return nextPos;
+ }
+ }
+ }
+ // we'll have to continue searching...
+ offset = nextPos;
+ }
+ }
+ #endregion
+ }
+
+ /// <summary>
+ /// Classifies a character as whitespace, line terminator, part of an identifier, or other.
+ /// </summary>
+ public enum CharacterClass
+ {
+ /// <summary>
+ /// The character is not whitespace, line terminator or part of an identifier.
+ /// </summary>
+ Other,
+ /// <summary>
+ /// The character is whitespace (but not line terminator).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "WPF uses 'Whitespace'")]
+ Whitespace,
+ /// <summary>
+ /// The character can be part of an identifier (Letter, digit or underscore).
+ /// </summary>
+ IdentifierPart,
+ /// <summary>
+ /// The character is line terminator (\r or \n).
+ /// </summary>
+ LineTerminator
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoOperationGroup.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoOperationGroup.cs
new file mode 100644
index 000000000..12afe3838
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoOperationGroup.cs
@@ -0,0 +1,61 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// This class stacks the last x operations from the undostack and makes
+ /// one undo/redo operation from it.
+ /// </summary>
+ sealed class UndoOperationGroup : IUndoableOperationWithContext
+ {
+ IUndoableOperation[] undolist;
+
+ public UndoOperationGroup(Deque<IUndoableOperation> stack, int numops)
+ {
+ if (stack == null) {
+ throw new ArgumentNullException("stack");
+ }
+
+ Debug.Assert(numops > 0 , "UndoOperationGroup : numops should be > 0");
+ Debug.Assert(numops <= stack.Count);
+
+ undolist = new IUndoableOperation[numops];
+ for (int i = 0; i < numops; ++i) {
+ undolist[i] = stack.PopBack();
+ }
+ }
+
+ public void Undo()
+ {
+ for (int i = 0; i < undolist.Length; ++i) {
+ undolist[i].Undo();
+ }
+ }
+
+ public void Undo(UndoStack stack)
+ {
+ for (int i = 0; i < undolist.Length; ++i) {
+ stack.RunUndo(undolist[i]);
+ }
+ }
+
+ public void Redo()
+ {
+ for (int i = undolist.Length - 1; i >= 0; --i) {
+ undolist[i].Redo();
+ }
+ }
+
+ public void Redo(UndoStack stack)
+ {
+ for (int i = undolist.Length - 1; i >= 0; --i) {
+ stack.RunRedo(undolist[i]);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoStack.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoStack.cs
new file mode 100644
index 000000000..f0a759b23
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/UndoStack.cs
@@ -0,0 +1,450 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Undo stack implementation.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
+ public sealed class UndoStack : INotifyPropertyChanged
+ {
+ /// undo stack is listening for changes
+ internal const int StateListen = 0;
+ /// undo stack is reverting/repeating a set of changes
+ internal const int StatePlayback = 1;
+ // undo stack is reverting/repeating a set of changes and modifies the document to do this
+ internal const int StatePlaybackModifyDocument = 2;
+ /// state is used for checking that noone but the UndoStack performs changes
+ /// during Undo events
+ internal int state = StateListen;
+
+ Deque<IUndoableOperation> undostack = new Deque<IUndoableOperation>();
+ Deque<IUndoableOperation> redostack = new Deque<IUndoableOperation>();
+ int sizeLimit = int.MaxValue;
+
+ int undoGroupDepth;
+ int actionCountInUndoGroup;
+ int optionalActionCount;
+ object lastGroupDescriptor;
+ bool allowContinue;
+
+ #region IsOriginalFile implementation
+ // implements feature request SD2-784 - File still considered dirty after undoing all changes
+
+ /// <summary>
+ /// Number of times undo must be executed until the original state is reached.
+ /// Negative: number of times redo must be executed until the original state is reached.
+ /// Special case: int.MinValue == original state is unreachable
+ /// </summary>
+ int elementsOnUndoUntilOriginalFile;
+
+ bool isOriginalFile = true;
+
+ /// <summary>
+ /// Gets whether the document is currently in its original state (no modifications).
+ /// </summary>
+ public bool IsOriginalFile {
+ get { return isOriginalFile; }
+ }
+
+ void RecalcIsOriginalFile()
+ {
+ bool newIsOriginalFile = (elementsOnUndoUntilOriginalFile == 0);
+ if (newIsOriginalFile != isOriginalFile) {
+ isOriginalFile = newIsOriginalFile;
+ NotifyPropertyChanged("IsOriginalFile");
+ }
+ }
+
+ /// <summary>
+ /// Marks the current state as original. Discards any previous "original" markers.
+ /// </summary>
+ public void MarkAsOriginalFile()
+ {
+ elementsOnUndoUntilOriginalFile = 0;
+ RecalcIsOriginalFile();
+ }
+
+ /// <summary>
+ /// Discards the current "original" marker.
+ /// </summary>
+ public void DiscardOriginalFileMarker()
+ {
+ elementsOnUndoUntilOriginalFile = int.MinValue;
+ RecalcIsOriginalFile();
+ }
+
+ void FileModified(int newElementsOnUndoStack)
+ {
+ if (elementsOnUndoUntilOriginalFile == int.MinValue)
+ return;
+
+ elementsOnUndoUntilOriginalFile += newElementsOnUndoStack;
+ if (elementsOnUndoUntilOriginalFile > undostack.Count)
+ elementsOnUndoUntilOriginalFile = int.MinValue;
+
+ // don't call RecalcIsOriginalFile(): wait until end of undo group
+ }
+ #endregion
+
+ /// <summary>
+ /// Gets if the undo stack currently accepts changes.
+ /// Is false while an undo action is running.
+ /// </summary>
+ public bool AcceptChanges {
+ get { return state == StateListen; }
+ }
+
+ /// <summary>
+ /// Gets if there are actions on the undo stack.
+ /// Use the PropertyChanged event to listen to changes of this property.
+ /// </summary>
+ public bool CanUndo {
+ get { return undostack.Count > 0; }
+ }
+
+ /// <summary>
+ /// Gets if there are actions on the redo stack.
+ /// Use the PropertyChanged event to listen to changes of this property.
+ /// </summary>
+ public bool CanRedo {
+ get { return redostack.Count > 0; }
+ }
+
+ /// <summary>
+ /// Gets/Sets the limit on the number of items on the undo stack.
+ /// </summary>
+ /// <remarks>The size limit is enforced only on the number of stored top-level undo groups.
+ /// Elements within undo groups do not count towards the size limit.</remarks>
+ public int SizeLimit {
+ get { return sizeLimit; }
+ set {
+ if (value < 0)
+ ThrowUtil.CheckNotNegative(value, "value");
+ if (sizeLimit != value) {
+ sizeLimit = value;
+ NotifyPropertyChanged("SizeLimit");
+ if (undoGroupDepth == 0)
+ EnforceSizeLimit();
+ }
+ }
+ }
+
+ void EnforceSizeLimit()
+ {
+ Debug.Assert(undoGroupDepth == 0);
+ while (undostack.Count > sizeLimit)
+ undostack.PopFront();
+ while (redostack.Count > sizeLimit)
+ redostack.PopFront();
+ }
+
+ /// <summary>
+ /// If an undo group is open, gets the group descriptor of the current top-level
+ /// undo group.
+ /// If no undo group is open, gets the group descriptor from the previous undo group.
+ /// </summary>
+ /// <remarks>The group descriptor can be used to join adjacent undo groups:
+ /// use a group descriptor to mark your changes, and on the second action,
+ /// compare LastGroupDescriptor and use <see cref="StartContinuedUndoGroup"/> if you
+ /// want to join the undo groups.</remarks>
+ public object LastGroupDescriptor {
+ get { return lastGroupDescriptor; }
+ }
+
+ /// <summary>
+ /// Starts grouping changes.
+ /// Maintains a counter so that nested calls are possible.
+ /// </summary>
+ public void StartUndoGroup()
+ {
+ StartUndoGroup(null);
+ }
+
+ /// <summary>
+ /// Starts grouping changes.
+ /// Maintains a counter so that nested calls are possible.
+ /// </summary>
+ /// <param name="groupDescriptor">An object that is stored with the undo group.
+ /// If this is not a top-level undo group, the parameter is ignored.</param>
+ public void StartUndoGroup(object groupDescriptor)
+ {
+ if (undoGroupDepth == 0) {
+ actionCountInUndoGroup = 0;
+ optionalActionCount = 0;
+ lastGroupDescriptor = groupDescriptor;
+ }
+ undoGroupDepth++;
+ //Util.LoggingService.Debug("Open undo group (new depth=" + undoGroupDepth + ")");
+ }
+
+ /// <summary>
+ /// Starts grouping changes, continuing with the previously closed undo group if possible.
+ /// Maintains a counter so that nested calls are possible.
+ /// If the call to StartContinuedUndoGroup is a nested call, it behaves exactly
+ /// as <see cref="StartUndoGroup()"/>, only top-level calls can continue existing undo groups.
+ /// </summary>
+ /// <param name="groupDescriptor">An object that is stored with the undo group.
+ /// If this is not a top-level undo group, the parameter is ignored.</param>
+ public void StartContinuedUndoGroup(object groupDescriptor)
+ {
+ if (undoGroupDepth == 0) {
+ actionCountInUndoGroup = (allowContinue && undostack.Count > 0) ? 1 : 0;
+ optionalActionCount = 0;
+ lastGroupDescriptor = groupDescriptor;
+ }
+ undoGroupDepth++;
+ //Util.LoggingService.Debug("Continue undo group (new depth=" + undoGroupDepth + ")");
+ }
+
+ /// <summary>
+ /// Stops grouping changes.
+ /// </summary>
+ public void EndUndoGroup()
+ {
+ if (undoGroupDepth == 0) return;
+ undoGroupDepth--;
+ //Util.LoggingService.Debug("Close undo group (new depth=" + undoGroupDepth + ")");
+ if (undoGroupDepth == 0) {
+ Debug.Assert(state == StateListen || actionCountInUndoGroup == 0);
+ if (actionCountInUndoGroup == optionalActionCount) {
+ // only optional actions: don't store them
+ for (int i = 0; i < optionalActionCount; i++) {
+ undostack.PopBack();
+ }
+ } else if (actionCountInUndoGroup > 1) {
+ // combine all actions within the group into a single grouped action
+ undostack.PushBack(new UndoOperationGroup(undostack, actionCountInUndoGroup));
+ FileModified(-actionCountInUndoGroup + 1 + optionalActionCount);
+ }
+ //if (state == StateListen) {
+ EnforceSizeLimit();
+ allowContinue = true;
+ RecalcIsOriginalFile(); // can raise event
+ //}
+ }
+ }
+
+ /// <summary>
+ /// Throws an InvalidOperationException if an undo group is current open.
+ /// </summary>
+ void ThrowIfUndoGroupOpen()
+ {
+ try
+ {
+ if (undoGroupDepth != 0)
+ {
+ undoGroupDepth = 0;
+ throw new InvalidOperationException("No undo group should be open at this point");
+ }
+ if (state != StateListen)
+ {
+ throw new InvalidOperationException("This method cannot be called while an undo operation is being performed");
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ List<TextDocument> affectedDocuments;
+
+ internal void RegisterAffectedDocument(TextDocument document)
+ {
+ if (affectedDocuments == null)
+ affectedDocuments = new List<TextDocument>();
+ if (!affectedDocuments.Contains(document)) {
+ affectedDocuments.Add(document);
+ document.BeginUpdate();
+ }
+ }
+
+ void CallEndUpdateOnAffectedDocuments()
+ {
+ if (affectedDocuments != null) {
+ foreach (TextDocument doc in affectedDocuments) {
+ doc.EndUpdate();
+ }
+ affectedDocuments = null;
+ }
+ }
+
+ /// <summary>
+ /// Call this method to undo the last operation on the stack
+ /// </summary>
+ public void Undo()
+ {
+ ThrowIfUndoGroupOpen();
+ if (undostack.Count > 0) {
+ // disallow continuing undo groups after undo operation
+ lastGroupDescriptor = null; allowContinue = false;
+ // fetch operation to undo and move it to redo stack
+ IUndoableOperation uedit = undostack.PopBack();
+ redostack.PushBack(uedit);
+ state = StatePlayback;
+ try {
+ RunUndo(uedit);
+ } finally {
+ state = StateListen;
+ FileModified(-1);
+ CallEndUpdateOnAffectedDocuments();
+ }
+ RecalcIsOriginalFile();
+ if (undostack.Count == 0)
+ NotifyPropertyChanged("CanUndo");
+ if (redostack.Count == 1)
+ NotifyPropertyChanged("CanRedo");
+ }
+ }
+
+ internal void RunUndo(IUndoableOperation op)
+ {
+ IUndoableOperationWithContext opWithCtx = op as IUndoableOperationWithContext;
+ if (opWithCtx != null)
+ opWithCtx.Undo(this);
+ else
+ op.Undo();
+ }
+
+ /// <summary>
+ /// Call this method to redo the last undone operation
+ /// </summary>
+ public void Redo()
+ {
+ ThrowIfUndoGroupOpen();
+ if (redostack.Count > 0) {
+ lastGroupDescriptor = null; allowContinue = false;
+ IUndoableOperation uedit = redostack.PopBack();
+ undostack.PushBack(uedit);
+ state = StatePlayback;
+ try {
+ RunRedo(uedit);
+ } finally {
+ state = StateListen;
+ FileModified(1);
+ CallEndUpdateOnAffectedDocuments();
+ }
+ RecalcIsOriginalFile();
+ if (redostack.Count == 0)
+ NotifyPropertyChanged("CanRedo");
+ if (undostack.Count == 1)
+ NotifyPropertyChanged("CanUndo");
+ }
+ }
+
+ internal void RunRedo(IUndoableOperation op)
+ {
+ IUndoableOperationWithContext opWithCtx = op as IUndoableOperationWithContext;
+ if (opWithCtx != null)
+ opWithCtx.Redo(this);
+ else
+ op.Redo();
+ }
+
+ /// <summary>
+ /// Call this method to push an UndoableOperation on the undostack.
+ /// The redostack will be cleared if you use this method.
+ /// </summary>
+ public void Push(IUndoableOperation operation)
+ {
+ Push(operation, false);
+ }
+
+ /// <summary>
+ /// Call this method to push an UndoableOperation on the undostack.
+ /// However, the operation will be only stored if the undo group contains a
+ /// non-optional operation.
+ /// Use this method to store the caret position/selection on the undo stack to
+ /// prevent having only actions that affect only the caret and not the document.
+ /// </summary>
+ public void PushOptional(IUndoableOperation operation)
+ {
+ if (undoGroupDepth == 0)
+ throw new InvalidOperationException("Cannot use PushOptional outside of undo group");
+ Push(operation, true);
+ }
+
+ void Push(IUndoableOperation operation, bool isOptional)
+ {
+ if (operation == null) {
+ throw new ArgumentNullException("operation");
+ }
+
+ if (state == StateListen && sizeLimit > 0) {
+ bool wasEmpty = undostack.Count == 0;
+
+ bool needsUndoGroup = undoGroupDepth == 0;
+ if (needsUndoGroup) StartUndoGroup();
+ undostack.PushBack(operation);
+ actionCountInUndoGroup++;
+ if (isOptional)
+ optionalActionCount++;
+ else
+ FileModified(1);
+ if (needsUndoGroup) EndUndoGroup();
+ if (wasEmpty)
+ NotifyPropertyChanged("CanUndo");
+ ClearRedoStack();
+ }
+ }
+
+ /// <summary>
+ /// Call this method, if you want to clear the redo stack
+ /// </summary>
+ public void ClearRedoStack()
+ {
+ if (redostack.Count != 0) {
+ redostack.Clear();
+ NotifyPropertyChanged("CanRedo");
+ // if the "original file" marker is on the redo stack: remove it
+ if (elementsOnUndoUntilOriginalFile < 0)
+ elementsOnUndoUntilOriginalFile = int.MinValue;
+ }
+ }
+
+ /// <summary>
+ /// Clears both the undo and redo stack.
+ /// </summary>
+ public void ClearAll()
+ {
+ ThrowIfUndoGroupOpen();
+ actionCountInUndoGroup = 0;
+ optionalActionCount = 0;
+ if (undostack.Count != 0) {
+ lastGroupDescriptor = null;
+ allowContinue = false;
+ undostack.Clear();
+ NotifyPropertyChanged("CanUndo");
+ }
+ ClearRedoStack();
+ }
+
+ internal void Push(TextDocument document, DocumentChangeEventArgs e)
+ {
+ if (state == StatePlayback)
+ throw new InvalidOperationException("Document changes during undo/redo operations are not allowed.");
+ if (state == StatePlaybackModifyDocument)
+ state = StatePlayback; // allow only 1 change per expected modification
+ else
+ Push(new DocumentChangeOperation(document, e));
+ }
+
+ /// <summary>
+ /// Is raised when a property (CanUndo, CanRedo) changed.
+ /// </summary>
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ void NotifyPropertyChanged(string propertyName)
+ {
+ if (PropertyChanged != null)
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/WeakLineTracker.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/WeakLineTracker.cs
new file mode 100644
index 000000000..a70874645
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Document/WeakLineTracker.cs
@@ -0,0 +1,85 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Document
+{
+ /// <summary>
+ /// Allows registering a line tracker on a TextDocument using a weak reference from the document to the line tracker.
+ /// </summary>
+ public sealed class WeakLineTracker : ILineTracker
+ {
+ TextDocument textDocument;
+ WeakReference targetObject;
+
+ private WeakLineTracker(TextDocument textDocument, ILineTracker targetTracker)
+ {
+ this.textDocument = textDocument;
+ this.targetObject = new WeakReference(targetTracker);
+ }
+
+ /// <summary>
+ /// Registers the <paramref name="targetTracker"/> as line tracker for the <paramref name="textDocument"/>.
+ /// A weak reference to the target tracker will be used, and the WeakLineTracker will deregister itself
+ /// when the target tracker is garbage collected.
+ /// </summary>
+ public static WeakLineTracker Register(TextDocument textDocument, ILineTracker targetTracker)
+ {
+ if (textDocument == null)
+ throw new ArgumentNullException("textDocument");
+ if (targetTracker == null)
+ throw new ArgumentNullException("targetTracker");
+ WeakLineTracker wlt = new WeakLineTracker(textDocument, targetTracker);
+ textDocument.LineTrackers.Add(wlt);
+ return wlt;
+ }
+
+ /// <summary>
+ /// Deregisters the weak line tracker.
+ /// </summary>
+ public void Deregister()
+ {
+ if (textDocument != null) {
+ textDocument.LineTrackers.Remove(this);
+ textDocument = null;
+ }
+ }
+
+ void ILineTracker.BeforeRemoveLine(DocumentLine line)
+ {
+ ILineTracker targetTracker = targetObject.Target as ILineTracker;
+ if (targetTracker != null)
+ targetTracker.BeforeRemoveLine(line);
+ else
+ Deregister();
+ }
+
+ void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength)
+ {
+ ILineTracker targetTracker = targetObject.Target as ILineTracker;
+ if (targetTracker != null)
+ targetTracker.SetLineLength(line, newTotalLength);
+ else
+ Deregister();
+ }
+
+ void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine)
+ {
+ ILineTracker targetTracker = targetObject.Target as ILineTracker;
+ if (targetTracker != null)
+ targetTracker.LineInserted(insertionPos, newLine);
+ else
+ Deregister();
+ }
+
+ void ILineTracker.RebuildDocument()
+ {
+ ILineTracker targetTracker = targetObject.Target as ILineTracker;
+ if (targetTracker != null)
+ targetTracker.RebuildDocument();
+ else
+ Deregister();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/AbstractMargin.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/AbstractMargin.cs
new file mode 100644
index 000000000..c82ff94a0
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/AbstractMargin.cs
@@ -0,0 +1,102 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Base class for margins.
+ /// Margins don't have to derive from this class, it just helps maintaining a reference to the TextView
+ /// and the TextDocument.
+ /// AbstractMargin derives from FrameworkElement, so if you don't want to handle visual children and rendering
+ /// on your own, choose another base class for your margin!
+ /// </summary>
+ public abstract class AbstractMargin : FrameworkElement, ITextViewConnect
+ {
+ /// <summary>
+ /// TextView property.
+ /// </summary>
+ public static readonly DependencyProperty TextViewProperty =
+ DependencyProperty.Register("TextView", typeof(TextView), typeof(AbstractMargin),
+ new FrameworkPropertyMetadata(OnTextViewChanged));
+
+ /// <summary>
+ /// Gets/sets the text view for which line numbers are displayed.
+ /// </summary>
+ /// <remarks>Adding a margin to <see cref="TextArea.LeftMargins"/> will automatically set this property to the text area's TextView.</remarks>
+ public TextView TextView {
+ get { return (TextView)GetValue(TextViewProperty); }
+ set { SetValue(TextViewProperty, value); }
+ }
+
+ static void OnTextViewChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ AbstractMargin margin = (AbstractMargin)dp;
+ margin.wasAutoAddedToTextView = false;
+ margin.OnTextViewChanged((TextView)e.OldValue, (TextView)e.NewValue);
+ }
+
+ // automatically set/unset TextView property using ITextViewConnect
+ bool wasAutoAddedToTextView;
+
+ void ITextViewConnect.AddToTextView(TextView textView)
+ {
+ if (this.TextView == null) {
+ this.TextView = textView;
+ wasAutoAddedToTextView = true;
+ } else if (this.TextView != textView) {
+ throw new InvalidOperationException("This margin belongs to a different TextView.");
+ }
+ }
+
+ void ITextViewConnect.RemoveFromTextView(TextView textView)
+ {
+ if (wasAutoAddedToTextView && this.TextView == textView) {
+ this.TextView = null;
+ Debug.Assert(!wasAutoAddedToTextView); // setting this.TextView should have unset this flag
+ }
+ }
+
+ TextDocument document;
+
+ /// <summary>
+ /// Gets the document associated with the margin.
+ /// </summary>
+ public TextDocument Document {
+ get { return document; }
+ }
+
+ /// <summary>
+ /// Called when the <see cref="TextView"/> is changing.
+ /// </summary>
+ protected virtual void OnTextViewChanged(TextView oldTextView, TextView newTextView)
+ {
+ if (oldTextView != null) {
+ oldTextView.DocumentChanged -= TextViewDocumentChanged;
+ }
+ if (newTextView != null) {
+ newTextView.DocumentChanged += TextViewDocumentChanged;
+ }
+ TextViewDocumentChanged(null, null);
+ }
+
+ void TextViewDocumentChanged(object sender, EventArgs e)
+ {
+ OnDocumentChanged(document, TextView != null ? TextView.Document : null);
+ }
+
+ /// <summary>
+ /// Called when the <see cref="Document"/> is changing.
+ /// </summary>
+ protected virtual void OnDocumentChanged(TextDocument oldDocument, TextDocument newDocument)
+ {
+ document = newDocument;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Caret.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Caret.cs
new file mode 100644
index 000000000..23542cd21
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Caret.cs
@@ -0,0 +1,472 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Helper class with caret-related methods.
+ /// </summary>
+ public sealed class Caret
+ {
+ readonly TextArea textArea;
+ readonly TextView textView;
+ readonly CaretLayer caretAdorner;
+ bool visible;
+
+ internal Caret(TextArea textArea)
+ {
+ this.textArea = textArea;
+ this.textView = textArea.TextView;
+ position = new TextViewPosition(1, 1, 0);
+
+ caretAdorner = new CaretLayer(textView);
+ textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace);
+ textView.VisualLinesChanged += TextView_VisualLinesChanged;
+ textView.ScrollOffsetChanged += TextView_ScrollOffsetChanged;
+ }
+
+ void TextView_VisualLinesChanged(object sender, EventArgs e)
+ {
+ if (visible) {
+ Show();
+ }
+ // required because the visual columns might have changed if the
+ // element generators did something differently than on the last run
+ // (e.g. a FoldingSection was collapsed)
+ InvalidateVisualColumn();
+ }
+
+ void TextView_ScrollOffsetChanged(object sender, EventArgs e)
+ {
+ if (caretAdorner != null) {
+ caretAdorner.InvalidateVisual();
+ }
+ }
+
+ double desiredXPos = double.NaN;
+ TextViewPosition position;
+
+ /// <summary>
+ /// Gets/Sets the position of the caret.
+ /// Retrieving this property will validate the visual column (which can be expensive).
+ /// Use the <see cref="Location"/> property instead if you don't need the visual column.
+ /// </summary>
+ public TextViewPosition Position {
+ get {
+ ValidateVisualColumn();
+ return position;
+ }
+ set {
+ if (position != value) {
+ position = value;
+
+ storedCaretOffset = -1;
+
+ //Debug.WriteLine("Caret position changing to " + value);
+
+ ValidatePosition();
+ InvalidateVisualColumn();
+ RaisePositionChanged();
+ Log("Caret position changed to " + value);
+ if (visible)
+ Show();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the caret position without validating it.
+ /// </summary>
+ internal TextViewPosition NonValidatedPosition {
+ get {
+ return position;
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the location of the caret.
+ /// The getter of this property is faster than <see cref="Position"/> because it doesn't have
+ /// to validate the visual column.
+ /// </summary>
+ public TextLocation Location {
+ get {
+ return position.Location;
+ }
+ set {
+ this.Position = new TextViewPosition(value);
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the caret line.
+ /// </summary>
+ public int Line {
+ get { return position.Line; }
+ set {
+ this.Position = new TextViewPosition(value, position.Column);
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the caret column.
+ /// </summary>
+ public int Column {
+ get { return position.Column; }
+ set {
+ this.Position = new TextViewPosition(position.Line, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the caret visual column.
+ /// </summary>
+ public int VisualColumn {
+ get {
+ ValidateVisualColumn();
+ return position.VisualColumn;
+ }
+ set {
+ this.Position = new TextViewPosition(position.Line, position.Column, value);
+ }
+ }
+
+ bool isInVirtualSpace;
+
+ /// <summary>
+ /// Gets whether the caret is in virtual space.
+ /// </summary>
+ public bool IsInVirtualSpace {
+ get {
+ ValidateVisualColumn();
+ return isInVirtualSpace;
+ }
+ }
+
+ int storedCaretOffset;
+
+ internal void OnDocumentChanging()
+ {
+ storedCaretOffset = this.Offset;
+ InvalidateVisualColumn();
+ }
+
+ internal void OnDocumentChanged(DocumentChangeEventArgs e)
+ {
+ InvalidateVisualColumn();
+ if (storedCaretOffset >= 0) {
+ int newCaretOffset = e.GetNewOffset(storedCaretOffset, AnchorMovementType.Default);
+ TextDocument document = textArea.Document;
+ if (document != null) {
+ // keep visual column
+ this.Position = new TextViewPosition(document.GetLocation(newCaretOffset), position.VisualColumn);
+ }
+ }
+ storedCaretOffset = -1;
+ }
+
+ /// <summary>
+ /// Gets/Sets the caret offset.
+ /// Setting the caret offset has the side effect of setting the <see cref="DesiredXPos"/> to NaN.
+ /// </summary>
+ public int Offset {
+ get {
+ TextDocument document = textArea.Document;
+ if (document == null) {
+ return 0;
+ } else {
+ return document.GetOffset(position.Location);
+ }
+ }
+ set {
+ TextDocument document = textArea.Document;
+ if (document != null) {
+ this.Position = new TextViewPosition(document.GetLocation(value));
+ this.DesiredXPos = double.NaN;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the desired x-position of the caret, in device-independent pixels.
+ /// This property is NaN if the caret has no desired position.
+ /// </summary>
+ public double DesiredXPos {
+ get { return desiredXPos; }
+ set { desiredXPos = value; }
+ }
+
+ void ValidatePosition()
+ {
+ if (position.Line < 1)
+ position.Line = 1;
+ if (position.Column < 1)
+ position.Column = 1;
+ if (position.VisualColumn < -1)
+ position.VisualColumn = -1;
+ TextDocument document = textArea.Document;
+ if (document != null) {
+ if (position.Line > document.LineCount) {
+ position.Line = document.LineCount;
+ position.Column = document.GetLineByNumber(position.Line).Length + 1;
+ position.VisualColumn = -1;
+ } else {
+ DocumentLine line = document.GetLineByNumber(position.Line);
+ if (position.Column > line.Length + 1) {
+ position.Column = line.Length + 1;
+ position.VisualColumn = -1;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Event raised when the caret position has changed.
+ /// If the caret position is changed inside a document update (between BeginUpdate/EndUpdate calls),
+ /// the PositionChanged event is raised only once at the end of the document update.
+ /// </summary>
+ public event EventHandler PositionChanged;
+
+ bool raisePositionChangedOnUpdateFinished;
+
+ void RaisePositionChanged()
+ {
+ if (textArea.Document != null && textArea.Document.IsInUpdate) {
+ raisePositionChangedOnUpdateFinished = true;
+ } else {
+ if (PositionChanged != null) {
+ PositionChanged(this, EventArgs.Empty);
+ }
+ }
+ }
+
+ internal void OnDocumentUpdateFinished()
+ {
+ if (raisePositionChangedOnUpdateFinished) {
+ if (PositionChanged != null) {
+ PositionChanged(this, EventArgs.Empty);
+ }
+ }
+ }
+
+ bool visualColumnValid;
+
+ void ValidateVisualColumn()
+ {
+ if (!visualColumnValid) {
+ TextDocument document = textArea.Document;
+ if (document != null) {
+ Debug.WriteLine("Explicit validation of caret column");
+ var documentLine = document.GetLineByNumber(position.Line);
+ RevalidateVisualColumn(textView.GetOrConstructVisualLine(documentLine));
+ }
+ }
+ }
+
+ void InvalidateVisualColumn()
+ {
+ visualColumnValid = false;
+ }
+
+ /// <summary>
+ /// Validates the visual column of the caret using the specified visual line.
+ /// The visual line must contain the caret offset.
+ /// </summary>
+ void RevalidateVisualColumn(VisualLine visualLine)
+ {
+ if (visualLine == null)
+ throw new ArgumentNullException("visualLine");
+
+ // mark column as validated
+ visualColumnValid = true;
+
+ int caretOffset = textView.Document.GetOffset(position.Location);
+ int firstDocumentLineOffset = visualLine.FirstDocumentLine.Offset;
+ position.VisualColumn = visualLine.ValidateVisualColumn(position, textArea.Selection.EnableVirtualSpace);
+
+ // search possible caret positions
+ int newVisualColumnForwards = visualLine.GetNextCaretPosition(position.VisualColumn - 1, LogicalDirection.Forward, CaretPositioningMode.Normal, textArea.Selection.EnableVirtualSpace);
+ // If position.VisualColumn was valid, we're done with validation.
+ if (newVisualColumnForwards != position.VisualColumn) {
+ // also search backwards so that we can pick the better match
+ int newVisualColumnBackwards = visualLine.GetNextCaretPosition(position.VisualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.Normal, textArea.Selection.EnableVirtualSpace);
+
+ if (newVisualColumnForwards < 0 && newVisualColumnBackwards < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+
+ // determine offsets for new visual column positions
+ int newOffsetForwards;
+ if (newVisualColumnForwards >= 0)
+ newOffsetForwards = visualLine.GetRelativeOffset(newVisualColumnForwards) + firstDocumentLineOffset;
+ else
+ newOffsetForwards = -1;
+ int newOffsetBackwards;
+ if (newVisualColumnBackwards >= 0)
+ newOffsetBackwards = visualLine.GetRelativeOffset(newVisualColumnBackwards) + firstDocumentLineOffset;
+ else
+ newOffsetBackwards = -1;
+
+ int newVisualColumn, newOffset;
+ // if there's only one valid position, use it
+ if (newVisualColumnForwards < 0) {
+ newVisualColumn = newVisualColumnBackwards;
+ newOffset = newOffsetBackwards;
+ } else if (newVisualColumnBackwards < 0) {
+ newVisualColumn = newVisualColumnForwards;
+ newOffset = newOffsetForwards;
+ } else {
+ // two valid positions: find the better match
+ if (Math.Abs(newOffsetBackwards - caretOffset) < Math.Abs(newOffsetForwards - caretOffset)) {
+ // backwards is better
+ newVisualColumn = newVisualColumnBackwards;
+ newOffset = newOffsetBackwards;
+ } else {
+ // forwards is better
+ newVisualColumn = newVisualColumnForwards;
+ newOffset = newOffsetForwards;
+ }
+ }
+ this.Position = new TextViewPosition(textView.Document.GetLocation(newOffset), newVisualColumn);
+ }
+ isInVirtualSpace = (position.VisualColumn > visualLine.VisualLength);
+ }
+
+ Rect CalcCaretRectangle(VisualLine visualLine)
+ {
+ if (!visualColumnValid) {
+ RevalidateVisualColumn(visualLine);
+ }
+
+ TextLine textLine = visualLine.GetTextLine(position.VisualColumn);
+ double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn);
+ double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
+ double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
+
+ return new Rect(xPos,
+ lineTop,
+ SystemParameters.CaretWidth,
+ lineBottom - lineTop);
+ }
+
+ /// <summary>
+ /// Returns the caret rectangle. The coordinate system is in device-independent pixels from the top of the document.
+ /// </summary>
+ public Rect CalculateCaretRectangle()
+ {
+ if (textView != null && textView.Document != null) {
+ VisualLine visualLine = textView.GetOrConstructVisualLine(textView.Document.GetLineByNumber(position.Line));
+ return CalcCaretRectangle(visualLine);
+ } else {
+ return Rect.Empty;
+ }
+ }
+
+ /// <summary>
+ /// Minimum distance of the caret to the view border.
+ /// </summary>
+ internal const double MinimumDistanceToViewBorder = 30;
+
+ /// <summary>
+ /// Scrolls the text view so that the caret is visible.
+ /// </summary>
+ public void BringCaretToView()
+ {
+ BringCaretToView(MinimumDistanceToViewBorder);
+ }
+
+ internal void BringCaretToView(double border)
+ {
+ Rect caretRectangle = CalculateCaretRectangle();
+ if (!caretRectangle.IsEmpty) {
+ caretRectangle.Inflate(border, border);
+ textView.MakeVisible(caretRectangle);
+ }
+ }
+
+ /// <summary>
+ /// Makes the caret visible and updates its on-screen position.
+ /// </summary>
+ public void Show()
+ {
+ Log("Caret.Show()");
+ visible = true;
+ if (!showScheduled) {
+ showScheduled = true;
+ textArea.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(ShowInternal));
+ }
+ }
+
+ bool showScheduled;
+ bool hasWin32Caret;
+
+ void ShowInternal()
+ {
+ showScheduled = false;
+
+ // if show was scheduled but caret hidden in the meantime
+ if (!visible)
+ return;
+
+ if (caretAdorner != null && textView != null) {
+ VisualLine visualLine = textView.GetVisualLine(position.Line);
+ if (visualLine != null) {
+ Rect caretRect = CalcCaretRectangle(visualLine);
+ // Create Win32 caret so that Windows knows where our managed caret is. This is necessary for
+ // features like 'Follow text editing' in the Windows Magnifier.
+ if (!hasWin32Caret) {
+ hasWin32Caret = Win32.CreateCaret(textView, caretRect.Size);
+ }
+ if (hasWin32Caret) {
+ Win32.SetCaretPosition(textView, caretRect.Location - textView.ScrollOffset);
+ }
+ caretAdorner.Show(caretRect);
+ textArea.ime.UpdateCompositionWindow();
+ } else {
+ caretAdorner.Hide();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Makes the caret invisible.
+ /// </summary>
+ public void Hide()
+ {
+ Log("Caret.Hide()");
+ visible = false;
+ if (hasWin32Caret) {
+ Win32.DestroyCaret();
+ hasWin32Caret = false;
+ }
+ if (caretAdorner != null) {
+ caretAdorner.Hide();
+ }
+ }
+
+ [Conditional("DEBUG")]
+ static void Log(string text)
+ {
+ // commented out to make debug output less noisy - add back if there are any problems with the caret
+ //Debug.WriteLine(text);
+ }
+
+ /// <summary>
+ /// Gets/Sets the color of the caret.
+ /// </summary>
+ public Brush CaretBrush {
+ get { return caretAdorner.CaretBrush; }
+ set { caretAdorner.CaretBrush = value; }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretLayer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretLayer.cs
new file mode 100644
index 000000000..105f38d16
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretLayer.cs
@@ -0,0 +1,86 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ sealed class CaretLayer : Layer
+ {
+ bool isVisible;
+ Rect caretRectangle;
+
+ DispatcherTimer caretBlinkTimer = new DispatcherTimer();
+ bool blink;
+
+ public CaretLayer(TextView textView) : base(textView, KnownLayer.Caret)
+ {
+ this.IsHitTestVisible = false;
+ caretBlinkTimer.Tick += new EventHandler(caretBlinkTimer_Tick);
+ }
+
+ void caretBlinkTimer_Tick(object sender, EventArgs e)
+ {
+ blink = !blink;
+ InvalidateVisual();
+ }
+
+ public void Show(Rect caretRectangle)
+ {
+ this.caretRectangle = caretRectangle;
+ this.isVisible = true;
+ StartBlinkAnimation();
+ InvalidateVisual();
+ }
+
+ public void Hide()
+ {
+ if (isVisible) {
+ isVisible = false;
+ StopBlinkAnimation();
+ InvalidateVisual();
+ }
+ }
+
+ void StartBlinkAnimation()
+ {
+ TimeSpan blinkTime = Win32.CaretBlinkTime;
+ blink = true; // the caret should visible initially
+ // This is important if blinking is disabled (system reports a negative blinkTime)
+ if (blinkTime.TotalMilliseconds > 0) {
+ caretBlinkTimer.Interval = blinkTime;
+ caretBlinkTimer.Start();
+ }
+ }
+
+ void StopBlinkAnimation()
+ {
+ caretBlinkTimer.Stop();
+ }
+
+ internal Brush CaretBrush;
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ base.OnRender(drawingContext);
+ if (isVisible && blink) {
+ Brush caretBrush = this.CaretBrush;
+ if (caretBrush == null)
+ caretBrush = (Brush)textView.GetValue(TextBlock.ForegroundProperty);
+ Rect r = new Rect(caretRectangle.X - textView.HorizontalOffset,
+ caretRectangle.Y - textView.VerticalOffset,
+ caretRectangle.Width,
+ caretRectangle.Height);
+ drawingContext.DrawRectangle(caretBrush, null, PixelSnapHelpers.Round(r, PixelSnapHelpers.GetPixelSize(this)));
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs
new file mode 100644
index 000000000..d1b812b66
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretNavigationCommandHandler.cs
@@ -0,0 +1,375 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ static class CaretNavigationCommandHandler
+ {
+ /// <summary>
+ /// Creates a new <see cref="TextAreaInputHandler"/> for the text area.
+ /// </summary>
+ public static TextAreaInputHandler Create(TextArea textArea)
+ {
+ TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
+ handler.CommandBindings.AddRange(CommandBindings);
+ handler.InputBindings.AddRange(InputBindings);
+ return handler;
+ }
+
+ static readonly List<CommandBinding> CommandBindings = new List<CommandBinding>();
+ static readonly List<InputBinding> InputBindings = new List<InputBinding>();
+
+ static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
+ {
+ CommandBindings.Add(new CommandBinding(command, handler));
+ InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(command, modifiers, key));
+ }
+
+ static CaretNavigationCommandHandler()
+ {
+ const ModifierKeys None = ModifierKeys.None;
+ const ModifierKeys Ctrl = ModifierKeys.Control;
+ const ModifierKeys Shift = ModifierKeys.Shift;
+ const ModifierKeys Alt = ModifierKeys.Alt;
+
+ AddBinding(EditingCommands.MoveLeftByCharacter, None, Key.Left, OnMoveCaret(CaretMovementType.CharLeft));
+ AddBinding(EditingCommands.SelectLeftByCharacter, Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.CharLeft));
+ AddBinding(RectangleSelection.BoxSelectLeftByCharacter, Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.CharLeft));
+ AddBinding(EditingCommands.MoveRightByCharacter, None, Key.Right, OnMoveCaret(CaretMovementType.CharRight));
+ AddBinding(EditingCommands.SelectRightByCharacter, Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.CharRight));
+ AddBinding(RectangleSelection.BoxSelectRightByCharacter, Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.CharRight));
+
+ AddBinding(EditingCommands.MoveLeftByWord, Ctrl, Key.Left, OnMoveCaret(CaretMovementType.WordLeft));
+ AddBinding(EditingCommands.SelectLeftByWord, Ctrl | Shift, Key.Left, OnMoveCaretExtendSelection(CaretMovementType.WordLeft));
+ AddBinding(RectangleSelection.BoxSelectLeftByWord, Ctrl | Alt | Shift, Key.Left, OnMoveCaretBoxSelection(CaretMovementType.WordLeft));
+ AddBinding(EditingCommands.MoveRightByWord, Ctrl, Key.Right, OnMoveCaret(CaretMovementType.WordRight));
+ AddBinding(EditingCommands.SelectRightByWord, Ctrl | Shift, Key.Right, OnMoveCaretExtendSelection(CaretMovementType.WordRight));
+ AddBinding(RectangleSelection.BoxSelectRightByWord, Ctrl | Alt | Shift, Key.Right, OnMoveCaretBoxSelection(CaretMovementType.WordRight));
+
+ AddBinding(EditingCommands.MoveUpByLine, None, Key.Up, OnMoveCaret(CaretMovementType.LineUp));
+ AddBinding(EditingCommands.SelectUpByLine, Shift, Key.Up, OnMoveCaretExtendSelection(CaretMovementType.LineUp));
+ AddBinding(RectangleSelection.BoxSelectUpByLine, Alt | Shift, Key.Up, OnMoveCaretBoxSelection(CaretMovementType.LineUp));
+ AddBinding(EditingCommands.MoveDownByLine, None, Key.Down, OnMoveCaret(CaretMovementType.LineDown));
+ AddBinding(EditingCommands.SelectDownByLine, Shift, Key.Down, OnMoveCaretExtendSelection(CaretMovementType.LineDown));
+ AddBinding(RectangleSelection.BoxSelectDownByLine, Alt | Shift, Key.Down, OnMoveCaretBoxSelection(CaretMovementType.LineDown));
+
+ AddBinding(EditingCommands.MoveDownByPage, None, Key.PageDown, OnMoveCaret(CaretMovementType.PageDown));
+ AddBinding(EditingCommands.SelectDownByPage, Shift, Key.PageDown, OnMoveCaretExtendSelection(CaretMovementType.PageDown));
+ AddBinding(EditingCommands.MoveUpByPage, None, Key.PageUp, OnMoveCaret(CaretMovementType.PageUp));
+ AddBinding(EditingCommands.SelectUpByPage, Shift, Key.PageUp, OnMoveCaretExtendSelection(CaretMovementType.PageUp));
+
+ AddBinding(EditingCommands.MoveToLineStart, None, Key.Home, OnMoveCaret(CaretMovementType.LineStart));
+ AddBinding(EditingCommands.SelectToLineStart, Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.LineStart));
+ AddBinding(RectangleSelection.BoxSelectToLineStart, Alt | Shift, Key.Home, OnMoveCaretBoxSelection(CaretMovementType.LineStart));
+ AddBinding(EditingCommands.MoveToLineEnd, None, Key.End, OnMoveCaret(CaretMovementType.LineEnd));
+ AddBinding(EditingCommands.SelectToLineEnd, Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.LineEnd));
+ AddBinding(RectangleSelection.BoxSelectToLineEnd, Alt | Shift, Key.End, OnMoveCaretBoxSelection(CaretMovementType.LineEnd));
+
+ AddBinding(EditingCommands.MoveToDocumentStart, Ctrl, Key.Home, OnMoveCaret(CaretMovementType.DocumentStart));
+ AddBinding(EditingCommands.SelectToDocumentStart, Ctrl | Shift, Key.Home, OnMoveCaretExtendSelection(CaretMovementType.DocumentStart));
+ AddBinding(EditingCommands.MoveToDocumentEnd, Ctrl, Key.End, OnMoveCaret(CaretMovementType.DocumentEnd));
+ AddBinding(EditingCommands.SelectToDocumentEnd, Ctrl | Shift, Key.End, OnMoveCaretExtendSelection(CaretMovementType.DocumentEnd));
+
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.SelectAll, OnSelectAll));
+
+ TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings);
+ }
+
+ static void OnSelectAll(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ textArea.Caret.Offset = textArea.Document.TextLength;
+ textArea.Selection = SimpleSelection.Create(textArea, 0, textArea.Document.TextLength);
+ }
+ }
+
+ static TextArea GetTextArea(object target)
+ {
+ return target as TextArea;
+ }
+
+ enum CaretMovementType
+ {
+ CharLeft,
+ CharRight,
+ WordLeft,
+ WordRight,
+ LineUp,
+ LineDown,
+ PageUp,
+ PageDown,
+ LineStart,
+ LineEnd,
+ DocumentStart,
+ DocumentEnd
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaret(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ textArea.ClearSelection();
+ MoveCaret(textArea, direction);
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaretExtendSelection(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ TextViewPosition oldPosition = textArea.Caret.Position;
+ MoveCaret(textArea, direction);
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ if (!textArea.Document.IsInUpdate) // if we're inside a larger update (e.g. called by EditingCommandHandler.OnDelete()), avoid calculating the caret rectangle now
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ static ExecutedRoutedEventHandler OnMoveCaretBoxSelection(CaretMovementType direction)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.Handled = true;
+ // First, convert the selection into a rectangle selection
+ // (this is required so that virtual space gets enabled for the caret movement)
+ if (textArea.Options.EnableRectangularSelection && !(textArea.Selection is RectangleSelection)) {
+ if (textArea.Selection.IsEmpty) {
+ textArea.Selection = new RectangleSelection(textArea, textArea.Caret.Position, textArea.Caret.Position);
+ } else {
+ // Convert normal selection to rectangle selection
+ textArea.Selection = new RectangleSelection(textArea, textArea.Selection.StartPosition, textArea.Caret.Position);
+ }
+ }
+ // Now move the caret and extend the selection
+ TextViewPosition oldPosition = textArea.Caret.Position;
+ MoveCaret(textArea, direction);
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ textArea.Caret.BringCaretToView();
+ }
+ };
+ }
+
+ #region Caret movement
+ static void MoveCaret(TextArea textArea, CaretMovementType direction)
+ {
+ DocumentLine caretLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
+ VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(caretLine);
+ TextViewPosition caretPosition = textArea.Caret.Position;
+ TextLine textLine = visualLine.GetTextLine(caretPosition.VisualColumn);
+ switch (direction) {
+ case CaretMovementType.CharLeft:
+ MoveCaretLeft(textArea, caretPosition, visualLine, CaretPositioningMode.Normal);
+ break;
+ case CaretMovementType.CharRight:
+ MoveCaretRight(textArea, caretPosition, visualLine, CaretPositioningMode.Normal);
+ break;
+ case CaretMovementType.WordLeft:
+ MoveCaretLeft(textArea, caretPosition, visualLine, CaretPositioningMode.WordStart);
+ break;
+ case CaretMovementType.WordRight:
+ MoveCaretRight(textArea, caretPosition, visualLine, CaretPositioningMode.WordStart);
+ break;
+ case CaretMovementType.LineUp:
+ case CaretMovementType.LineDown:
+ case CaretMovementType.PageUp:
+ case CaretMovementType.PageDown:
+ MoveCaretUpDown(textArea, direction, visualLine, textLine, caretPosition.VisualColumn);
+ break;
+ case CaretMovementType.DocumentStart:
+ SetCaretPosition(textArea, 0, 0);
+ break;
+ case CaretMovementType.DocumentEnd:
+ SetCaretPosition(textArea, -1, textArea.Document.TextLength);
+ break;
+ case CaretMovementType.LineStart:
+ MoveCaretToStartOfLine(textArea, visualLine);
+ break;
+ case CaretMovementType.LineEnd:
+ MoveCaretToEndOfLine(textArea, visualLine);
+ break;
+ default:
+ throw new NotSupportedException(direction.ToString());
+ }
+ }
+ #endregion
+
+ #region Home/End
+ static void MoveCaretToStartOfLine(TextArea textArea, VisualLine visualLine)
+ {
+ int newVC = visualLine.GetNextCaretPosition(-1, LogicalDirection.Forward, CaretPositioningMode.WordStart, textArea.Selection.EnableVirtualSpace);
+ if (newVC < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+ // when the caret is already at the start of the text, jump to start before whitespace
+ if (newVC == textArea.Caret.VisualColumn)
+ newVC = 0;
+ int offset = visualLine.FirstDocumentLine.Offset + visualLine.GetRelativeOffset(newVC);
+ SetCaretPosition(textArea, newVC, offset);
+ }
+
+ static void MoveCaretToEndOfLine(TextArea textArea, VisualLine visualLine)
+ {
+ int newVC = visualLine.VisualLength;
+ int offset = visualLine.FirstDocumentLine.Offset + visualLine.GetRelativeOffset(newVC);
+ SetCaretPosition(textArea, newVC, offset);
+ }
+ #endregion
+
+ #region By-character / By-word movement
+ static void MoveCaretRight(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode)
+ {
+ int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos >= 0) {
+ SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
+ } else {
+ // move to start of next line
+ DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine;
+ if (nextDocumentLine != null) {
+ VisualLine nextLine = textArea.TextView.GetOrConstructVisualLine(nextDocumentLine);
+ pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+ SetCaretPosition(textArea, pos, nextLine.GetRelativeOffset(pos) + nextLine.FirstDocumentLine.Offset);
+ } else {
+ // at end of document
+ Debug.Assert(visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength == textArea.Document.TextLength);
+ SetCaretPosition(textArea, -1, textArea.Document.TextLength);
+ }
+ }
+ }
+
+ static void MoveCaretLeft(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode)
+ {
+ int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos >= 0) {
+ SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
+ } else {
+ // move to end of previous line
+ DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine;
+ if (previousDocumentLine != null) {
+ VisualLine previousLine = textArea.TextView.GetOrConstructVisualLine(previousDocumentLine);
+ pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace);
+ if (pos < 0)
+ throw ThrowUtil.NoValidCaretPosition();
+ SetCaretPosition(textArea, pos, previousLine.GetRelativeOffset(pos) + previousLine.FirstDocumentLine.Offset);
+ } else {
+ // at start of document
+ Debug.Assert(visualLine.FirstDocumentLine.Offset == 0);
+ SetCaretPosition(textArea, 0, 0);
+ }
+ }
+ }
+ #endregion
+
+ #region Line+Page up/down
+ static void MoveCaretUpDown(TextArea textArea, CaretMovementType direction, VisualLine visualLine, TextLine textLine, int caretVisualColumn)
+ {
+ // moving up/down happens using the desired visual X position
+ double xPos = textArea.Caret.DesiredXPos;
+ if (double.IsNaN(xPos))
+ xPos = visualLine.GetTextLineVisualXPosition(textLine, caretVisualColumn);
+ // now find the TextLine+VisualLine where the caret will end up in
+ VisualLine targetVisualLine = visualLine;
+ TextLine targetLine;
+ int textLineIndex = visualLine.TextLines.IndexOf(textLine);
+ switch (direction) {
+ case CaretMovementType.LineUp:
+ {
+ // Move up: move to the previous TextLine in the same visual line
+ // or move to the last TextLine of the previous visual line
+ int prevLineNumber = visualLine.FirstDocumentLine.LineNumber - 1;
+ if (textLineIndex > 0) {
+ targetLine = visualLine.TextLines[textLineIndex - 1];
+ } else if (prevLineNumber >= 1) {
+ DocumentLine prevLine = textArea.Document.GetLineByNumber(prevLineNumber);
+ targetVisualLine = textArea.TextView.GetOrConstructVisualLine(prevLine);
+ targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1];
+ } else {
+ targetLine = null;
+ }
+ break;
+ }
+ case CaretMovementType.LineDown:
+ {
+ // Move down: move to the next TextLine in the same visual line
+ // or move to the first TextLine of the next visual line
+ int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1;
+ if (textLineIndex < visualLine.TextLines.Count - 1) {
+ targetLine = visualLine.TextLines[textLineIndex + 1];
+ } else if (nextLineNumber <= textArea.Document.LineCount) {
+ DocumentLine nextLine = textArea.Document.GetLineByNumber(nextLineNumber);
+ targetVisualLine = textArea.TextView.GetOrConstructVisualLine(nextLine);
+ targetLine = targetVisualLine.TextLines[0];
+ } else {
+ targetLine = null;
+ }
+ break;
+ }
+ case CaretMovementType.PageUp:
+ case CaretMovementType.PageDown:
+ {
+ // Page up/down: find the target line using its visual position
+ double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
+ if (direction == CaretMovementType.PageUp)
+ yPos -= textArea.TextView.RenderSize.Height;
+ else
+ yPos += textArea.TextView.RenderSize.Height;
+ DocumentLine newLine = textArea.TextView.GetDocumentLineByVisualTop(yPos);
+ targetVisualLine = textArea.TextView.GetOrConstructVisualLine(newLine);
+ targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
+ break;
+ }
+ default:
+ throw new NotSupportedException(direction.ToString());
+ }
+ if (targetLine != null) {
+ double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle);
+ int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), textArea.Selection.EnableVirtualSpace);
+ SetCaretPosition(textArea, targetVisualLine, targetLine, newVisualColumn, false);
+ textArea.Caret.DesiredXPos = xPos;
+ }
+ }
+ #endregion
+
+ #region SetCaretPosition
+ static void SetCaretPosition(TextArea textArea, VisualLine targetVisualLine, TextLine targetLine,
+ int newVisualColumn, bool allowWrapToNextLine)
+ {
+ int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine);
+ if (!allowWrapToNextLine && newVisualColumn >= targetLineStartCol + targetLine.Length) {
+ if (newVisualColumn <= targetVisualLine.VisualLength)
+ newVisualColumn = targetLineStartCol + targetLine.Length - 1;
+ }
+ int newOffset = targetVisualLine.GetRelativeOffset(newVisualColumn) + targetVisualLine.FirstDocumentLine.Offset;
+ SetCaretPosition(textArea, newVisualColumn, newOffset);
+ }
+
+ static void SetCaretPosition(TextArea textArea, int newVisualColumn, int newOffset)
+ {
+ textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(newOffset), newVisualColumn);
+ textArea.Caret.DesiredXPos = double.NaN;
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretWeakEventHandler.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretWeakEventHandler.cs
new file mode 100644
index 000000000..ebf5e4947
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/CaretWeakEventHandler.cs
@@ -0,0 +1,33 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Contains classes for handling weak events on the Caret class.
+ /// </summary>
+ public static class CaretWeakEventManager
+ {
+ /// <summary>
+ /// Handles the Caret.PositionChanged event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class PositionChanged : WeakEventManagerBase<PositionChanged, Caret>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(Caret source)
+ {
+ source.PositionChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(Caret source)
+ {
+ source.PositionChanged -= DeliverEvent;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DottedLineMargin.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DottedLineMargin.cs
new file mode 100644
index 000000000..3de848dda
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DottedLineMargin.cs
@@ -0,0 +1,63 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Media;
+using System.Windows.Shapes;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Margin for use with the text area.
+ /// A vertical dotted line to separate the line numbers from the text view.
+ /// </summary>
+ public static class DottedLineMargin
+ {
+ static readonly object tag = new object();
+
+ /// <summary>
+ /// Creates a vertical dotted line to separate the line numbers from the text view.
+ /// </summary>
+ public static UIElement Create()
+ {
+ Line line = new Line {
+ //X1 = 0, Y1 = 0, X2 = 0, Y2 = 1,
+ //StrokeDashArray = { 0, 2 },
+ //Stretch = Stretch.Fill,
+ //StrokeThickness = 1,
+ //StrokeDashCap = PenLineCap.Round,
+ //Margin = new Thickness(2, 0, 2, 0),
+ //Tag = tag
+ };
+
+ return line;
+ }
+
+ /// <summary>
+ /// Creates a vertical dotted line to separate the line numbers from the text view.
+ /// </summary>
+ [Obsolete("This method got published accidentally; and will be removed again in a future version. Use the parameterless overload instead.")]
+ public static UIElement Create(TextEditor editor)
+ {
+ Line line = (Line)Create();
+
+ line.SetBinding(
+ Line.StrokeProperty,
+ new Binding("LineNumbersForeground") { Source = editor }
+ );
+
+ return line;
+ }
+
+ /// <summary>
+ /// Gets whether the specified UIElement is the result of a DottedLineMargin.Create call.
+ /// </summary>
+ public static bool IsDottedLineMargin(UIElement element)
+ {
+ Line l = element as Line;
+ return l != null && l.Tag == tag;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DragDropException.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DragDropException.cs
new file mode 100644
index 000000000..2ba8e41ec
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/DragDropException.cs
@@ -0,0 +1,46 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Wraps exceptions that occur during drag'n'drop.
+ /// Exceptions during drag'n'drop might
+ /// get swallowed by WPF/COM, so AvalonEdit catches them and re-throws them later
+ /// wrapped in a DragDropException.
+ /// </summary>
+ [Serializable()]
+ public class DragDropException : Exception
+ {
+ /// <summary>
+ /// Creates a new DragDropException.
+ /// </summary>
+ public DragDropException() : base()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new DragDropException.
+ /// </summary>
+ public DragDropException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new DragDropException.
+ /// </summary>
+ public DragDropException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Deserializes a DragDropException.
+ /// </summary>
+ protected DragDropException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EditingCommandHandler.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EditingCommandHandler.cs
new file mode 100644
index 000000000..685e7ae31
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EditingCommandHandler.cs
@@ -0,0 +1,578 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Highlighting;
+using Tango.Scripting.Editors.Search;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// We re-use the CommandBinding and InputBinding instances between multiple text areas,
+ /// so this class is static.
+ /// </summary>
+ static class EditingCommandHandler
+ {
+ /// <summary>
+ /// Creates a new <see cref="TextAreaInputHandler"/> for the text area.
+ /// </summary>
+ public static TextAreaInputHandler Create(TextArea textArea)
+ {
+ TextAreaInputHandler handler = new TextAreaInputHandler(textArea);
+ handler.CommandBindings.AddRange(CommandBindings);
+ handler.InputBindings.AddRange(InputBindings);
+ return handler;
+ }
+
+ static readonly List<CommandBinding> CommandBindings = new List<CommandBinding>();
+ static readonly List<InputBinding> InputBindings = new List<InputBinding>();
+
+ static void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
+ {
+ CommandBindings.Add(new CommandBinding(command, handler));
+ InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(command, modifiers, key));
+ }
+
+ static EditingCommandHandler()
+ {
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, OnDelete(ApplicationCommands.NotACommand), CanDelete));
+ AddBinding(EditingCommands.Delete, ModifierKeys.None, Key.Delete, OnDelete(EditingCommands.SelectRightByCharacter));
+ AddBinding(EditingCommands.DeleteNextWord, ModifierKeys.Control, Key.Delete, OnDelete(EditingCommands.SelectRightByWord));
+ AddBinding(EditingCommands.Backspace, ModifierKeys.None, Key.Back, OnDelete(EditingCommands.SelectLeftByCharacter));
+ InputBindings.Add(TextAreaDefaultInputHandler.CreateFrozenKeyBinding(EditingCommands.Backspace, ModifierKeys.Shift, Key.Back)); // make Shift-Backspace do the same as plain backspace
+ AddBinding(EditingCommands.DeletePreviousWord, ModifierKeys.Control, Key.Back, OnDelete(EditingCommands.SelectLeftByWord));
+ AddBinding(EditingCommands.EnterParagraphBreak, ModifierKeys.None, Key.Enter, OnEnter);
+ AddBinding(EditingCommands.EnterLineBreak, ModifierKeys.Shift, Key.Enter, OnEnter);
+ AddBinding(EditingCommands.TabForward, ModifierKeys.None, Key.Tab, OnTab);
+ AddBinding(EditingCommands.TabBackward, ModifierKeys.Shift, Key.Tab, OnShiftTab);
+
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, OnCopy, CanCutOrCopy));
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, OnCut, CanCutOrCopy));
+ CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, OnPaste, CanPaste));
+
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.DeleteLine, OnDeleteLine));
+
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.RemoveLeadingWhitespace, OnRemoveLeadingWhitespace));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.RemoveTrailingWhitespace, OnRemoveTrailingWhitespace));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertToUppercase, OnConvertToUpperCase));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertToLowercase, OnConvertToLowerCase));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertToTitleCase, OnConvertToTitleCase));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.InvertCase, OnInvertCase));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertTabsToSpaces, OnConvertTabsToSpaces));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertSpacesToTabs, OnConvertSpacesToTabs));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertLeadingTabsToSpaces, OnConvertLeadingTabsToSpaces));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.ConvertLeadingSpacesToTabs, OnConvertLeadingSpacesToTabs));
+ CommandBindings.Add(new CommandBinding(AvalonEditCommands.IndentSelection, OnIndentSelection));
+
+ TextAreaDefaultInputHandler.WorkaroundWPFMemoryLeak(InputBindings);
+ }
+
+ static TextArea GetTextArea(object target)
+ {
+ return target as TextArea;
+ }
+
+ #region Text Transformation Helpers
+ enum DefaultSegmentType
+ {
+ None,
+ WholeDocument,
+ CurrentLine
+ }
+
+ /// <summary>
+ /// Calls transformLine on all lines in the selected range.
+ /// transformLine needs to handle read-only segments!
+ /// </summary>
+ static void TransformSelectedLines(Action<TextArea, DocumentLine> transformLine, object target, ExecutedRoutedEventArgs args, DefaultSegmentType defaultSegmentType)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ using (textArea.Document.RunUpdate()) {
+ DocumentLine start, end;
+ if (textArea.Selection.IsEmpty) {
+ if (defaultSegmentType == DefaultSegmentType.CurrentLine) {
+ start = end = textArea.Document.GetLineByNumber(textArea.Caret.Line);
+ } else if (defaultSegmentType == DefaultSegmentType.WholeDocument) {
+ start = textArea.Document.Lines.First();
+ end = textArea.Document.Lines.Last();
+ } else {
+ start = end = null;
+ }
+ } else {
+ ISegment segment = textArea.Selection.SurroundingSegment;
+ start = textArea.Document.GetLineByOffset(segment.Offset);
+ end = textArea.Document.GetLineByOffset(segment.EndOffset);
+ // don't include the last line if no characters on it are selected
+ if (start != end && end.Offset == segment.EndOffset)
+ end = end.PreviousLine;
+ }
+ if (start != null) {
+ transformLine(textArea, start);
+ while (start != end) {
+ start = start.NextLine;
+ transformLine(textArea, start);
+ }
+ }
+ }
+ textArea.Caret.BringCaretToView();
+ args.Handled = true;
+ }
+ }
+
+ /// <summary>
+ /// Calls transformLine on all writable segment in the selected range.
+ /// </summary>
+ static void TransformSelectedSegments(Action<TextArea, ISegment> transformSegment, object target, ExecutedRoutedEventArgs args, DefaultSegmentType defaultSegmentType)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ using (textArea.Document.RunUpdate()) {
+ IEnumerable<ISegment> segments;
+ if (textArea.Selection.IsEmpty) {
+ if (defaultSegmentType == DefaultSegmentType.CurrentLine) {
+ segments = new ISegment[] { textArea.Document.GetLineByNumber(textArea.Caret.Line) };
+ } else if (defaultSegmentType == DefaultSegmentType.WholeDocument) {
+ segments = textArea.Document.Lines.Cast<ISegment>();
+ } else {
+ segments = null;
+ }
+ } else {
+ segments = textArea.Selection.Segments.Cast<ISegment>();
+ }
+ if (segments != null) {
+ foreach (ISegment segment in segments.Reverse()) {
+ foreach (ISegment writableSegment in textArea.GetDeletableSegments(segment).Reverse()) {
+ transformSegment(textArea, writableSegment);
+ }
+ }
+ }
+ }
+ textArea.Caret.BringCaretToView();
+ args.Handled = true;
+ }
+ }
+ #endregion
+
+ #region EnterLineBreak
+ static void OnEnter(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.IsKeyboardFocused) {
+ textArea.PerformTextInput("\n");
+ args.Handled = true;
+ }
+ }
+ #endregion
+
+ #region Tab
+ static void OnTab(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ using (textArea.Document.RunUpdate()) {
+ if (textArea.Selection.IsMultiline) {
+ var segment = textArea.Selection.SurroundingSegment;
+ DocumentLine start = textArea.Document.GetLineByOffset(segment.Offset);
+ DocumentLine end = textArea.Document.GetLineByOffset(segment.EndOffset);
+ // don't include the last line if no characters on it are selected
+ if (start != end && end.Offset == segment.EndOffset)
+ end = end.PreviousLine;
+ DocumentLine current = start;
+ while (true) {
+ int offset = current.Offset;
+ if (textArea.ReadOnlySectionProvider.CanInsert(offset))
+ textArea.Document.Replace(offset, 0, textArea.Options.IndentationString, OffsetChangeMappingType.KeepAnchorBeforeInsertion);
+ if (current == end)
+ break;
+ current = current.NextLine;
+ }
+ } else {
+ string indentationString = textArea.Options.GetIndentationString(textArea.Caret.Column);
+ textArea.ReplaceSelectionWithText(indentationString);
+ }
+ }
+ textArea.Caret.BringCaretToView();
+ args.Handled = true;
+ }
+ }
+
+ static void OnShiftTab(object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedLines(
+ delegate (TextArea textArea, DocumentLine line) {
+ int offset = line.Offset;
+ ISegment s = TextUtilities.GetSingleIndentationSegment(textArea.Document, offset, textArea.Options.IndentationSize);
+ if (s.Length > 0) {
+ s = textArea.GetDeletableSegments(s).FirstOrDefault();
+ if (s != null && s.Length > 0) {
+ textArea.Document.Remove(s.Offset, s.Length);
+ }
+ }
+ }, target, args, DefaultSegmentType.CurrentLine);
+ }
+ #endregion
+
+ #region Delete
+ static ExecutedRoutedEventHandler OnDelete(RoutedUICommand selectingCommand)
+ {
+ return (target, args) => {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ // call BeginUpdate before running the 'selectingCommand'
+ // so that undoing the delete does not select the deleted character
+ using (textArea.Document.RunUpdate()) {
+ if (textArea.Selection.IsEmpty) {
+ TextViewPosition oldCaretPosition = textArea.Caret.Position;
+ if (textArea.Caret.IsInVirtualSpace && selectingCommand == EditingCommands.SelectRightByCharacter)
+ EditingCommands.SelectRightByWord.Execute(args.Parameter, textArea);
+ else
+ selectingCommand.Execute(args.Parameter, textArea);
+ bool hasSomethingDeletable = false;
+ foreach (ISegment s in textArea.Selection.Segments) {
+ if (textArea.GetDeletableSegments(s).Length > 0) {
+ hasSomethingDeletable = true;
+ break;
+ }
+ }
+ if (!hasSomethingDeletable) {
+ // If nothing in the selection is deletable; then reset caret+selection
+ // to the previous value. This prevents the caret from moving through read-only sections.
+ textArea.Caret.Position = oldCaretPosition;
+ textArea.ClearSelection();
+ }
+ }
+ textArea.RemoveSelectedText();
+ }
+ textArea.Caret.BringCaretToView();
+ args.Handled = true;
+ }
+ };
+ }
+
+ static void CanDelete(object target, CanExecuteRoutedEventArgs args)
+ {
+ // HasSomethingSelected for delete command
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.CanExecute = !textArea.Selection.IsEmpty;
+ args.Handled = true;
+ }
+ }
+ #endregion
+
+ #region Clipboard commands
+ static void CanCutOrCopy(object target, CanExecuteRoutedEventArgs args)
+ {
+ // HasSomethingSelected for copy and cut commands
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.CanExecute = textArea.Options.CutCopyWholeLine || !textArea.Selection.IsEmpty;
+ args.Handled = true;
+ }
+ }
+
+ static void OnCopy(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ if (textArea.Selection.IsEmpty && textArea.Options.CutCopyWholeLine) {
+ DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
+ CopyWholeLine(textArea, currentLine);
+ } else {
+ CopySelectedText(textArea);
+ }
+ args.Handled = true;
+ }
+ }
+
+ static void OnCut(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ if (textArea.Selection.IsEmpty && textArea.Options.CutCopyWholeLine) {
+ DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
+ CopyWholeLine(textArea, currentLine);
+ ISegment[] segmentsToDelete = textArea.GetDeletableSegments(new SimpleSegment(currentLine.Offset, currentLine.TotalLength));
+ for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
+ textArea.Document.Remove(segmentsToDelete[i]);
+ }
+ } else {
+ CopySelectedText(textArea);
+ textArea.RemoveSelectedText();
+ }
+ textArea.Caret.BringCaretToView();
+ args.Handled = true;
+ }
+ }
+
+ static void CopySelectedText(TextArea textArea)
+ {
+ var data = textArea.Selection.CreateDataObject(textArea);
+
+ try {
+ Clipboard.SetDataObject(data, true);
+ } catch (ExternalException) {
+ // Apparently this exception sometimes happens randomly.
+ // The MS controls just ignore it, so we'll do the same.
+ return;
+ }
+
+ string text = textArea.Selection.GetText();
+ text = TextUtilities.NormalizeNewLines(text, Environment.NewLine);
+ textArea.OnTextCopied(new TextEventArgs(text));
+ }
+
+ const string LineSelectedType = "MSDEVLineSelect"; // This is the type VS 2003 and 2005 use for flagging a whole line copy
+
+ static void CopyWholeLine(TextArea textArea, DocumentLine line)
+ {
+ ISegment wholeLine = new SimpleSegment(line.Offset, line.TotalLength);
+ string text = textArea.Document.GetText(wholeLine);
+ // Ensure we use the appropriate newline sequence for the OS
+ text = TextUtilities.NormalizeNewLines(text, Environment.NewLine);
+ DataObject data = new DataObject(text);
+
+ // Also copy text in HTML format to clipboard - good for pasting text into Word
+ // or to the SharpDevelop forums.
+ 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);
+ lineSelected.WriteByte(1);
+ data.SetData(LineSelectedType, lineSelected, false);
+
+ try {
+ Clipboard.SetDataObject(data, true);
+ } catch (ExternalException) {
+ // Apparently this exception sometimes happens randomly.
+ // The MS controls just ignore it, so we'll do the same.
+ return;
+ }
+ textArea.OnTextCopied(new TextEventArgs(text));
+ }
+
+ static void CanPaste(object target, CanExecuteRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ args.CanExecute = textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset)
+ && Clipboard.ContainsText();
+ // WPF Clipboard.ContainsText() is safe to call without catching ExternalExceptions
+ // because it doesn't try to lock the clipboard - it just peeks inside with IsClipboardFormatAvailable().
+ args.Handled = true;
+ }
+ }
+
+ static void OnPaste(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ IDataObject dataObject;
+ try {
+ dataObject = Clipboard.GetDataObject();
+ } catch (ExternalException) {
+ return;
+ }
+ if (dataObject == null)
+ return;
+ Debug.WriteLine( dataObject.GetData(DataFormats.Html) as string );
+
+ // convert text back to correct newlines for this document
+ string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
+ string text;
+ try {
+ text = (string)dataObject.GetData(DataFormats.UnicodeText);
+ text = TextUtilities.NormalizeNewLines(text, newLine);
+ } catch (OutOfMemoryException) {
+ return;
+ }
+
+ if (!string.IsNullOrEmpty(text)) {
+ bool fullLine = textArea.Options.CutCopyWholeLine && dataObject.GetDataPresent(LineSelectedType);
+ bool rectangular = dataObject.GetDataPresent(RectangleSelection.RectangularSelectionDataType);
+ if (fullLine) {
+ DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
+ if (textArea.ReadOnlySectionProvider.CanInsert(currentLine.Offset)) {
+ textArea.Document.Insert(currentLine.Offset, text);
+ }
+ } else if (rectangular && textArea.Selection.IsEmpty && !(textArea.Selection is RectangleSelection)) {
+ if (!RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, false))
+ textArea.ReplaceSelectionWithText(text);
+ } else {
+ textArea.ReplaceSelectionWithText(text);
+ }
+ }
+ textArea.Caret.BringCaretToView();
+ args.Handled = true;
+ }
+ }
+ #endregion
+
+ #region DeleteLine
+ static void OnDeleteLine(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ DocumentLine currentLine = textArea.Document.GetLineByNumber(textArea.Caret.Line);
+ textArea.Selection = Selection.Create(textArea, currentLine.Offset, currentLine.Offset + currentLine.TotalLength);
+ textArea.RemoveSelectedText();
+ args.Handled = true;
+ }
+ }
+ #endregion
+
+ #region Remove..Whitespace / Convert Tabs-Spaces
+ static void OnRemoveLeadingWhitespace(object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedLines(
+ delegate (TextArea textArea, DocumentLine line) {
+ textArea.Document.Remove(TextUtilities.GetLeadingWhitespace(textArea.Document, line));
+ }, target, args, DefaultSegmentType.WholeDocument);
+ }
+
+ static void OnRemoveTrailingWhitespace(object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedLines(
+ delegate (TextArea textArea, DocumentLine line) {
+ textArea.Document.Remove(TextUtilities.GetTrailingWhitespace(textArea.Document, line));
+ }, target, args, DefaultSegmentType.WholeDocument);
+ }
+
+ static void OnConvertTabsToSpaces(object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedSegments(ConvertTabsToSpaces, target, args, DefaultSegmentType.WholeDocument);
+ }
+
+ static void OnConvertLeadingTabsToSpaces(object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedLines(
+ delegate (TextArea textArea, DocumentLine line) {
+ ConvertTabsToSpaces(textArea, TextUtilities.GetLeadingWhitespace(textArea.Document, line));
+ }, target, args, DefaultSegmentType.WholeDocument);
+ }
+
+ static void ConvertTabsToSpaces(TextArea textArea, ISegment segment)
+ {
+ TextDocument document = textArea.Document;
+ int endOffset = segment.EndOffset;
+ string indentationString = new string(' ', textArea.Options.IndentationSize);
+ for (int offset = segment.Offset; offset < endOffset; offset++) {
+ if (document.GetCharAt(offset) == '\t') {
+ document.Replace(offset, 1, indentationString, OffsetChangeMappingType.CharacterReplace);
+ endOffset += indentationString.Length - 1;
+ }
+ }
+ }
+
+ static void OnConvertSpacesToTabs(object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedSegments(ConvertSpacesToTabs, target, args, DefaultSegmentType.WholeDocument);
+ }
+
+ static void OnConvertLeadingSpacesToTabs(object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedLines(
+ delegate (TextArea textArea, DocumentLine line) {
+ ConvertSpacesToTabs(textArea, TextUtilities.GetLeadingWhitespace(textArea.Document, line));
+ }, target, args, DefaultSegmentType.WholeDocument);
+ }
+
+ static void ConvertSpacesToTabs(TextArea textArea, ISegment segment)
+ {
+ TextDocument document = textArea.Document;
+ int endOffset = segment.EndOffset;
+ int indentationSize = textArea.Options.IndentationSize;
+ int spacesCount = 0;
+ for (int offset = segment.Offset; offset < endOffset; offset++) {
+ if (document.GetCharAt(offset) == ' ') {
+ spacesCount++;
+ if (spacesCount == indentationSize) {
+ document.Replace(offset - (indentationSize - 1), indentationSize, "\t", OffsetChangeMappingType.CharacterReplace);
+ spacesCount = 0;
+ offset -= indentationSize - 1;
+ endOffset -= indentationSize - 1;
+ }
+ } else {
+ spacesCount = 0;
+ }
+ }
+ }
+ #endregion
+
+ #region Convert...Case
+ static void ConvertCase(Func<string, string> transformText, object target, ExecutedRoutedEventArgs args)
+ {
+ TransformSelectedSegments(
+ delegate (TextArea textArea, ISegment segment) {
+ string oldText = textArea.Document.GetText(segment);
+ string newText = transformText(oldText);
+ textArea.Document.Replace(segment.Offset, segment.Length, newText, OffsetChangeMappingType.CharacterReplace);
+ }, target, args, DefaultSegmentType.WholeDocument);
+ }
+
+ static void OnConvertToUpperCase(object target, ExecutedRoutedEventArgs args)
+ {
+ ConvertCase(CultureInfo.CurrentCulture.TextInfo.ToUpper, target, args);
+ }
+
+ static void OnConvertToLowerCase(object target, ExecutedRoutedEventArgs args)
+ {
+ ConvertCase(CultureInfo.CurrentCulture.TextInfo.ToLower, target, args);
+ }
+
+ static void OnConvertToTitleCase(object target, ExecutedRoutedEventArgs args)
+ {
+ ConvertCase(CultureInfo.CurrentCulture.TextInfo.ToTitleCase, target, args);
+ }
+
+ static void OnInvertCase(object target, ExecutedRoutedEventArgs args)
+ {
+ ConvertCase(InvertCase, target, args);
+ }
+
+ static string InvertCase(string text)
+ {
+ CultureInfo culture = CultureInfo.CurrentCulture;
+ char[] buffer = text.ToCharArray();
+ for (int i = 0; i < buffer.Length; ++i) {
+ char c = buffer[i];
+ buffer[i] = char.IsUpper(c) ? char.ToLower(c, culture) : char.ToUpper(c, culture);
+ }
+ return new string(buffer);
+ }
+ #endregion
+
+ static void OnIndentSelection(object target, ExecutedRoutedEventArgs args)
+ {
+ TextArea textArea = GetTextArea(target);
+ if (textArea != null && textArea.Document != null) {
+ using (textArea.Document.RunUpdate()) {
+ int start, end;
+ if (textArea.Selection.IsEmpty) {
+ start = 1;
+ end = textArea.Document.LineCount;
+ } else {
+ start = textArea.Document.GetLineByOffset(textArea.Selection.SurroundingSegment.Offset).LineNumber;
+ end = textArea.Document.GetLineByOffset(textArea.Selection.SurroundingSegment.EndOffset).LineNumber;
+ }
+ textArea.IndentationStrategy.IndentLines(textArea.Document, start, end);
+ }
+ textArea.Caret.BringCaretToView();
+ args.Handled = true;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EmptySelection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EmptySelection.cs
new file mode 100644
index 000000000..6cca63ecb
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/EmptySelection.cs
@@ -0,0 +1,85 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ sealed class EmptySelection : Selection
+ {
+ public EmptySelection(TextArea textArea) : base(textArea)
+ {
+ }
+
+ public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
+ {
+ return this;
+ }
+
+ public override TextViewPosition StartPosition {
+ get { return new TextViewPosition(TextLocation.Empty); }
+ }
+
+ public override TextViewPosition EndPosition {
+ get { return new TextViewPosition(TextLocation.Empty); }
+ }
+
+ public override ISegment SurroundingSegment {
+ get { return null; }
+ }
+
+ public override Selection SetEndpoint(TextViewPosition endPosition)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition)
+ {
+ var document = textArea.Document;
+ if (document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return Create(textArea, startPosition, endPosition);
+ }
+
+ public override IEnumerable<SelectionSegment> Segments {
+ get { return Empty<SelectionSegment>.Array; }
+ }
+
+ public override string GetText()
+ {
+ return string.Empty;
+ }
+
+ public override void ReplaceSelectionWithText(string newText)
+ {
+ if (newText == null)
+ throw new ArgumentNullException("newText");
+ newText = AddSpacesIfRequired(newText, textArea.Caret.Position, textArea.Caret.Position);
+ if (newText.Length > 0) {
+ if (textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset)) {
+ textArea.Document.Insert(textArea.Caret.Offset, newText);
+ }
+ }
+ textArea.Caret.VisualColumn = -1;
+ }
+
+ public override int Length {
+ get { return 0; }
+ }
+
+ // Use reference equality because there's only one EmptySelection per text area.
+ public override int GetHashCode()
+ {
+ return RuntimeHelpers.GetHashCode(this);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return this == obj;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/IReadOnlySectionProvider.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/IReadOnlySectionProvider.cs
new file mode 100644
index 000000000..fc775baf3
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/IReadOnlySectionProvider.cs
@@ -0,0 +1,32 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Determines whether the document can be modified.
+ /// </summary>
+ public interface IReadOnlySectionProvider
+ {
+ /// <summary>
+ /// Gets whether insertion is possible at the specified offset.
+ /// </summary>
+ bool CanInsert(int offset);
+
+ /// <summary>
+ /// Gets the deletable segments inside the given segment.
+ /// </summary>
+ /// <remarks>
+ /// All segments in the result must be within the given segment, and they must be returned in order
+ /// (e.g. if two segments are returned, EndOffset of first segment must be less than StartOffset of second segment).
+ ///
+ /// For replacements, the last segment being returned will be replaced with the new text. If an empty list is returned,
+ /// no replacement will be done.
+ /// </remarks>
+ IEnumerable<ISegment> GetDeletableSegments(ISegment segment);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs
new file mode 100644
index 000000000..bc2c6f5e4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeNativeWrapper.cs
@@ -0,0 +1,206 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+using Tango.Scripting.Editors;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+using Draw = System.Drawing;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Native API required for IME support.
+ /// </summary>
+ static class ImeNativeWrapper
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ struct CompositionForm
+ {
+ public int dwStyle;
+ public POINT ptCurrentPos;
+ public RECT rcArea;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct POINT
+ {
+ public int x;
+ public int y;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct RECT
+ {
+ public int left;
+ public int top;
+ public int right;
+ public int bottom;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
+ struct LOGFONT
+ {
+ public int lfHeight;
+ public int lfWidth;
+ public int lfEscapement;
+ public int lfOrientation;
+ public int lfWeight;
+ public byte lfItalic;
+ public byte lfUnderline;
+ public byte lfStrikeOut;
+ public byte lfCharSet;
+ public byte lfOutPrecision;
+ public byte lfClipPrecision;
+ public byte lfQuality;
+ public byte lfPitchAndFamily;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)] public string lfFaceName;
+ }
+
+ const int CPS_CANCEL = 0x4;
+ const int NI_COMPOSITIONSTR = 0x15;
+ const int GCS_COMPSTR = 0x0008;
+
+ public const int WM_IME_COMPOSITION = 0x10F;
+ public const int WM_IME_SETCONTEXT = 0x281;
+ public const int WM_INPUTLANGCHANGE = 0x51;
+
+ [DllImport("imm32.dll")]
+ public static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
+ [DllImport("imm32.dll")]
+ internal static extern IntPtr ImmGetContext(IntPtr hWnd);
+ [DllImport("imm32.dll")]
+ internal static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd);
+ [DllImport("imm32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
+ [DllImport("imm32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool ImmNotifyIME(IntPtr hIMC, int dwAction, int dwIndex, int dwValue = 0);
+ [DllImport("imm32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool ImmSetCompositionWindow(IntPtr hIMC, ref CompositionForm form);
+ [DllImport("imm32.dll", CharSet = CharSet.Unicode)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT font);
+ [DllImport("imm32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool ImmGetCompositionFont(IntPtr hIMC, out LOGFONT font);
+
+ [DllImport("msctf.dll")]
+ static extern int TF_CreateThreadMgr(out ITfThreadMgr threadMgr);
+
+ [ThreadStatic] static bool textFrameworkThreadMgrInitialized;
+ [ThreadStatic] static ITfThreadMgr textFrameworkThreadMgr;
+
+ public static ITfThreadMgr GetTextFrameworkThreadManager()
+ {
+ if (!textFrameworkThreadMgrInitialized) {
+ textFrameworkThreadMgrInitialized = true;
+ TF_CreateThreadMgr(out textFrameworkThreadMgr);
+ }
+ return textFrameworkThreadMgr;
+ }
+
+ public static bool NotifyIme(IntPtr hIMC)
+ {
+ return ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL);
+ }
+
+ public static bool SetCompositionWindow(HwndSource source, IntPtr hIMC, TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ Rect textViewBounds = textArea.TextView.GetBounds(source);
+ Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source);
+ CompositionForm form = new CompositionForm();
+ form.dwStyle = 0x0020;
+ form.ptCurrentPos.x = (int)Math.Max(characterBounds.Left, textViewBounds.Left);
+ form.ptCurrentPos.y = (int)Math.Max(characterBounds.Top, textViewBounds.Top);
+ form.rcArea.left = (int)textViewBounds.Left;
+ form.rcArea.top = (int)textViewBounds.Top;
+ form.rcArea.right = (int)textViewBounds.Right;
+ form.rcArea.bottom = (int)textViewBounds.Bottom;
+ return ImmSetCompositionWindow(hIMC, ref form);
+ }
+
+ public static bool SetCompositionFont(HwndSource source, IntPtr hIMC, TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ LOGFONT lf = new LOGFONT();
+ Rect characterBounds = textArea.TextView.GetCharacterBounds(textArea.Caret.Position, source);
+ lf.lfFaceName = textArea.FontFamily.Source;
+ lf.lfHeight = (int)characterBounds.Height;
+ return ImmSetCompositionFont(hIMC, ref lf);
+ }
+
+ static Rect GetBounds(this TextView textView, HwndSource source)
+ {
+ // this may happen during layout changes in AvalonDock, so we just return an empty rectangle
+ // in those cases. It should be refreshed immediately.
+ if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView))
+ return EMPTY_RECT;
+ Rect displayRect = new Rect(0, 0, textView.ActualWidth, textView.ActualHeight);
+ return textView
+ .TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual
+ .TransformToDevice(source.RootVisual); // rect on HWND
+ }
+
+ static readonly Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
+
+ static Rect GetCharacterBounds(this TextView textView, TextViewPosition pos, HwndSource source)
+ {
+ VisualLine vl = textView.GetVisualLine(pos.Line);
+ if (vl == null)
+ return EMPTY_RECT;
+ // this may happen during layout changes in AvalonDock, so we just return an empty rectangle
+ // in those cases. It should be refreshed immediately.
+ if (source.RootVisual == null || !source.RootVisual.IsAncestorOf(textView))
+ return EMPTY_RECT;
+ TextLine line = vl.GetTextLine(pos.VisualColumn);
+ Rect displayRect;
+ // calculate the display rect for the current character
+ if (pos.VisualColumn < vl.VisualLengthWithEndOfLineMarker) {
+ displayRect = line.GetTextBounds(pos.VisualColumn, 1).First().Rectangle;
+ displayRect.Offset(0, vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop));
+ } else {
+ // if we are in virtual space, we just use one wide-space as character width
+ displayRect = new Rect(vl.GetVisualPosition(pos.VisualColumn, VisualYPosition.TextTop),
+ new Size(textView.WideSpaceWidth, textView.DefaultLineHeight));
+ }
+ // adjust to current scrolling
+ displayRect.Offset(-textView.ScrollOffset);
+ return textView
+ .TransformToAncestor(source.RootVisual).TransformBounds(displayRect) // rect on root visual
+ .TransformToDevice(source.RootVisual); // rect on HWND
+ }
+ }
+
+ [ComImport, Guid("aa80e801-2021-11d2-93e0-0060b067b86e"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface ITfThreadMgr
+ {
+ void Activate(out int clientId);
+ void Deactivate();
+ void CreateDocumentMgr(out IntPtr docMgr);
+ void EnumDocumentMgrs(out IntPtr enumDocMgrs);
+ void GetFocus(out IntPtr docMgr);
+ void SetFocus(IntPtr docMgr);
+ void AssociateFocus(IntPtr hwnd, IntPtr newDocMgr, out IntPtr prevDocMgr);
+ void IsThreadFocus([MarshalAs(UnmanagedType.Bool)] out bool isFocus);
+ void GetFunctionProvider(ref Guid classId, out IntPtr funcProvider);
+ void EnumFunctionProviders(out IntPtr enumProviders);
+ void GetGlobalCompartment(out IntPtr compartmentMgr);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeSupport.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeSupport.cs
new file mode 100644
index 000000000..ad4c22e10
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/ImeSupport.cs
@@ -0,0 +1,150 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ class ImeSupport
+ {
+ readonly TextArea textArea;
+ IntPtr currentContext;
+ IntPtr previousContext;
+ IntPtr defaultImeWnd;
+ HwndSource hwndSource;
+ EventHandler requerySuggestedHandler; // we need to keep the event handler instance alive because CommandManager.RequerySuggested uses weak references
+ bool isReadOnly;
+
+ public ImeSupport(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ InputMethod.SetIsInputMethodSuspended(this.textArea, textArea.Options.EnableImeSupport);
+ // We listen to CommandManager.RequerySuggested for both caret offset changes and changes to the set of read-only sections.
+ // This is because there's no dedicated event for read-only section changes; but RequerySuggested needs to be raised anyways
+ // to invalidate the Paste command.
+ requerySuggestedHandler = OnRequerySuggested;
+ CommandManager.RequerySuggested += requerySuggestedHandler;
+ textArea.OptionChanged += TextAreaOptionChanged;
+ }
+
+ void OnRequerySuggested(object sender, EventArgs e)
+ {
+ UpdateImeEnabled();
+ }
+
+ void TextAreaOptionChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "EnableImeSupport") {
+ InputMethod.SetIsInputMethodSuspended(this.textArea, textArea.Options.EnableImeSupport);
+ UpdateImeEnabled();
+ }
+ }
+
+ public void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ UpdateImeEnabled();
+ }
+
+ public void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ if (e.OldFocus == textArea && currentContext != IntPtr.Zero)
+ ImeNativeWrapper.NotifyIme(currentContext);
+ ClearContext();
+ }
+
+ void UpdateImeEnabled()
+ {
+ if (textArea.Options.EnableImeSupport && textArea.IsKeyboardFocused) {
+ bool newReadOnly = !textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset);
+ if (hwndSource == null || isReadOnly != newReadOnly) {
+ ClearContext(); // clear existing context (on read-only change)
+ isReadOnly = newReadOnly;
+ CreateContext();
+ }
+ } else {
+ ClearContext();
+ }
+ }
+
+ void ClearContext()
+ {
+ if (hwndSource != null) {
+ ImeNativeWrapper.ImmAssociateContext(hwndSource.Handle, previousContext);
+ ImeNativeWrapper.ImmReleaseContext(defaultImeWnd, currentContext);
+ currentContext = IntPtr.Zero;
+ defaultImeWnd = IntPtr.Zero;
+ hwndSource.RemoveHook(WndProc);
+ hwndSource = null;
+ }
+ }
+
+ void CreateContext()
+ {
+ hwndSource = (HwndSource)PresentationSource.FromVisual(this.textArea);
+ if (hwndSource != null) {
+ if (isReadOnly) {
+ defaultImeWnd = IntPtr.Zero;
+ currentContext = IntPtr.Zero;
+ } else {
+ defaultImeWnd = ImeNativeWrapper.ImmGetDefaultIMEWnd(IntPtr.Zero);
+ currentContext = ImeNativeWrapper.ImmGetContext(defaultImeWnd);
+ }
+ previousContext = ImeNativeWrapper.ImmAssociateContext(hwndSource.Handle, currentContext);
+ hwndSource.AddHook(WndProc);
+ // UpdateCompositionWindow() will be called by the caret becoming visible
+
+ var threadMgr = ImeNativeWrapper.GetTextFrameworkThreadManager();
+ if (threadMgr != null) {
+ // Even though the docu says passing null is invalid, this seems to help
+ // activating the IME on the default input context that is shared with WPF
+ threadMgr.SetFocus(IntPtr.Zero);
+ }
+ }
+ }
+
+ IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
+ {
+ switch (msg) {
+ case ImeNativeWrapper.WM_INPUTLANGCHANGE:
+ // Don't mark the message as handled; other windows
+ // might want to handle it as well.
+
+ // If we have a context, recreate it
+ if (hwndSource != null) {
+ ClearContext();
+ CreateContext();
+ }
+ break;
+ case ImeNativeWrapper.WM_IME_COMPOSITION:
+ UpdateCompositionWindow();
+ break;
+ }
+ return IntPtr.Zero;
+ }
+
+ public void UpdateCompositionWindow()
+ {
+ if (currentContext != IntPtr.Zero) {
+ ImeNativeWrapper.SetCompositionFont(hwndSource, currentContext, textArea);
+ ImeNativeWrapper.SetCompositionWindow(hwndSource, currentContext, textArea);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/LineNumberMargin.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/LineNumberMargin.cs
new file mode 100644
index 000000000..3da96b08c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/LineNumberMargin.cs
@@ -0,0 +1,243 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Margin showing line numbers.
+ /// </summary>
+ public class LineNumberMargin : AbstractMargin, IWeakEventListener
+ {
+ public Brush Foreground
+ {
+ get { return (Brush)GetValue(ForegroundProperty); }
+ set { SetValue(ForegroundProperty, value); }
+ }
+ public static readonly DependencyProperty ForegroundProperty =
+ DependencyProperty.Register("Foreground", typeof(Brush), typeof(LineNumberMargin), new PropertyMetadata(Brushes.Gray));
+
+
+
+ static LineNumberMargin()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(LineNumberMargin),
+ new FrameworkPropertyMetadata(typeof(LineNumberMargin)));
+ }
+
+ TextArea textArea;
+
+ Typeface typeface;
+ double emSize;
+
+ /// <inheritdoc/>
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ typeface = this.CreateTypeface();
+ emSize = (double)GetValue(TextBlock.FontSizeProperty);
+
+ FormattedText text = TextFormatterFactory.CreateFormattedText(
+ this,
+ new string('9', maxLineNumberLength),
+ typeface,
+ emSize,
+ Foreground
+ );
+ return new Size(text.Width, 0);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ TextView textView = this.TextView;
+ Size renderSize = this.RenderSize;
+ if (textView != null && textView.VisualLinesValid) {
+ var foreground = Foreground;
+ foreach (VisualLine line in textView.VisualLines) {
+ int lineNumber = line.FirstDocumentLine.LineNumber;
+ FormattedText text = TextFormatterFactory.CreateFormattedText(
+ this,
+ lineNumber.ToString(CultureInfo.CurrentCulture),
+ typeface, emSize, foreground
+ );
+ double y = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.TextTop);
+ drawingContext.DrawText(text, new Point((renderSize.Width / 2) - text.Width + 5, y - textView.VerticalOffset));
+ }
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView)
+ {
+ if (oldTextView != null) {
+ oldTextView.VisualLinesChanged -= TextViewVisualLinesChanged;
+ }
+ base.OnTextViewChanged(oldTextView, newTextView);
+ if (newTextView != null) {
+ newTextView.VisualLinesChanged += TextViewVisualLinesChanged;
+
+ // find the text area belonging to the new text view
+ textArea = newTextView.Services.GetService(typeof(TextArea)) as TextArea;
+ } else {
+ textArea = null;
+ }
+ InvalidateVisual();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnDocumentChanged(TextDocument oldDocument, TextDocument newDocument)
+ {
+ if (oldDocument != null) {
+ PropertyChangedEventManager.RemoveListener(oldDocument, this, "LineCount");
+ }
+ base.OnDocumentChanged(oldDocument, newDocument);
+ if (newDocument != null) {
+ PropertyChangedEventManager.AddListener(newDocument, this, "LineCount");
+ }
+ OnDocumentLineCountChanged();
+ }
+
+ /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
+ protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(PropertyChangedEventManager)) {
+ OnDocumentLineCountChanged();
+ return true;
+ }
+ return false;
+ }
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ return ReceiveWeakEvent(managerType, sender, e);
+ }
+
+ int maxLineNumberLength = 1;
+
+ void OnDocumentLineCountChanged()
+ {
+ int documentLineCount = Document != null ? Document.LineCount : 1;
+ int newLength = documentLineCount.ToString(CultureInfo.CurrentCulture).Length;
+
+ // The margin looks too small when there is only one digit, so always reserve space for
+ // at least two digits
+ if (newLength < 2)
+ newLength = 2;
+
+ if (newLength != maxLineNumberLength) {
+ maxLineNumberLength = newLength;
+ InvalidateMeasure();
+ }
+ }
+
+ void TextViewVisualLinesChanged(object sender, EventArgs e)
+ {
+ InvalidateVisual();
+ }
+
+ AnchorSegment selectionStart;
+ bool selecting;
+
+ /// <inheritdoc/>
+ protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
+ {
+ base.OnMouseLeftButtonDown(e);
+ if (!e.Handled && TextView != null && textArea != null) {
+ e.Handled = true;
+ textArea.Focus();
+
+ SimpleSegment currentSeg = GetTextLineSegment(e);
+ if (currentSeg == SimpleSegment.Invalid)
+ return;
+ textArea.Caret.Offset = currentSeg.Offset + currentSeg.Length;
+ if (CaptureMouse()) {
+ selecting = true;
+ selectionStart = new AnchorSegment(Document, currentSeg.Offset, currentSeg.Length);
+ if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) {
+ SimpleSelection simpleSelection = textArea.Selection as SimpleSelection;
+ if (simpleSelection != null)
+ selectionStart = new AnchorSegment(Document, simpleSelection.SurroundingSegment);
+ }
+ textArea.Selection = Selection.Create(textArea, selectionStart);
+ if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) {
+ ExtendSelection(currentSeg);
+ }
+ }
+ }
+ }
+
+ SimpleSegment GetTextLineSegment(MouseEventArgs e)
+ {
+ Point pos = e.GetPosition(TextView);
+ pos.X = 0;
+ pos.Y += TextView.VerticalOffset;
+ VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y);
+ if (vl == null)
+ return SimpleSegment.Invalid;
+ TextLine tl = vl.GetTextLineByVisualYPosition(pos.Y);
+ int visualStartColumn = vl.GetTextLineVisualStartColumn(tl);
+ int visualEndColumn = visualStartColumn + tl.Length;
+ int relStart = vl.FirstDocumentLine.Offset;
+ int startOffset = vl.GetRelativeOffset(visualStartColumn) + relStart;
+ int endOffset = vl.GetRelativeOffset(visualEndColumn) + relStart;
+ if (endOffset == vl.LastDocumentLine.Offset + vl.LastDocumentLine.Length)
+ endOffset += vl.LastDocumentLine.DelimiterLength;
+ return new SimpleSegment(startOffset, endOffset - startOffset);
+ }
+
+ void ExtendSelection(SimpleSegment currentSeg)
+ {
+ if (currentSeg.Offset < selectionStart.Offset) {
+ textArea.Caret.Offset = currentSeg.Offset;
+ textArea.Selection = Selection.Create(textArea, currentSeg.Offset, selectionStart.Offset + selectionStart.Length);
+ } else {
+ textArea.Caret.Offset = currentSeg.Offset + currentSeg.Length;
+ textArea.Selection = Selection.Create(textArea, selectionStart.Offset, currentSeg.Offset + currentSeg.Length);
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ if (selecting && textArea != null && TextView != null) {
+ e.Handled = true;
+ SimpleSegment currentSeg = GetTextLineSegment(e);
+ if (currentSeg == SimpleSegment.Invalid)
+ return;
+ ExtendSelection(currentSeg);
+ }
+ base.OnMouseMove(e);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
+ {
+ if (selecting) {
+ selecting = false;
+ selectionStart = null;
+ ReleaseMouseCapture();
+ e.Handled = true;
+ }
+ base.OnMouseLeftButtonUp(e);
+ }
+
+ /// <inheritdoc/>
+ protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
+ {
+ // accept clicks even when clicking on the background
+ return new PointHitTestResult(this, hitTestParameters.HitPoint);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/NoReadOnlySections.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/NoReadOnlySections.cs
new file mode 100644
index 000000000..35cd1dd1b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/NoReadOnlySections.cs
@@ -0,0 +1,50 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// <see cref="IReadOnlySectionProvider"/> that has no read-only sections; all text is editable.
+ /// </summary>
+ sealed class NoReadOnlySections : IReadOnlySectionProvider
+ {
+ public static readonly NoReadOnlySections Instance = new NoReadOnlySections();
+
+ public bool CanInsert(int offset)
+ {
+ return true;
+ }
+
+ public IEnumerable<ISegment> GetDeletableSegments(ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+ // the segment is always deletable
+ return ExtensionMethods.Sequence(segment);
+ }
+ }
+
+ /// <summary>
+ /// <see cref="IReadOnlySectionProvider"/> that completely disables editing.
+ /// </summary>
+ sealed class ReadOnlyDocument : IReadOnlySectionProvider
+ {
+ public static readonly ReadOnlyDocument Instance = new ReadOnlyDocument();
+
+ public bool CanInsert(int offset)
+ {
+ return false;
+ }
+
+ public IEnumerable<ISegment> GetDeletableSegments(ISegment segment)
+ {
+ return Enumerable.Empty<ISegment>();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/RectangleSelection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/RectangleSelection.cs
new file mode 100644
index 000000000..2d87d7934
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/RectangleSelection.cs
@@ -0,0 +1,396 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Rectangular selection ("box selection").
+ /// </summary>
+ public sealed class RectangleSelection : Selection
+ {
+ #region Commands
+ /// <summary>
+ /// Expands the selection left by one character, creating a rectangular selection.
+ /// Key gesture: Alt+Shift+Left
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectLeftByCharacter = Command("BoxSelectLeftByCharacter");
+
+ /// <summary>
+ /// Expands the selection right by one character, creating a rectangular selection.
+ /// Key gesture: Alt+Shift+Right
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectRightByCharacter = Command("BoxSelectRightByCharacter");
+
+ /// <summary>
+ /// Expands the selection left by one word, creating a rectangular selection.
+ /// Key gesture: Ctrl+Alt+Shift+Left
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectLeftByWord = Command("BoxSelectLeftByWord");
+
+ /// <summary>
+ /// Expands the selection left by one word, creating a rectangular selection.
+ /// Key gesture: Ctrl+Alt+Shift+Right
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectRightByWord = Command("BoxSelectRightByWord");
+
+ /// <summary>
+ /// Expands the selection up by one line, creating a rectangular selection.
+ /// Key gesture: Alt+Shift+Up
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectUpByLine = Command("BoxSelectUpByLine");
+
+ /// <summary>
+ /// Expands the selection up by one line, creating a rectangular selection.
+ /// Key gesture: Alt+Shift+Down
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectDownByLine = Command("BoxSelectDownByLine");
+
+ /// <summary>
+ /// Expands the selection to the start of the line, creating a rectangular selection.
+ /// Key gesture: Alt+Shift+Home
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectToLineStart = Command("BoxSelectToLineStart");
+
+ /// <summary>
+ /// Expands the selection to the end of the line, creating a rectangular selection.
+ /// Key gesture: Alt+Shift+End
+ /// </summary>
+ public static readonly RoutedUICommand BoxSelectToLineEnd = Command("BoxSelectToLineEnd");
+
+ static RoutedUICommand Command(string name)
+ {
+ return new RoutedUICommand(name, name, typeof(RectangleSelection));
+ }
+ #endregion
+
+ TextDocument document;
+ readonly int startLine, endLine;
+ readonly double startXPos, endXPos;
+ readonly int topLeftOffset, bottomRightOffset;
+ readonly TextViewPosition start, end;
+
+ readonly List<SelectionSegment> segments = new List<SelectionSegment>();
+
+ #region Constructors
+ /// <summary>
+ /// Creates a new rectangular selection.
+ /// </summary>
+ public RectangleSelection(TextArea textArea, TextViewPosition start, TextViewPosition end)
+ : base(textArea)
+ {
+ InitDocument();
+ this.startLine = start.Line;
+ this.endLine = end.Line;
+ this.startXPos = GetXPos(textArea, start);
+ this.endXPos = GetXPos(textArea, end);
+ CalculateSegments();
+ this.topLeftOffset = this.segments.First().StartOffset;
+ this.bottomRightOffset = this.segments.Last().EndOffset;
+
+ this.start = start;
+ this.end = end;
+ }
+
+ private RectangleSelection(TextArea textArea, int startLine, double startXPos, TextViewPosition end)
+ : base(textArea)
+ {
+ InitDocument();
+ this.startLine = startLine;
+ this.endLine = end.Line;
+ this.startXPos = startXPos;
+ this.endXPos = GetXPos(textArea, end);
+ CalculateSegments();
+ this.topLeftOffset = this.segments.First().StartOffset;
+ this.bottomRightOffset = this.segments.Last().EndOffset;
+
+ this.start = GetStart();
+ this.end = end;
+ }
+
+ private RectangleSelection(TextArea textArea, TextViewPosition start, int endLine, double endXPos)
+ : base(textArea)
+ {
+ InitDocument();
+ this.startLine = start.Line;
+ this.endLine = endLine;
+ this.startXPos = GetXPos(textArea, start);
+ this.endXPos = endXPos;
+ CalculateSegments();
+ this.topLeftOffset = this.segments.First().StartOffset;
+ this.bottomRightOffset = this.segments.Last().EndOffset;
+
+ this.start = start;
+ this.end = GetEnd();
+ }
+
+ void InitDocument()
+ {
+ document = textArea.Document;
+ if (document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ }
+
+ static double GetXPos(TextArea textArea, TextViewPosition pos)
+ {
+ DocumentLine documentLine = textArea.Document.GetLineByNumber(pos.Line);
+ VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(documentLine);
+ int vc = visualLine.ValidateVisualColumn(pos, true);
+ TextLine textLine = visualLine.GetTextLine(vc);
+ return visualLine.GetTextLineVisualXPosition(textLine, vc);
+ }
+
+ void CalculateSegments()
+ {
+ DocumentLine nextLine = document.GetLineByNumber(Math.Min(startLine, endLine));
+ do {
+ VisualLine vl = textArea.TextView.GetOrConstructVisualLine(nextLine);
+ int startVC = vl.GetVisualColumn(new Point(startXPos, 0), true);
+ int endVC = vl.GetVisualColumn(new Point(endXPos, 0), true);
+
+ int baseOffset = vl.FirstDocumentLine.Offset;
+ int startOffset = baseOffset + vl.GetRelativeOffset(startVC);
+ int endOffset = baseOffset + vl.GetRelativeOffset(endVC);
+ segments.Add(new SelectionSegment(startOffset, startVC, endOffset, endVC));
+
+ nextLine = vl.LastDocumentLine.NextLine;
+ } while (nextLine != null && nextLine.LineNumber <= Math.Max(startLine, endLine));
+ }
+
+ TextViewPosition GetStart()
+ {
+ SelectionSegment segment = (startLine < endLine ? segments.First() : segments.Last());
+ if (startXPos < endXPos) {
+ return new TextViewPosition(document.GetLocation(segment.StartOffset), segment.StartVisualColumn);
+ } else {
+ return new TextViewPosition(document.GetLocation(segment.EndOffset), segment.EndVisualColumn);
+ }
+ }
+
+ TextViewPosition GetEnd()
+ {
+ SelectionSegment segment = (startLine < endLine ? segments.Last() : segments.First());
+ if (startXPos < endXPos) {
+ return new TextViewPosition(document.GetLocation(segment.EndOffset), segment.EndVisualColumn);
+ } else {
+ return new TextViewPosition(document.GetLocation(segment.StartOffset), segment.StartVisualColumn);
+ }
+ }
+ #endregion
+
+ /// <inheritdoc/>
+ public override string GetText()
+ {
+ StringBuilder b = new StringBuilder();
+ foreach (ISegment s in this.Segments) {
+ if (b.Length > 0)
+ b.AppendLine();
+ b.Append(document.GetText(s));
+ }
+ return b.ToString();
+ }
+
+ /// <inheritdoc/>
+ public override Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition)
+ {
+ return SetEndpoint(endPosition);
+ }
+
+ /// <inheritdoc/>
+ public override int Length {
+ get {
+ return this.Segments.Sum(s => s.Length);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override bool EnableVirtualSpace {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override ISegment SurroundingSegment {
+ get {
+ return new SimpleSegment(topLeftOffset, bottomRightOffset - topLeftOffset);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override IEnumerable<SelectionSegment> Segments {
+ get { return segments; }
+ }
+
+ /// <inheritdoc/>
+ public override TextViewPosition StartPosition {
+ get { return start; }
+ }
+
+ /// <inheritdoc/>
+ public override TextViewPosition EndPosition {
+ get { return end; }
+ }
+
+ /// <inheritdoc/>
+ public override bool Equals(object obj)
+ {
+ RectangleSelection r = obj as RectangleSelection;
+ return r != null && r.textArea == this.textArea
+ && r.topLeftOffset == this.topLeftOffset && r.bottomRightOffset == this.bottomRightOffset
+ && r.startLine == this.startLine && r.endLine == this.endLine
+ && r.startXPos == this.startXPos && r.endXPos == this.endXPos;
+ }
+
+ /// <inheritdoc/>
+ public override int GetHashCode()
+ {
+ return topLeftOffset ^ bottomRightOffset;
+ }
+
+ /// <inheritdoc/>
+ public override Selection SetEndpoint(TextViewPosition endPosition)
+ {
+ return new RectangleSelection(textArea, startLine, startXPos, endPosition);
+ }
+
+ int GetVisualColumnFromXPos(int line, double xPos)
+ {
+ var vl = textArea.TextView.GetOrConstructVisualLine(textArea.Document.GetLineByNumber(line));
+ return vl.GetVisualColumn(new Point(xPos, 0), true);
+ }
+
+ /// <inheritdoc/>
+ public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
+ {
+ TextLocation newStartLocation = textArea.Document.GetLocation(e.GetNewOffset(topLeftOffset, AnchorMovementType.AfterInsertion));
+ TextLocation newEndLocation = textArea.Document.GetLocation(e.GetNewOffset(bottomRightOffset, AnchorMovementType.BeforeInsertion));
+
+ return new RectangleSelection(textArea,
+ new TextViewPosition(newStartLocation, GetVisualColumnFromXPos(newStartLocation.Line, startXPos)),
+ new TextViewPosition(newEndLocation, GetVisualColumnFromXPos(newEndLocation.Line, endXPos)));
+ }
+
+ /// <inheritdoc/>
+ public override void ReplaceSelectionWithText(string newText)
+ {
+ if (newText == null)
+ throw new ArgumentNullException("newText");
+ using (textArea.Document.RunUpdate()) {
+ TextViewPosition start = new TextViewPosition(document.GetLocation(topLeftOffset), GetVisualColumnFromXPos(startLine, startXPos));
+ TextViewPosition end = new TextViewPosition(document.GetLocation(bottomRightOffset), GetVisualColumnFromXPos(endLine, endXPos));
+ int insertionLength;
+ int totalInsertionLength = 0;
+ int firstInsertionLength = 0;
+ int editOffset = Math.Min(topLeftOffset, bottomRightOffset);
+ TextViewPosition pos;
+ if (NewLineFinder.NextNewLine(newText, 0) == SimpleSegment.Invalid) {
+ // insert same text into every line
+ foreach (SelectionSegment lineSegment in this.Segments.Reverse()) {
+ ReplaceSingleLineText(textArea, lineSegment, newText, out insertionLength);
+ totalInsertionLength += insertionLength;
+ firstInsertionLength = insertionLength;
+ }
+
+ int newEndOffset = editOffset + totalInsertionLength;
+ pos = new TextViewPosition(document.GetLocation(editOffset + firstInsertionLength));
+
+ textArea.Selection = new RectangleSelection(textArea, pos, Math.Max(startLine, endLine), GetXPos(textArea, pos));
+ } else {
+ string[] lines = newText.Split(NewLineFinder.NewlineStrings, segments.Count, StringSplitOptions.None);
+ int line = Math.Min(startLine, endLine);
+ for (int i = lines.Length - 1; i >= 0; i--) {
+ ReplaceSingleLineText(textArea, segments[i], lines[i], out insertionLength);
+ firstInsertionLength = insertionLength;
+ }
+ pos = new TextViewPosition(document.GetLocation(editOffset + firstInsertionLength));
+ textArea.ClearSelection();
+ }
+ textArea.Caret.Position = textArea.TextView.GetPosition(new Point(GetXPos(textArea, pos), textArea.TextView.GetVisualTopByDocumentLine(Math.Max(startLine, endLine)))).GetValueOrDefault();
+ }
+ }
+
+ void ReplaceSingleLineText(TextArea textArea, SelectionSegment lineSegment, string newText, out int insertionLength)
+ {
+ if (lineSegment.Length == 0) {
+ if (newText.Length > 0 && textArea.ReadOnlySectionProvider.CanInsert(lineSegment.StartOffset)) {
+ newText = AddSpacesIfRequired(newText, new TextViewPosition(document.GetLocation(lineSegment.StartOffset), lineSegment.StartVisualColumn), new TextViewPosition(document.GetLocation(lineSegment.EndOffset), lineSegment.EndVisualColumn));
+ textArea.Document.Insert(lineSegment.StartOffset, newText);
+ }
+ } else {
+ ISegment[] segmentsToDelete = textArea.GetDeletableSegments(lineSegment);
+ for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
+ if (i == segmentsToDelete.Length - 1) {
+ if (segmentsToDelete[i].Offset == SurroundingSegment.Offset && segmentsToDelete[i].Length == SurroundingSegment.Length) {
+ newText = AddSpacesIfRequired(newText, new TextViewPosition(document.GetLocation(lineSegment.StartOffset), lineSegment.StartVisualColumn), new TextViewPosition(document.GetLocation(lineSegment.EndOffset), lineSegment.EndVisualColumn));
+ }
+ textArea.Document.Replace(segmentsToDelete[i], newText);
+ } else {
+ textArea.Document.Remove(segmentsToDelete[i]);
+ }
+ }
+ }
+ insertionLength = newText.Length;
+ }
+
+ /// <summary>
+ /// Performs a rectangular paste operation.
+ /// </summary>
+ public static bool PerformRectangularPaste(TextArea textArea, TextViewPosition startPosition, string text, bool selectInsertedText)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ if (text == null)
+ throw new ArgumentNullException("text");
+ int newLineCount = text.Count(c => c == '\n'); // TODO might not work in all cases, but single \r line endings are really rare today.
+ TextLocation endLocation = new TextLocation(startPosition.Line + newLineCount, startPosition.Column);
+ if (endLocation.Line <= textArea.Document.LineCount) {
+ int endOffset = textArea.Document.GetOffset(endLocation);
+ if (textArea.Selection.EnableVirtualSpace || textArea.Document.GetLocation(endOffset) == endLocation) {
+ RectangleSelection rsel = new RectangleSelection(textArea, startPosition, endLocation.Line, GetXPos(textArea, startPosition));
+ rsel.ReplaceSelectionWithText(text);
+ if (selectInsertedText && textArea.Selection is RectangleSelection) {
+ RectangleSelection sel = (RectangleSelection)textArea.Selection;
+ textArea.Selection = new RectangleSelection(textArea, startPosition, sel.endLine, sel.endXPos);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Gets the name of the entry in the DataObject that signals rectangle selections.
+ /// </summary>
+ public const string RectangularSelectionDataType = "AvalonEditRectangularSelection";
+
+ /// <inheritdoc/>
+ public override System.Windows.DataObject CreateDataObject(TextArea textArea)
+ {
+ var data = base.CreateDataObject(textArea);
+
+ MemoryStream isRectangle = new MemoryStream(1);
+ isRectangle.WriteByte(1);
+ data.SetData(RectangularSelectionDataType, isRectangle, false);
+ return data;
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ // It's possible that ToString() gets called on old (invalid) selections, e.g. for "change from... to..." debug message
+ // make sure we don't crash even when the desired locations don't exist anymore.
+ return string.Format("[RectangleSelection {0} {1} {2} to {3} {4} {5}]", startLine, topLeftOffset, startXPos, endLine, bottomRightOffset, endXPos);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Selection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Selection.cs
new file mode 100644
index 000000000..014fa8680
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/Selection.cs
@@ -0,0 +1,268 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Highlighting;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Base class for selections.
+ /// </summary>
+ public abstract class Selection
+ {
+ /// <summary>
+ /// Creates a new simple selection that selects the text from startOffset to endOffset.
+ /// </summary>
+ public static Selection Create(TextArea textArea, int startOffset, int endOffset)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ if (startOffset == endOffset)
+ return textArea.emptySelection;
+ else
+ return new SimpleSelection(textArea,
+ new TextViewPosition(textArea.Document.GetLocation(startOffset)),
+ new TextViewPosition(textArea.Document.GetLocation(endOffset)));
+ }
+
+ internal static Selection Create(TextArea textArea, TextViewPosition start, TextViewPosition end)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ if (textArea.Document.GetOffset(start.Location) == textArea.Document.GetOffset(end.Location) && start.VisualColumn == end.VisualColumn)
+ return textArea.emptySelection;
+ else
+ return new SimpleSelection(textArea, start, end);
+ }
+
+ /// <summary>
+ /// Creates a new simple selection that selects the text in the specified segment.
+ /// </summary>
+ public static Selection Create(TextArea textArea, ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+ return Create(textArea, segment.Offset, segment.EndOffset);
+ }
+
+ internal readonly TextArea textArea;
+
+ /// <summary>
+ /// Constructor for Selection.
+ /// </summary>
+ protected Selection(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ }
+
+ /// <summary>
+ /// Gets the start position of the selection.
+ /// </summary>
+ public abstract TextViewPosition StartPosition { get; }
+
+ /// <summary>
+ /// Gets the end position of the selection.
+ /// </summary>
+ public abstract TextViewPosition EndPosition { get; }
+
+ /// <summary>
+ /// Gets the selected text segments.
+ /// </summary>
+ public abstract IEnumerable<SelectionSegment> Segments { get; }
+
+ /// <summary>
+ /// Gets the smallest segment that contains all segments in this selection.
+ /// May return null if the selection is empty.
+ /// </summary>
+ public abstract ISegment SurroundingSegment { get; }
+
+ /// <summary>
+ /// Replaces the selection with the specified text.
+ /// </summary>
+ public abstract void ReplaceSelectionWithText(string newText);
+
+ internal string AddSpacesIfRequired(string newText, TextViewPosition start, TextViewPosition end)
+ {
+ if (EnableVirtualSpace && InsertVirtualSpaces(newText, start, end)) {
+ var line = textArea.Document.GetLineByNumber(start.Line);
+ string lineText = textArea.Document.GetText(line);
+ var vLine = textArea.TextView.GetOrConstructVisualLine(line);
+ int colDiff = start.VisualColumn - vLine.VisualLengthWithEndOfLineMarker;
+ if (colDiff > 0) {
+ string additionalSpaces = "";
+ if (!textArea.Options.ConvertTabsToSpaces && lineText.Trim('\t').Length == 0) {
+ int tabCount = (int)(colDiff / textArea.Options.IndentationSize);
+ additionalSpaces = new string('\t', tabCount);
+ colDiff -= tabCount * textArea.Options.IndentationSize;
+ }
+ additionalSpaces += new string(' ', colDiff);
+ return additionalSpaces + newText;
+ }
+ }
+ return newText;
+ }
+
+ bool InsertVirtualSpaces(string newText, TextViewPosition start, TextViewPosition end)
+ {
+ return (!string.IsNullOrEmpty(newText) || !(IsInVirtualSpace(start) && IsInVirtualSpace(end)))
+ && newText != "\r\n"
+ && newText != "\n"
+ && newText != "\r";
+ }
+
+ bool IsInVirtualSpace(TextViewPosition pos)
+ {
+ return pos.VisualColumn > textArea.TextView.GetOrConstructVisualLine(textArea.Document.GetLineByNumber(pos.Line)).VisualLength;
+ }
+
+ /// <summary>
+ /// Updates the selection when the document changes.
+ /// </summary>
+ public abstract Selection UpdateOnDocumentChange(DocumentChangeEventArgs e);
+
+ /// <summary>
+ /// Gets whether the selection is empty.
+ /// </summary>
+ public virtual bool IsEmpty {
+ get { return Length == 0; }
+ }
+
+ /// <summary>
+ /// Gets whether virtual space is enabled for this selection.
+ /// </summary>
+ public virtual bool EnableVirtualSpace {
+ get { return textArea.Options.EnableVirtualSpace; }
+ }
+
+ /// <summary>
+ /// Gets the selection length.
+ /// </summary>
+ public abstract int Length { get; }
+
+ /// <summary>
+ /// Returns a new selection with the changed end point.
+ /// </summary>
+ /// <exception cref="NotSupportedException">Cannot set endpoint for empty selection</exception>
+ public abstract Selection SetEndpoint(TextViewPosition endPosition);
+
+ /// <summary>
+ /// If this selection is empty, starts a new selection from <paramref name="startPosition"/> to
+ /// <paramref name="endPosition"/>, otherwise, changes the endpoint of this selection.
+ /// </summary>
+ public abstract Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition);
+
+ /// <summary>
+ /// Gets whether the selection is multi-line.
+ /// </summary>
+ public virtual bool IsMultiline {
+ get {
+ ISegment surroundingSegment = this.SurroundingSegment;
+ if (surroundingSegment == null)
+ return false;
+ int start = surroundingSegment.Offset;
+ int end = start + surroundingSegment.Length;
+ var document = textArea.Document;
+ if (document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return document.GetLineByOffset(start) != document.GetLineByOffset(end);
+ }
+ }
+
+ /// <summary>
+ /// Gets the selected text.
+ /// </summary>
+ public virtual string GetText()
+ {
+ var document = textArea.Document;
+ if (document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ StringBuilder b = null;
+ string text = null;
+ foreach (ISegment s in Segments) {
+ if (text != null) {
+ if (b == null)
+ b = new StringBuilder(text);
+ else
+ b.Append(text);
+ }
+ text = document.GetText(s);
+ }
+ if (b != null) {
+ if (text != null) b.Append(text);
+ return b.ToString();
+ } else {
+ return text ?? string.Empty;
+ }
+ }
+
+ /// <summary>
+ /// Creates a HTML fragment for the selected text.
+ /// </summary>
+ public string CreateHtmlFragment(HtmlOptions options)
+ {
+ if (options == null)
+ throw new ArgumentNullException("options");
+ IHighlighter highlighter = textArea.GetService(typeof(IHighlighter)) as IHighlighter;
+ StringBuilder html = new StringBuilder();
+ bool first = true;
+ foreach (ISegment selectedSegment in this.Segments) {
+ if (first)
+ first = false;
+ else
+ html.AppendLine("<br>");
+ html.Append(HtmlClipboard.CreateHtmlFragment(textArea.Document, highlighter, selectedSegment, options));
+ }
+ return html.ToString();
+ }
+
+ /// <inheritdoc/>
+ public abstract override bool Equals(object obj);
+
+ /// <inheritdoc/>
+ public abstract override int GetHashCode();
+
+ /// <summary>
+ /// Gets whether the specified offset is included in the selection.
+ /// </summary>
+ /// <returns>True, if the selection contains the offset (selection borders inclusive);
+ /// otherwise, false.</returns>
+ public virtual bool Contains(int offset)
+ {
+ if (this.IsEmpty)
+ return false;
+ if (this.SurroundingSegment.Contains(offset)) {
+ foreach (ISegment s in this.Segments) {
+ if (s.Contains(offset)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Creates a data object containing the selection's text.
+ /// </summary>
+ public virtual DataObject CreateDataObject(TextArea textArea)
+ {
+ string text = GetText();
+ // Ensure we use the appropriate newline sequence for the OS
+ DataObject data = new DataObject(TextUtilities.NormalizeNewLines(text, Environment.NewLine));
+ // we cannot use DataObject.SetText - then we cannot drag to SciTe
+ // (but dragging to Word works in both cases)
+
+ // Also copy text in HTML format to clipboard - good for pasting text into Word
+ // or to the SharpDevelop forums.
+ HtmlClipboard.SetHtml(data, CreateHtmlFragment(new HtmlOptions(textArea.Options)));
+ return data;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionColorizer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionColorizer.cs
new file mode 100644
index 000000000..857c4c060
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionColorizer.cs
@@ -0,0 +1,58 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ sealed class SelectionColorizer : ColorizingTransformer
+ {
+ TextArea textArea;
+
+ public SelectionColorizer(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ }
+
+ protected override void Colorize(ITextRunConstructionContext context)
+ {
+ // if SelectionForeground is null, keep the existing foreground color
+ if (textArea.SelectionForeground == null)
+ return;
+
+ int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
+ int lineEndOffset = context.VisualLine.LastDocumentLine.Offset + context.VisualLine.LastDocumentLine.TotalLength;
+
+ foreach (SelectionSegment segment in textArea.Selection.Segments) {
+ int segmentStart = segment.StartOffset;
+ int segmentEnd = segment.EndOffset;
+ if (segmentEnd <= lineStartOffset)
+ continue;
+ if (segmentStart >= lineEndOffset)
+ continue;
+ int startColumn;
+ if (segmentStart < lineStartOffset)
+ startColumn = 0;
+ else
+ startColumn = context.VisualLine.ValidateVisualColumn(segment.StartOffset, segment.StartVisualColumn, textArea.Selection.EnableVirtualSpace);
+
+ int endColumn;
+ if (segmentEnd > lineEndOffset)
+ endColumn = textArea.Selection.EnableVirtualSpace ? int.MaxValue : context.VisualLine.VisualLengthWithEndOfLineMarker;
+ else
+ endColumn = context.VisualLine.ValidateVisualColumn(segment.EndOffset, segment.EndVisualColumn, textArea.Selection.EnableVirtualSpace);
+
+ ChangeVisualElements(
+ startColumn, endColumn,
+ element => {
+ element.TextRunProperties.SetForegroundBrush(textArea.SelectionForeground);
+ });
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionLayer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionLayer.cs
new file mode 100644
index 000000000..28631affe
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionLayer.cs
@@ -0,0 +1,53 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ sealed class SelectionLayer : Layer, IWeakEventListener
+ {
+ readonly TextArea textArea;
+
+ public SelectionLayer(TextArea textArea) : base(textArea.TextView, KnownLayer.Selection)
+ {
+ this.IsHitTestVisible = false;
+
+ this.textArea = textArea;
+ TextViewWeakEventManager.VisualLinesChanged.AddListener(textView, this);
+ TextViewWeakEventManager.ScrollOffsetChanged.AddListener(textView, this);
+ }
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(TextViewWeakEventManager.VisualLinesChanged)
+ || managerType == typeof(TextViewWeakEventManager.ScrollOffsetChanged))
+ {
+ InvalidateVisual();
+ return true;
+ }
+ return false;
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ base.OnRender(drawingContext);
+
+ BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
+ geoBuilder.AlignToMiddleOfPixels = true;
+ geoBuilder.ExtendToFullWidthAtLineEnd = textArea.Selection.EnableVirtualSpace;
+ geoBuilder.CornerRadius = textArea.SelectionCornerRadius;
+ foreach (var segment in textArea.Selection.Segments) {
+ geoBuilder.AddSegment(textView, segment);
+ }
+ Geometry geometry = geoBuilder.CreateGeometry();
+ if (geometry != null) {
+ drawingContext.DrawGeometry(textArea.SelectionBrush, textArea.SelectionBorder, geometry);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs
new file mode 100644
index 000000000..3c5ec51dc
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionMouseHandler.cs
@@ -0,0 +1,631 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media.TextFormatting;
+using System.Windows.Threading;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Handles selection of text using the mouse.
+ /// </summary>
+ sealed class SelectionMouseHandler : ITextAreaInputHandler
+ {
+ #region enum SelectionMode
+ enum SelectionMode
+ {
+ /// <summary>
+ /// no selection (no mouse button down)
+ /// </summary>
+ None,
+ /// <summary>
+ /// left mouse button down on selection, might be normal click
+ /// or might be drag'n'drop
+ /// </summary>
+ PossibleDragStart,
+ /// <summary>
+ /// dragging text
+ /// </summary>
+ Drag,
+ /// <summary>
+ /// normal selection (click+drag)
+ /// </summary>
+ Normal,
+ /// <summary>
+ /// whole-word selection (double click+drag or ctrl+click+drag)
+ /// </summary>
+ WholeWord,
+ /// <summary>
+ /// whole-line selection (triple click+drag)
+ /// </summary>
+ WholeLine,
+ /// <summary>
+ /// rectangular selection (alt+click+drag)
+ /// </summary>
+ Rectangular
+ }
+ #endregion
+
+ readonly TextArea textArea;
+
+ SelectionMode mode;
+ AnchorSegment startWord;
+ Point possibleDragStartMousePos;
+
+ #region Constructor + Attach + Detach
+ public SelectionMouseHandler(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ }
+
+ public TextArea TextArea {
+ get { return textArea; }
+ }
+
+ public void Attach()
+ {
+ textArea.MouseLeftButtonDown += textArea_MouseLeftButtonDown;
+ textArea.MouseMove += textArea_MouseMove;
+ textArea.MouseLeftButtonUp += textArea_MouseLeftButtonUp;
+ textArea.QueryCursor += textArea_QueryCursor;
+ textArea.OptionChanged += textArea_OptionChanged;
+
+ enableTextDragDrop = textArea.Options.EnableTextDragDrop;
+ if (enableTextDragDrop) {
+ AttachDragDrop();
+ }
+ }
+
+ public void Detach()
+ {
+ mode = SelectionMode.None;
+ textArea.MouseLeftButtonDown -= textArea_MouseLeftButtonDown;
+ textArea.MouseMove -= textArea_MouseMove;
+ textArea.MouseLeftButtonUp -= textArea_MouseLeftButtonUp;
+ textArea.QueryCursor -= textArea_QueryCursor;
+ textArea.OptionChanged -= textArea_OptionChanged;
+ if (enableTextDragDrop) {
+ DetachDragDrop();
+ }
+ }
+
+ void AttachDragDrop()
+ {
+ textArea.AllowDrop = true;
+ textArea.GiveFeedback += textArea_GiveFeedback;
+ textArea.QueryContinueDrag += textArea_QueryContinueDrag;
+ textArea.DragEnter += textArea_DragEnter;
+ textArea.DragOver += textArea_DragOver;
+ textArea.DragLeave += textArea_DragLeave;
+ textArea.Drop += textArea_Drop;
+ }
+
+ void DetachDragDrop()
+ {
+ textArea.AllowDrop = false;
+ textArea.GiveFeedback -= textArea_GiveFeedback;
+ textArea.QueryContinueDrag -= textArea_QueryContinueDrag;
+ textArea.DragEnter -= textArea_DragEnter;
+ textArea.DragOver -= textArea_DragOver;
+ textArea.DragLeave -= textArea_DragLeave;
+ textArea.Drop -= textArea_Drop;
+ }
+
+ bool enableTextDragDrop;
+
+ void textArea_OptionChanged(object sender, PropertyChangedEventArgs e)
+ {
+ bool newEnableTextDragDrop = textArea.Options.EnableTextDragDrop;
+ if (newEnableTextDragDrop != enableTextDragDrop) {
+ enableTextDragDrop = newEnableTextDragDrop;
+ if (newEnableTextDragDrop)
+ AttachDragDrop();
+ else
+ DetachDragDrop();
+ }
+ }
+ #endregion
+
+ #region Dropping text
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ void textArea_DragEnter(object sender, DragEventArgs e)
+ {
+ try {
+ e.Effects = GetEffect(e);
+ textArea.Caret.Show();
+ } catch (Exception ex) {
+ OnDragException(ex);
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ void textArea_DragOver(object sender, DragEventArgs e)
+ {
+ try {
+ e.Effects = GetEffect(e);
+ } catch (Exception ex) {
+ OnDragException(ex);
+ }
+ }
+
+ DragDropEffects GetEffect(DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(DataFormats.UnicodeText, true)) {
+ e.Handled = true;
+ int visualColumn;
+ int offset = GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn);
+ if (offset >= 0) {
+ textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn);
+ textArea.Caret.DesiredXPos = double.NaN;
+ if (textArea.ReadOnlySectionProvider.CanInsert(offset)) {
+ if ((e.AllowedEffects & DragDropEffects.Move) == DragDropEffects.Move
+ && (e.KeyStates & DragDropKeyStates.ControlKey) != DragDropKeyStates.ControlKey)
+ {
+ return DragDropEffects.Move;
+ } else {
+ return e.AllowedEffects & DragDropEffects.Copy;
+ }
+ }
+ }
+ }
+ return DragDropEffects.None;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ void textArea_DragLeave(object sender, DragEventArgs e)
+ {
+ try {
+ e.Handled = true;
+ if (!textArea.IsKeyboardFocusWithin)
+ textArea.Caret.Hide();
+ } catch (Exception ex) {
+ OnDragException(ex);
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ void textArea_Drop(object sender, DragEventArgs e)
+ {
+ try {
+ DragDropEffects effect = GetEffect(e);
+ e.Effects = effect;
+ if (effect != DragDropEffects.None) {
+ string text = e.Data.GetData(DataFormats.UnicodeText, true) as string;
+ if (text != null) {
+ int start = textArea.Caret.Offset;
+ if (mode == SelectionMode.Drag && textArea.Selection.Contains(start)) {
+ Debug.WriteLine("Drop: did not drop: drop target is inside selection");
+ e.Effects = DragDropEffects.None;
+ } else {
+ Debug.WriteLine("Drop: insert at " + start);
+
+ bool rectangular = e.Data.GetDataPresent(RectangleSelection.RectangularSelectionDataType);
+
+ string newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
+ text = TextUtilities.NormalizeNewLines(text, newLine);
+
+ // Mark the undo group with the currentDragDescriptor, if the drag
+ // is originating from the same control. This allows combining
+ // the undo groups when text is moved.
+ textArea.Document.UndoStack.StartUndoGroup(this.currentDragDescriptor);
+ try {
+ if (rectangular && RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, true)) {
+
+ } else {
+ textArea.Document.Insert(start, text);
+ textArea.Selection = Selection.Create(textArea, start, start + text.Length);
+ }
+ } finally {
+ textArea.Document.UndoStack.EndUndoGroup();
+ }
+ }
+ e.Handled = true;
+ }
+ }
+ } catch (Exception ex) {
+ OnDragException(ex);
+ }
+ }
+
+ void OnDragException(Exception ex)
+ {
+ // WPF swallows exceptions during drag'n'drop or reports them incorrectly, so
+ // we re-throw them later to allow the application's unhandled exception handler
+ // to catch them
+ textArea.Dispatcher.BeginInvoke(
+ DispatcherPriority.Normal,
+ new Action(delegate {
+ throw new DragDropException("Exception during drag'n'drop", ex);
+ }));
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ void textArea_GiveFeedback(object sender, GiveFeedbackEventArgs e)
+ {
+ try {
+ e.UseDefaultCursors = true;
+ e.Handled = true;
+ } catch (Exception ex) {
+ OnDragException(ex);
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ void textArea_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
+ {
+ try {
+ if (e.EscapePressed) {
+ e.Action = DragAction.Cancel;
+ } else if ((e.KeyStates & DragDropKeyStates.LeftMouseButton) != DragDropKeyStates.LeftMouseButton) {
+ e.Action = DragAction.Drop;
+ } else {
+ e.Action = DragAction.Continue;
+ }
+ e.Handled = true;
+ } catch (Exception ex) {
+ OnDragException(ex);
+ }
+ }
+ #endregion
+
+ #region Start Drag
+ object currentDragDescriptor;
+
+ void StartDrag()
+ {
+ // prevent nested StartDrag calls
+ mode = SelectionMode.Drag;
+
+ // mouse capture and Drag'n'Drop doesn't mix
+ textArea.ReleaseMouseCapture();
+
+ DataObject dataObject = textArea.Selection.CreateDataObject(textArea);
+
+ DragDropEffects allowedEffects = DragDropEffects.All;
+ var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();
+ foreach (ISegment s in deleteOnMove) {
+ ISegment[] result = textArea.GetDeletableSegments(s);
+ if (result.Length != 1 || result[0].Offset != s.Offset || result[0].EndOffset != s.EndOffset) {
+ allowedEffects &= ~DragDropEffects.Move;
+ }
+ }
+
+ object dragDescriptor = new object();
+ this.currentDragDescriptor = dragDescriptor;
+
+ DragDropEffects resultEffect;
+ using (textArea.AllowCaretOutsideSelection()) {
+ var oldCaretPosition = textArea.Caret.Position;
+ try {
+ Debug.WriteLine("DoDragDrop with allowedEffects=" + allowedEffects);
+ resultEffect = DragDrop.DoDragDrop(textArea, dataObject, allowedEffects);
+ Debug.WriteLine("DoDragDrop done, resultEffect=" + resultEffect);
+ } catch (COMException ex) {
+ // ignore COM errors - don't crash on badly implemented drop targets
+ Debug.WriteLine("DoDragDrop failed: " + ex.ToString());
+ return;
+ }
+ if (resultEffect == DragDropEffects.None) {
+ // reset caret if drag was aborted
+ textArea.Caret.Position = oldCaretPosition;
+ }
+ }
+
+ this.currentDragDescriptor = null;
+
+ if (deleteOnMove != null && resultEffect == DragDropEffects.Move && (allowedEffects & DragDropEffects.Move) == DragDropEffects.Move) {
+ bool draggedInsideSingleDocument = (dragDescriptor == textArea.Document.UndoStack.LastGroupDescriptor);
+ if (draggedInsideSingleDocument)
+ textArea.Document.UndoStack.StartContinuedUndoGroup(null);
+ textArea.Document.BeginUpdate();
+ try {
+ foreach (ISegment s in deleteOnMove) {
+ textArea.Document.Remove(s.Offset, s.Length);
+ }
+ } finally {
+ textArea.Document.EndUpdate();
+ if (draggedInsideSingleDocument)
+ textArea.Document.UndoStack.EndUndoGroup();
+ }
+ }
+ }
+ #endregion
+
+ #region QueryCursor
+ // provide the IBeam Cursor for the text area
+ void textArea_QueryCursor(object sender, QueryCursorEventArgs e)
+ {
+ if (!e.Handled) {
+ if (mode != SelectionMode.None || !enableTextDragDrop) {
+ e.Cursor = Cursors.IBeam;
+ e.Handled = true;
+ } else if (textArea.TextView.VisualLinesValid) {
+ // Only query the cursor if the visual lines are valid.
+ // If they are invalid, the cursor will get re-queried when the visual lines
+ // get refreshed.
+ Point p = e.GetPosition(textArea.TextView);
+ if (p.X >= 0 && p.Y >= 0 && p.X <= textArea.TextView.ActualWidth && p.Y <= textArea.TextView.ActualHeight) {
+ int visualColumn;
+ int offset = GetOffsetFromMousePosition(e, out visualColumn);
+ if (textArea.Selection.Contains(offset))
+ e.Cursor = Cursors.Arrow;
+ else
+ e.Cursor = Cursors.IBeam;
+ e.Handled = true;
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region LeftButtonDown
+ void textArea_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ mode = SelectionMode.None;
+ if (!e.Handled && e.ChangedButton == MouseButton.Left) {
+ ModifierKeys modifiers = Keyboard.Modifiers;
+ bool shift = (modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
+ if (enableTextDragDrop && e.ClickCount == 1 && !shift) {
+ int visualColumn;
+ int offset = GetOffsetFromMousePosition(e, out visualColumn);
+ if (textArea.Selection.Contains(offset)) {
+ if (textArea.CaptureMouse()) {
+ mode = SelectionMode.PossibleDragStart;
+ possibleDragStartMousePos = e.GetPosition(textArea);
+ }
+ e.Handled = true;
+ return;
+ }
+ }
+
+ var oldPosition = textArea.Caret.Position;
+ SetCaretOffsetToMousePosition(e);
+
+
+ if (!shift) {
+ textArea.ClearSelection();
+ }
+ if (textArea.CaptureMouse()) {
+ if ((modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && textArea.Options.EnableRectangularSelection) {
+ mode = SelectionMode.Rectangular;
+ if (shift && textArea.Selection is RectangleSelection) {
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ }
+ } else if (e.ClickCount == 1 && ((modifiers & ModifierKeys.Control) == 0)) {
+ mode = SelectionMode.Normal;
+ if (shift && !(textArea.Selection is RectangleSelection)) {
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ }
+ } else {
+ SimpleSegment startWord;
+ if (e.ClickCount == 3) {
+ mode = SelectionMode.WholeLine;
+ startWord = GetLineAtMousePosition(e);
+ } else {
+ mode = SelectionMode.WholeWord;
+ startWord = GetWordAtMousePosition(e);
+ }
+ if (startWord == SimpleSegment.Invalid) {
+ mode = SelectionMode.None;
+ textArea.ReleaseMouseCapture();
+ return;
+ }
+ if (shift && !textArea.Selection.IsEmpty) {
+ if (startWord.Offset < textArea.Selection.SurroundingSegment.Offset) {
+ textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.Offset)));
+ } else if (startWord.EndOffset > textArea.Selection.SurroundingSegment.EndOffset) {
+ textArea.Selection = textArea.Selection.SetEndpoint(new TextViewPosition(textArea.Document.GetLocation(startWord.EndOffset)));
+ }
+ this.startWord = new AnchorSegment(textArea.Document, textArea.Selection.SurroundingSegment);
+ } else {
+ textArea.Selection = Selection.Create(textArea, startWord.Offset, startWord.EndOffset);
+ this.startWord = new AnchorSegment(textArea.Document, startWord.Offset, startWord.Length);
+ }
+ }
+ }
+ }
+ e.Handled = true;
+ }
+ #endregion
+
+ #region Mouse Position <-> Text coordinates
+ SimpleSegment GetWordAtMousePosition(MouseEventArgs e)
+ {
+ TextView textView = textArea.TextView;
+ if (textView == null) return SimpleSegment.Invalid;
+ Point pos = e.GetPosition(textView);
+ if (pos.Y < 0)
+ pos.Y = 0;
+ if (pos.Y > textView.ActualHeight)
+ pos.Y = textView.ActualHeight;
+ pos += textView.ScrollOffset;
+ VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
+ if (line != null) {
+ int visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace);
+ int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace);
+ if (wordStartVC == -1)
+ wordStartVC = 0;
+ int wordEndVC = line.GetNextCaretPosition(wordStartVC, LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace);
+ if (wordEndVC == -1)
+ wordEndVC = line.VisualLength;
+ int relOffset = line.FirstDocumentLine.Offset;
+ int wordStartOffset = line.GetRelativeOffset(wordStartVC) + relOffset;
+ int wordEndOffset = line.GetRelativeOffset(wordEndVC) + relOffset;
+ return new SimpleSegment(wordStartOffset, wordEndOffset - wordStartOffset);
+ } else {
+ return SimpleSegment.Invalid;
+ }
+ }
+
+ SimpleSegment GetLineAtMousePosition(MouseEventArgs e)
+ {
+ TextView textView = textArea.TextView;
+ if (textView == null) return SimpleSegment.Invalid;
+ Point pos = e.GetPosition(textView);
+ if (pos.Y < 0)
+ pos.Y = 0;
+ if (pos.Y > textView.ActualHeight)
+ pos.Y = textView.ActualHeight;
+ pos += textView.ScrollOffset;
+ VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
+ if (line != null) {
+ return new SimpleSegment(line.StartOffset, line.LastDocumentLine.EndOffset - line.StartOffset);
+ } else {
+ return SimpleSegment.Invalid;
+ }
+ }
+
+ int GetOffsetFromMousePosition(MouseEventArgs e, out int visualColumn)
+ {
+ return GetOffsetFromMousePosition(e.GetPosition(textArea.TextView), out visualColumn);
+ }
+
+ int GetOffsetFromMousePosition(Point positionRelativeToTextView, out int visualColumn)
+ {
+ visualColumn = 0;
+ TextView textView = textArea.TextView;
+ Point pos = positionRelativeToTextView;
+ if (pos.Y < 0)
+ pos.Y = 0;
+ if (pos.Y > textView.ActualHeight)
+ pos.Y = textView.ActualHeight;
+ pos += textView.ScrollOffset;
+ if (pos.Y > textView.DocumentHeight)
+ pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
+ VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
+ if (line != null) {
+ visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace);
+ return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
+ }
+ return -1;
+ }
+
+ int GetOffsetFromMousePositionFirstTextLineOnly(Point positionRelativeToTextView, out int visualColumn)
+ {
+ visualColumn = 0;
+ TextView textView = textArea.TextView;
+ Point pos = positionRelativeToTextView;
+ if (pos.Y < 0)
+ pos.Y = 0;
+ if (pos.Y > textView.ActualHeight)
+ pos.Y = textView.ActualHeight;
+ pos += textView.ScrollOffset;
+ if (pos.Y > textView.DocumentHeight)
+ pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
+ VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
+ if (line != null) {
+ visualColumn = line.GetVisualColumn(line.TextLines.First(), pos.X, textArea.Selection.EnableVirtualSpace);
+ return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
+ }
+ return -1;
+ }
+ #endregion
+
+ #region MouseMove
+ void textArea_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (e.Handled)
+ return;
+ if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) {
+ e.Handled = true;
+ if (textArea.TextView.VisualLinesValid) {
+ // If the visual lines are not valid, don't extend the selection.
+ // Extending the selection forces a VisualLine refresh, and it is sufficient
+ // to do that on MouseUp, we don't have to do it every MouseMove.
+ ExtendSelectionToMouse(e);
+ }
+ } else if (mode == SelectionMode.PossibleDragStart) {
+ e.Handled = true;
+ Vector mouseMovement = e.GetPosition(textArea) - possibleDragStartMousePos;
+ if (Math.Abs(mouseMovement.X) > SystemParameters.MinimumHorizontalDragDistance
+ || Math.Abs(mouseMovement.Y) > SystemParameters.MinimumVerticalDragDistance)
+ {
+ StartDrag();
+ }
+ }
+ }
+ #endregion
+
+ #region ExtendSelection
+ void SetCaretOffsetToMousePosition(MouseEventArgs e)
+ {
+ SetCaretOffsetToMousePosition(e, null);
+ }
+
+ void SetCaretOffsetToMousePosition(MouseEventArgs e, ISegment allowedSegment)
+ {
+ int visualColumn;
+ int offset;
+ if (mode == SelectionMode.Rectangular)
+ offset = GetOffsetFromMousePositionFirstTextLineOnly(e.GetPosition(textArea.TextView), out visualColumn);
+ else
+ offset = GetOffsetFromMousePosition(e, out visualColumn);
+ if (allowedSegment != null) {
+ offset = offset.CoerceValue(allowedSegment.Offset, allowedSegment.EndOffset);
+ }
+ if (offset >= 0) {
+ textArea.Caret.Position = new TextViewPosition(textArea.Document.GetLocation(offset), visualColumn);
+ textArea.Caret.DesiredXPos = double.NaN;
+ }
+ }
+
+ void ExtendSelectionToMouse(MouseEventArgs e)
+ {
+ TextViewPosition oldPosition = textArea.Caret.Position;
+ if (mode == SelectionMode.Normal || mode == SelectionMode.Rectangular) {
+ SetCaretOffsetToMousePosition(e);
+ if (mode == SelectionMode.Normal && textArea.Selection is RectangleSelection)
+ textArea.Selection = new SimpleSelection(textArea, oldPosition, textArea.Caret.Position);
+ else if (mode == SelectionMode.Rectangular && !(textArea.Selection is RectangleSelection))
+ textArea.Selection = new RectangleSelection(textArea, oldPosition, textArea.Caret.Position);
+ else
+ textArea.Selection = textArea.Selection.StartSelectionOrSetEndpoint(oldPosition, textArea.Caret.Position);
+ } else if (mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine) {
+ var newWord = (mode == SelectionMode.WholeLine) ? GetLineAtMousePosition(e) : GetWordAtMousePosition(e);
+ if (newWord != SimpleSegment.Invalid) {
+ textArea.Selection = Selection.Create(textArea,
+ Math.Min(newWord.Offset, startWord.Offset),
+ Math.Max(newWord.EndOffset, startWord.EndOffset));
+ // Set caret offset, but limit the caret to stay inside the selection.
+ // in whole-word selection, it's otherwise possible that we get the caret outside the
+ // selection - but the TextArea doesn't like that and will reset the selection, causing
+ // flickering.
+ SetCaretOffsetToMousePosition(e, textArea.Selection.SurroundingSegment);
+ }
+ }
+ textArea.Caret.BringCaretToView(5.0);
+ }
+ #endregion
+
+ #region MouseLeftButtonUp
+ void textArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (mode == SelectionMode.None || e.Handled)
+ return;
+ e.Handled = true;
+ if (mode == SelectionMode.PossibleDragStart) {
+ // -> this was not a drag start (mouse didn't move after mousedown)
+ SetCaretOffsetToMousePosition(e);
+ textArea.ClearSelection();
+ } else if (mode == SelectionMode.Normal || mode == SelectionMode.WholeWord || mode == SelectionMode.WholeLine || mode == SelectionMode.Rectangular) {
+ ExtendSelectionToMouse(e);
+ }
+ mode = SelectionMode.None;
+ textArea.ReleaseMouseCapture();
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionSegment.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionSegment.cs
new file mode 100644
index 000000000..46c560b34
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SelectionSegment.cs
@@ -0,0 +1,89 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Represents a selected segment.
+ /// </summary>
+ public class SelectionSegment : ISegment
+ {
+ readonly int startOffset, endOffset;
+ readonly int startVC, endVC;
+
+ /// <summary>
+ /// Creates a SelectionSegment from two offsets.
+ /// </summary>
+ public SelectionSegment(int startOffset, int endOffset)
+ {
+ this.startOffset = Math.Min(startOffset, endOffset);
+ this.endOffset = Math.Max(startOffset, endOffset);
+ this.startVC = this.endVC = -1;
+ }
+
+ /// <summary>
+ /// Creates a SelectionSegment from two offsets and visual columns.
+ /// </summary>
+ public SelectionSegment(int startOffset, int startVC, int endOffset, int endVC)
+ {
+ if (startOffset < endOffset || (startOffset == endOffset && startVC <= endVC)) {
+ this.startOffset = startOffset;
+ this.startVC = startVC;
+ this.endOffset = endOffset;
+ this.endVC = endVC;
+ } else {
+ this.startOffset = endOffset;
+ this.startVC = endVC;
+ this.endOffset = startOffset;
+ this.endVC = startVC;
+ }
+ }
+
+ /// <summary>
+ /// Gets the start offset.
+ /// </summary>
+ public int StartOffset {
+ get { return startOffset; }
+ }
+
+ /// <summary>
+ /// Gets the end offset.
+ /// </summary>
+ public int EndOffset {
+ get { return endOffset; }
+ }
+
+ /// <summary>
+ /// Gets the start visual column.
+ /// </summary>
+ public int StartVisualColumn {
+ get { return startVC; }
+ }
+
+ /// <summary>
+ /// Gets the end visual column.
+ /// </summary>
+ public int EndVisualColumn {
+ get { return endVC; }
+ }
+
+ /// <inheritdoc/>
+ int ISegment.Offset {
+ get { return startOffset; }
+ }
+
+ /// <inheritdoc/>
+ public int Length {
+ get { return endOffset - startOffset; }
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format("[SelectionSegment StartOffset={0}, EndOffset={1}, StartVC={2}, EndVC={3}]", startOffset, endOffset, startVC, endVC);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SimpleSelection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SimpleSelection.cs
new file mode 100644
index 000000000..132b09e20
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/SimpleSelection.cs
@@ -0,0 +1,144 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// A simple selection.
+ /// </summary>
+ sealed class SimpleSelection : Selection
+ {
+ readonly TextViewPosition start, end;
+ readonly int startOffset, endOffset;
+
+ /// <summary>
+ /// Creates a new SimpleSelection instance.
+ /// </summary>
+ internal SimpleSelection(TextArea textArea, TextViewPosition start, TextViewPosition end)
+ : base(textArea)
+ {
+ this.start = start;
+ this.end = end;
+ this.startOffset = textArea.Document.GetOffset(start.Location);
+ this.endOffset = textArea.Document.GetOffset(end.Location);
+ }
+
+ /// <inheritdoc/>
+ public override IEnumerable<SelectionSegment> Segments {
+ get {
+ return ExtensionMethods.Sequence<SelectionSegment>(new SelectionSegment(startOffset, start.VisualColumn, endOffset, end.VisualColumn));
+ }
+ }
+
+ /// <inheritdoc/>
+ public override ISegment SurroundingSegment {
+ get {
+ return new SelectionSegment(startOffset, endOffset);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override void ReplaceSelectionWithText(string newText)
+ {
+ if (newText == null)
+ throw new ArgumentNullException("newText");
+ using (textArea.Document.RunUpdate()) {
+ ISegment[] segmentsToDelete = textArea.GetDeletableSegments(this.SurroundingSegment);
+ for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
+ if (i == segmentsToDelete.Length - 1) {
+ if (segmentsToDelete[i].Offset == SurroundingSegment.Offset && segmentsToDelete[i].Length == SurroundingSegment.Length) {
+ newText = AddSpacesIfRequired(newText, start, end);
+ }
+ int vc = textArea.Caret.VisualColumn;
+ textArea.Caret.Offset = segmentsToDelete[i].EndOffset;
+ if (string.IsNullOrEmpty(newText))
+ textArea.Caret.VisualColumn = vc;
+ textArea.Document.Replace(segmentsToDelete[i], newText);
+ } else {
+ textArea.Document.Remove(segmentsToDelete[i]);
+ }
+ }
+ if (segmentsToDelete.Length != 0) {
+ textArea.ClearSelection();
+ }
+ }
+ }
+
+ public override TextViewPosition StartPosition {
+ get { return start; }
+ }
+
+ public override TextViewPosition EndPosition {
+ get { return end; }
+ }
+
+ /// <inheritdoc/>
+ public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
+ {
+ if (e == null)
+ throw new ArgumentNullException("e");
+ return Selection.Create(
+ textArea,
+ new TextViewPosition(textArea.Document.GetLocation(e.GetNewOffset(startOffset, AnchorMovementType.Default)), start.VisualColumn),
+ new TextViewPosition(textArea.Document.GetLocation(e.GetNewOffset(endOffset, AnchorMovementType.Default)), end.VisualColumn)
+ );
+ }
+
+ /// <inheritdoc/>
+ public override bool IsEmpty {
+ get { return startOffset == endOffset; }
+ }
+
+ /// <inheritdoc/>
+ public override int Length {
+ get {
+ return Math.Abs(endOffset - startOffset);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override Selection SetEndpoint(TextViewPosition endPosition)
+ {
+ return Create(textArea, start, endPosition);
+ }
+
+ public override Selection StartSelectionOrSetEndpoint(TextViewPosition startPosition, TextViewPosition endPosition)
+ {
+ var document = textArea.Document;
+ if (document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return Create(textArea, start, endPosition);
+ }
+
+ /// <inheritdoc/>
+ public override int GetHashCode()
+ {
+ unchecked {
+ return startOffset * 27811 + endOffset + textArea.GetHashCode();
+ }
+ }
+
+ /// <inheritdoc/>
+ public override bool Equals(object obj)
+ {
+ SimpleSelection other = obj as SimpleSelection;
+ if (other == null) return false;
+ return this.start.Equals(other.start) && this.end.Equals(other.end)
+ && this.startOffset == other.startOffset && this.endOffset == other.endOffset
+ && this.textArea == other.textArea;
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[SimpleSelection Start=" + start + " End=" + end + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextArea.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextArea.cs
new file mode 100644
index 000000000..393a3e29c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextArea.cs
@@ -0,0 +1,1048 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Indentation;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Control that wraps a TextView and adds support for user input and the caret.
+ /// </summary>
+ public class TextArea : Control, IScrollInfo, IWeakEventListener, ITextEditorComponent, IServiceProvider
+ {
+ internal readonly ImeSupport ime;
+
+ #region Constructor
+ static TextArea()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(TextArea),
+ new FrameworkPropertyMetadata(typeof(TextArea)));
+ KeyboardNavigation.IsTabStopProperty.OverrideMetadata(
+ typeof(TextArea), new FrameworkPropertyMetadata(Boxes.True));
+ KeyboardNavigation.TabNavigationProperty.OverrideMetadata(
+ typeof(TextArea), new FrameworkPropertyMetadata(KeyboardNavigationMode.None));
+ FocusableProperty.OverrideMetadata(
+ typeof(TextArea), new FrameworkPropertyMetadata(Boxes.True));
+ }
+
+ /// <summary>
+ /// Creates a new TextArea instance.
+ /// </summary>
+ public TextArea() : this(new TextView())
+ {
+ }
+
+ /// <summary>
+ /// Creates a new TextArea instance.
+ /// </summary>
+ protected TextArea(TextView textView)
+ {
+ if (textView == null)
+ throw new ArgumentNullException("textView");
+ this.textView = textView;
+ this.Options = textView.Options;
+
+ selection = emptySelection = new EmptySelection(this);
+
+ textView.Services.AddService(typeof(TextArea), this);
+
+ textView.LineTransformers.Add(new SelectionColorizer(this));
+ textView.InsertLayer(new SelectionLayer(this), KnownLayer.Selection, LayerInsertionPosition.Replace);
+
+ caret = new Caret(this);
+ caret.PositionChanged += (sender, e) => RequestSelectionValidation();
+ ime = new ImeSupport(this);
+
+ leftMargins.CollectionChanged += leftMargins_CollectionChanged;
+
+ this.DefaultInputHandler = new TextAreaDefaultInputHandler(this);
+ this.ActiveInputHandler = this.DefaultInputHandler;
+ }
+ #endregion
+
+ #region InputHandler management
+ /// <summary>
+ /// Gets the default input handler.
+ /// </summary>
+ /// <remarks><inheritdoc cref="ITextAreaInputHandler"/></remarks>
+ public TextAreaDefaultInputHandler DefaultInputHandler { get; private set; }
+
+ ITextAreaInputHandler activeInputHandler;
+ bool isChangingInputHandler;
+
+ /// <summary>
+ /// Gets/Sets the active input handler.
+ /// This property does not return currently active stacked input handlers. Setting this property detached all stacked input handlers.
+ /// </summary>
+ /// <remarks><inheritdoc cref="ITextAreaInputHandler"/></remarks>
+ public ITextAreaInputHandler ActiveInputHandler {
+ get { return activeInputHandler; }
+ set {
+ if (value != null && value.TextArea != this)
+ throw new ArgumentException("The input handler was created for a different text area than this one.");
+ if (isChangingInputHandler)
+ throw new InvalidOperationException("Cannot set ActiveInputHandler recursively");
+ if (activeInputHandler != value) {
+ isChangingInputHandler = true;
+ try {
+ // pop the whole stack
+ PopStackedInputHandler(stackedInputHandlers.LastOrDefault());
+ Debug.Assert(stackedInputHandlers.IsEmpty);
+
+ if (activeInputHandler != null)
+ activeInputHandler.Detach();
+ activeInputHandler = value;
+ if (value != null)
+ value.Attach();
+ } finally {
+ isChangingInputHandler = false;
+ }
+ if (ActiveInputHandlerChanged != null)
+ ActiveInputHandlerChanged(this, EventArgs.Empty);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Occurs when the ActiveInputHandler property changes.
+ /// </summary>
+ public event EventHandler ActiveInputHandlerChanged;
+
+ ImmutableStack<TextAreaStackedInputHandler> stackedInputHandlers = ImmutableStack<TextAreaStackedInputHandler>.Empty;
+
+ /// <summary>
+ /// Gets the list of currently active stacked input handlers.
+ /// </summary>
+ /// <remarks><inheritdoc cref="ITextAreaInputHandler"/></remarks>
+ public ImmutableStack<TextAreaStackedInputHandler> StackedInputHandlers {
+ get { return stackedInputHandlers; }
+ }
+
+ /// <summary>
+ /// Pushes an input handler onto the list of stacked input handlers.
+ /// </summary>
+ /// <remarks><inheritdoc cref="ITextAreaInputHandler"/></remarks>
+ public void PushStackedInputHandler(TextAreaStackedInputHandler inputHandler)
+ {
+ if (inputHandler == null)
+ throw new ArgumentNullException("inputHandler");
+ stackedInputHandlers = stackedInputHandlers.Push(inputHandler);
+ inputHandler.Attach();
+ }
+
+ /// <summary>
+ /// Pops the stacked input handler (and all input handlers above it).
+ /// If <paramref name="inputHandler"/> is not found in the currently stacked input handlers, or is null, this method
+ /// does nothing.
+ /// </summary>
+ /// <remarks><inheritdoc cref="ITextAreaInputHandler"/></remarks>
+ public void PopStackedInputHandler(TextAreaStackedInputHandler inputHandler)
+ {
+ if (stackedInputHandlers.Any(i => i == inputHandler)) {
+ ITextAreaInputHandler oldHandler;
+ do {
+ oldHandler = stackedInputHandlers.Peek();
+ stackedInputHandlers = stackedInputHandlers.Pop();
+ oldHandler.Detach();
+ } while (oldHandler != inputHandler);
+ }
+ }
+ #endregion
+
+ #region Document property
+ /// <summary>
+ /// Document property.
+ /// </summary>
+ public static readonly DependencyProperty DocumentProperty
+ = TextView.DocumentProperty.AddOwner(typeof(TextArea), new FrameworkPropertyMetadata(OnDocumentChanged));
+
+ /// <summary>
+ /// Gets/Sets the document displayed by the text editor.
+ /// </summary>
+ public TextDocument Document {
+ get { return (TextDocument)GetValue(DocumentProperty); }
+ set { SetValue(DocumentProperty, value); }
+ }
+
+ /// <inheritdoc/>
+ public event EventHandler DocumentChanged;
+
+ static void OnDocumentChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextArea)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue);
+ }
+
+ void OnDocumentChanged(TextDocument oldValue, TextDocument newValue)
+ {
+ if (oldValue != null) {
+ TextDocumentWeakEventManager.Changing.RemoveListener(oldValue, this);
+ TextDocumentWeakEventManager.Changed.RemoveListener(oldValue, this);
+ TextDocumentWeakEventManager.UpdateStarted.RemoveListener(oldValue, this);
+ TextDocumentWeakEventManager.UpdateFinished.RemoveListener(oldValue, this);
+ }
+ textView.Document = newValue;
+ if (newValue != null) {
+ TextDocumentWeakEventManager.Changing.AddListener(newValue, this);
+ TextDocumentWeakEventManager.Changed.AddListener(newValue, this);
+ TextDocumentWeakEventManager.UpdateStarted.AddListener(newValue, this);
+ TextDocumentWeakEventManager.UpdateFinished.AddListener(newValue, this);
+ }
+ // Reset caret location and selection: this is necessary because the caret/selection might be invalid
+ // in the new document (e.g. if new document is shorter than the old document).
+ caret.Location = new TextLocation(1, 1);
+ this.ClearSelection();
+ if (DocumentChanged != null)
+ DocumentChanged(this, EventArgs.Empty);
+ CommandManager.InvalidateRequerySuggested();
+ }
+ #endregion
+
+ #region Options property
+ /// <summary>
+ /// Options property.
+ /// </summary>
+ public static readonly DependencyProperty OptionsProperty
+ = TextView.OptionsProperty.AddOwner(typeof(TextArea), new FrameworkPropertyMetadata(OnOptionsChanged));
+
+ /// <summary>
+ /// Gets/Sets the document displayed by the text editor.
+ /// </summary>
+ public TextEditorOptions Options {
+ get { return (TextEditorOptions)GetValue(OptionsProperty); }
+ set { SetValue(OptionsProperty, value); }
+ }
+
+ /// <summary>
+ /// Occurs when a text editor option has changed.
+ /// </summary>
+ public event PropertyChangedEventHandler OptionChanged;
+
+ /// <summary>
+ /// Raises the <see cref="OptionChanged"/> event.
+ /// </summary>
+ protected virtual void OnOptionChanged(PropertyChangedEventArgs e)
+ {
+ if (OptionChanged != null) {
+ OptionChanged(this, e);
+ }
+ }
+
+ static void OnOptionsChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextArea)dp).OnOptionsChanged((TextEditorOptions)e.OldValue, (TextEditorOptions)e.NewValue);
+ }
+
+ void OnOptionsChanged(TextEditorOptions oldValue, TextEditorOptions newValue)
+ {
+ if (oldValue != null) {
+ PropertyChangedWeakEventManager.RemoveListener(oldValue, this);
+ }
+ textView.Options = newValue;
+ if (newValue != null) {
+ PropertyChangedWeakEventManager.AddListener(newValue, this);
+ }
+ OnOptionChanged(new PropertyChangedEventArgs(null));
+ }
+ #endregion
+
+ #region ReceiveWeakEvent
+ /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
+ protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(TextDocumentWeakEventManager.Changing)) {
+ OnDocumentChanging();
+ return true;
+ } else if (managerType == typeof(TextDocumentWeakEventManager.Changed)) {
+ OnDocumentChanged((DocumentChangeEventArgs)e);
+ return true;
+ } else if (managerType == typeof(TextDocumentWeakEventManager.UpdateStarted)) {
+ OnUpdateStarted();
+ return true;
+ } else if (managerType == typeof(TextDocumentWeakEventManager.UpdateFinished)) {
+ OnUpdateFinished();
+ return true;
+ } else if (managerType == typeof(PropertyChangedWeakEventManager)) {
+ OnOptionChanged((PropertyChangedEventArgs)e);
+ return true;
+ }
+ return false;
+ }
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ return ReceiveWeakEvent(managerType, sender, e);
+ }
+ #endregion
+
+ #region Caret handling on document changes
+ void OnDocumentChanging()
+ {
+ caret.OnDocumentChanging();
+ }
+
+ void OnDocumentChanged(DocumentChangeEventArgs e)
+ {
+ caret.OnDocumentChanged(e);
+ this.Selection = selection.UpdateOnDocumentChange(e);
+ }
+
+ void OnUpdateStarted()
+ {
+ Document.UndoStack.PushOptional(new RestoreCaretAndSelectionUndoAction(this));
+ }
+
+ void OnUpdateFinished()
+ {
+ caret.OnDocumentUpdateFinished();
+ }
+
+ sealed class RestoreCaretAndSelectionUndoAction : IUndoableOperation
+ {
+ // keep textarea in weak reference because the IUndoableOperation is stored with the document
+ WeakReference textAreaReference;
+ TextViewPosition caretPosition;
+ Selection selection;
+
+ public RestoreCaretAndSelectionUndoAction(TextArea textArea)
+ {
+ this.textAreaReference = new WeakReference(textArea);
+ // Just save the old caret position, no need to validate here.
+ // If we restore it, we'll validate it anyways.
+ this.caretPosition = textArea.Caret.NonValidatedPosition;
+ this.selection = textArea.Selection;
+ }
+
+ public void Undo()
+ {
+ TextArea textArea = (TextArea)textAreaReference.Target;
+ if (textArea != null) {
+ textArea.Caret.Position = caretPosition;
+ textArea.Selection = selection;
+ }
+ }
+
+ public void Redo()
+ {
+ // redo=undo: we just restore the caret/selection state
+ Undo();
+ }
+ }
+ #endregion
+
+ #region TextView property
+ readonly TextView textView;
+ IScrollInfo scrollInfo;
+
+ /// <summary>
+ /// Gets the text view used to display text in this text area.
+ /// </summary>
+ public TextView TextView {
+ get {
+ return textView;
+ }
+ }
+ /// <inheritdoc/>
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ scrollInfo = textView;
+ ApplyScrollInfo();
+ }
+ #endregion
+
+ #region Selection property
+ internal readonly Selection emptySelection;
+ Selection selection;
+
+ /// <summary>
+ /// Occurs when the selection has changed.
+ /// </summary>
+ public event EventHandler SelectionChanged;
+
+ /// <summary>
+ /// Gets/Sets the selection in this text area.
+ /// </summary>
+
+ public Selection Selection {
+ get { return selection; }
+ set {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ if (value.textArea != this)
+ throw new ArgumentException("Cannot use a Selection instance that belongs to another text area.");
+ if (!object.Equals(selection, value)) {
+// Debug.WriteLine("Selection change from " + selection + " to " + value);
+ if (textView != null) {
+ ISegment oldSegment = selection.SurroundingSegment;
+ ISegment newSegment = value.SurroundingSegment;
+ if (!Selection.EnableVirtualSpace && (selection is SimpleSelection && value is SimpleSelection && oldSegment != null && newSegment != null)) {
+ // perf optimization:
+ // When a simple selection changes, don't redraw the whole selection, but only the changed parts.
+ int oldSegmentOffset = oldSegment.Offset;
+ int newSegmentOffset = newSegment.Offset;
+ if (oldSegmentOffset != newSegmentOffset) {
+ textView.Redraw(Math.Min(oldSegmentOffset, newSegmentOffset),
+ Math.Abs(oldSegmentOffset - newSegmentOffset),
+ DispatcherPriority.Background);
+ }
+ int oldSegmentEndOffset = oldSegment.EndOffset;
+ int newSegmentEndOffset = newSegment.EndOffset;
+ if (oldSegmentEndOffset != newSegmentEndOffset) {
+ textView.Redraw(Math.Min(oldSegmentEndOffset, newSegmentEndOffset),
+ Math.Abs(oldSegmentEndOffset - newSegmentEndOffset),
+ DispatcherPriority.Background);
+ }
+ } else {
+ textView.Redraw(oldSegment, DispatcherPriority.Background);
+ textView.Redraw(newSegment, DispatcherPriority.Background);
+ }
+ }
+ selection = value;
+ if (SelectionChanged != null)
+ SelectionChanged(this, EventArgs.Empty);
+ // a selection change causes commands like copy/paste/etc. to change status
+ CommandManager.InvalidateRequerySuggested();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Clears the current selection.
+ /// </summary>
+ public void ClearSelection()
+ {
+ this.Selection = emptySelection;
+ }
+
+ /// <summary>
+ /// The <see cref="SelectionBrush"/> property.
+ /// </summary>
+ public static readonly DependencyProperty SelectionBrushProperty =
+ DependencyProperty.Register("SelectionBrush", typeof(Brush), typeof(TextArea));
+
+ /// <summary>
+ /// Gets/Sets the background brush used for the selection.
+ /// </summary>
+ public Brush SelectionBrush {
+ get { return (Brush)GetValue(SelectionBrushProperty); }
+ set { SetValue(SelectionBrushProperty, value); }
+ }
+
+ /// <summary>
+ /// The <see cref="SelectionForeground"/> property.
+ /// </summary>
+ public static readonly DependencyProperty SelectionForegroundProperty =
+ DependencyProperty.Register("SelectionForeground", typeof(Brush), typeof(TextArea));
+
+ /// <summary>
+ /// Gets/Sets the foreground brush used selected text.
+ /// </summary>
+ public Brush SelectionForeground {
+ get { return (Brush)GetValue(SelectionForegroundProperty); }
+ set { SetValue(SelectionForegroundProperty, value); }
+ }
+
+ /// <summary>
+ /// The <see cref="SelectionBorder"/> property.
+ /// </summary>
+ public static readonly DependencyProperty SelectionBorderProperty =
+ DependencyProperty.Register("SelectionBorder", typeof(Pen), typeof(TextArea));
+
+ /// <summary>
+ /// Gets/Sets the background brush used for the selection.
+ /// </summary>
+ public Pen SelectionBorder {
+ get { return (Pen)GetValue(SelectionBorderProperty); }
+ set { SetValue(SelectionBorderProperty, value); }
+ }
+
+ /// <summary>
+ /// The <see cref="SelectionCornerRadius"/> property.
+ /// </summary>
+ public static readonly DependencyProperty SelectionCornerRadiusProperty =
+ DependencyProperty.Register("SelectionCornerRadius", typeof(double), typeof(TextArea),
+ new FrameworkPropertyMetadata(3.0));
+
+ /// <summary>
+ /// Gets/Sets the corner radius of the selection.
+ /// </summary>
+ public double SelectionCornerRadius {
+ get { return (double)GetValue(SelectionCornerRadiusProperty); }
+ set { SetValue(SelectionCornerRadiusProperty, value); }
+ }
+ #endregion
+
+ #region Force caret to stay inside selection
+ bool ensureSelectionValidRequested;
+ int allowCaretOutsideSelection;
+
+ void RequestSelectionValidation()
+ {
+ if (!ensureSelectionValidRequested && allowCaretOutsideSelection == 0) {
+ ensureSelectionValidRequested = true;
+ Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(EnsureSelectionValid));
+ }
+ }
+
+ /// <summary>
+ /// Code that updates only the caret but not the selection can cause confusion when
+ /// keys like 'Delete' delete the (possibly invisible) selected text and not the
+ /// text around the caret.
+ ///
+ /// So we'll ensure that the caret is inside the selection.
+ /// (when the caret is not in the selection, we'll clear the selection)
+ ///
+ /// This method is invoked using the Dispatcher so that code may temporarily violate this rule
+ /// (e.g. most 'extend selection' methods work by first setting the caret, then the selection),
+ /// it's sufficient to fix it after any event handlers have run.
+ /// </summary>
+ void EnsureSelectionValid()
+ {
+ ensureSelectionValidRequested = false;
+ if (allowCaretOutsideSelection == 0) {
+ if (!selection.IsEmpty && !selection.Contains(caret.Offset)) {
+ Debug.WriteLine("Resetting selection because caret is outside");
+ this.ClearSelection();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Temporarily allows positioning the caret outside the selection.
+ /// Dispose the returned IDisposable to revert the allowance.
+ /// </summary>
+ /// <remarks>
+ /// The text area only forces the caret to be inside the selection when other events
+ /// have finished running (using the dispatcher), so you don't have to use this method
+ /// for temporarily positioning the caret in event handlers.
+ /// This method is only necessary if you want to run the WPF dispatcher, e.g. if you
+ /// perform a drag'n'drop operation.
+ /// </remarks>
+ public IDisposable AllowCaretOutsideSelection()
+ {
+ VerifyAccess();
+ allowCaretOutsideSelection++;
+ return new CallbackOnDispose(
+ delegate {
+ VerifyAccess();
+ allowCaretOutsideSelection--;
+ RequestSelectionValidation();
+ });
+ }
+ #endregion
+
+ #region Properties
+ readonly Caret caret;
+
+ /// <summary>
+ /// Gets the Caret used for this text area.
+ /// </summary>
+ public Caret Caret {
+ get { return caret; }
+ }
+
+ ObservableCollection<UIElement> leftMargins = new ObservableCollection<UIElement>();
+
+ /// <summary>
+ /// Gets the collection of margins displayed to the left of the text view.
+ /// </summary>
+ public ObservableCollection<UIElement> LeftMargins {
+ get {
+ return leftMargins;
+ }
+ }
+
+ void leftMargins_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.OldItems != null) {
+ foreach (ITextViewConnect c in e.OldItems.OfType<ITextViewConnect>()) {
+ c.RemoveFromTextView(textView);
+ }
+ }
+ if (e.NewItems != null) {
+ foreach (ITextViewConnect c in e.NewItems.OfType<ITextViewConnect>()) {
+ c.AddToTextView(textView);
+ }
+ }
+ }
+
+ IReadOnlySectionProvider readOnlySectionProvider = NoReadOnlySections.Instance;
+
+ /// <summary>
+ /// Gets/Sets an object that provides read-only sections for the text area.
+ /// </summary>
+ public IReadOnlySectionProvider ReadOnlySectionProvider {
+ get { return readOnlySectionProvider; }
+ set {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ readOnlySectionProvider = value;
+ CommandManager.InvalidateRequerySuggested(); // the read-only status effects Paste.CanExecute and the IME
+ }
+ }
+ #endregion
+
+ #region IScrollInfo implementation
+ ScrollViewer scrollOwner;
+ bool canVerticallyScroll, canHorizontallyScroll;
+
+ void ApplyScrollInfo()
+ {
+ if (scrollInfo != null) {
+ scrollInfo.ScrollOwner = scrollOwner;
+ scrollInfo.CanVerticallyScroll = canVerticallyScroll;
+ scrollInfo.CanHorizontallyScroll = canHorizontallyScroll;
+ scrollOwner = null;
+ }
+ }
+
+ bool IScrollInfo.CanVerticallyScroll {
+ get { return scrollInfo != null ? scrollInfo.CanVerticallyScroll : false; }
+ set {
+ canVerticallyScroll = value;
+ if (scrollInfo != null)
+ scrollInfo.CanVerticallyScroll = value;
+ }
+ }
+
+ bool IScrollInfo.CanHorizontallyScroll {
+ get { return scrollInfo != null ? scrollInfo.CanHorizontallyScroll : false; }
+ set {
+ canHorizontallyScroll = value;
+ if (scrollInfo != null)
+ scrollInfo.CanHorizontallyScroll = value;
+ }
+ }
+
+ double IScrollInfo.ExtentWidth {
+ get { return scrollInfo != null ? scrollInfo.ExtentWidth : 0; }
+ }
+
+ double IScrollInfo.ExtentHeight {
+ get { return scrollInfo != null ? scrollInfo.ExtentHeight : 0; }
+ }
+
+ double IScrollInfo.ViewportWidth {
+ get { return scrollInfo != null ? scrollInfo.ViewportWidth : 0; }
+ }
+
+ double IScrollInfo.ViewportHeight {
+ get { return scrollInfo != null ? scrollInfo.ViewportHeight : 0; }
+ }
+
+ double IScrollInfo.HorizontalOffset {
+ get { return scrollInfo != null ? scrollInfo.HorizontalOffset : 0; }
+ }
+
+ double IScrollInfo.VerticalOffset {
+ get { return scrollInfo != null ? scrollInfo.VerticalOffset : 0; }
+ }
+
+ ScrollViewer IScrollInfo.ScrollOwner {
+ get { return scrollInfo != null ? scrollInfo.ScrollOwner : null; }
+ set {
+ if (scrollInfo != null)
+ scrollInfo.ScrollOwner = value;
+ else
+ scrollOwner = value;
+ }
+ }
+
+ void IScrollInfo.LineUp()
+ {
+ if (scrollInfo != null) scrollInfo.LineUp();
+ }
+
+ void IScrollInfo.LineDown()
+ {
+ if (scrollInfo != null) scrollInfo.LineDown();
+ }
+
+ void IScrollInfo.LineLeft()
+ {
+ if (scrollInfo != null) scrollInfo.LineLeft();
+ }
+
+ void IScrollInfo.LineRight()
+ {
+ if (scrollInfo != null) scrollInfo.LineRight();
+ }
+
+ void IScrollInfo.PageUp()
+ {
+ if (scrollInfo != null) scrollInfo.PageUp();
+ }
+
+ void IScrollInfo.PageDown()
+ {
+ if (scrollInfo != null) scrollInfo.PageDown();
+ }
+
+ void IScrollInfo.PageLeft()
+ {
+ if (scrollInfo != null) scrollInfo.PageLeft();
+ }
+
+ void IScrollInfo.PageRight()
+ {
+ if (scrollInfo != null) scrollInfo.PageRight();
+ }
+
+ void IScrollInfo.MouseWheelUp()
+ {
+ if (scrollInfo != null) scrollInfo.MouseWheelUp();
+ }
+
+ void IScrollInfo.MouseWheelDown()
+ {
+ if (scrollInfo != null) scrollInfo.MouseWheelDown();
+ }
+
+ void IScrollInfo.MouseWheelLeft()
+ {
+ if (scrollInfo != null) scrollInfo.MouseWheelLeft();
+ }
+
+ void IScrollInfo.MouseWheelRight()
+ {
+ if (scrollInfo != null) scrollInfo.MouseWheelRight();
+ }
+
+ void IScrollInfo.SetHorizontalOffset(double offset)
+ {
+ if (scrollInfo != null) scrollInfo.SetHorizontalOffset(offset);
+ }
+
+ void IScrollInfo.SetVerticalOffset(double offset)
+ {
+ if (scrollInfo != null) scrollInfo.SetVerticalOffset(offset);
+ }
+
+ Rect IScrollInfo.MakeVisible(System.Windows.Media.Visual visual, Rect rectangle)
+ {
+ if (scrollInfo != null)
+ return scrollInfo.MakeVisible(visual, rectangle);
+ else
+ return Rect.Empty;
+ }
+ #endregion
+
+ #region Focus Handling (Show/Hide Caret)
+ /// <inheritdoc/>
+ protected override void OnMouseDown(MouseButtonEventArgs e)
+ {
+ base.OnMouseDown(e);
+ Focus();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ base.OnGotKeyboardFocus(e);
+ // First activate IME, then show caret
+ ime.OnGotKeyboardFocus(e);
+ caret.Show();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ base.OnLostKeyboardFocus(e);
+ caret.Hide();
+ ime.OnLostKeyboardFocus(e);
+ }
+ #endregion
+
+ #region OnTextInput / RemoveSelectedText / ReplaceSelectionWithText
+ /// <summary>
+ /// Occurs when the TextArea receives text input.
+ /// This is like the <see cref="UIElement.TextInput"/> event,
+ /// but occurs immediately before the TextArea handles the TextInput event.
+ /// </summary>
+ public event TextCompositionEventHandler TextEntering;
+
+ /// <summary>
+ /// Occurs when the TextArea receives text input.
+ /// This is like the <see cref="UIElement.TextInput"/> event,
+ /// but occurs immediately after the TextArea handles the TextInput event.
+ /// </summary>
+ public event TextCompositionEventHandler TextEntered;
+
+ /// <summary>
+ /// Raises the TextEntering event.
+ /// </summary>
+ protected virtual void OnTextEntering(TextCompositionEventArgs e)
+ {
+ if (TextEntering != null) {
+ TextEntering(this, e);
+ }
+ }
+
+ /// <summary>
+ /// Raises the TextEntered event.
+ /// </summary>
+ protected virtual void OnTextEntered(TextCompositionEventArgs e)
+ {
+ if (TextEntered != null) {
+ TextEntered(this, e);
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnTextInput(TextCompositionEventArgs e)
+ {
+ //Debug.WriteLine("TextInput: Text='" + e.Text + "' SystemText='" + e.SystemText + "' ControlText='" + e.ControlText + "'");
+ base.OnTextInput(e);
+ if (!e.Handled && this.Document != null) {
+ if (string.IsNullOrEmpty(e.Text) || e.Text == "\x1b" || e.Text == "\b") {
+ // ASCII 0x1b = ESC.
+ // WPF produces a TextInput event with that old ASCII control char
+ // when Escape is pressed. We'll just ignore it.
+
+ // A deadkey followed by backspace causes a textinput event for the BS character.
+
+ // Similarly, some shortcuts like Alt+Space produce an empty TextInput event.
+ // We have to ignore those (not handle them) to keep the shortcut working.
+ return;
+ }
+ PerformTextInput(e);
+ e.Handled = true;
+ }
+ }
+
+ /// <summary>
+ /// Performs text input.
+ /// This raises the <see cref="TextEntering"/> event, replaces the selection with the text,
+ /// and then raises the <see cref="TextEntered"/> event.
+ /// </summary>
+ public void PerformTextInput(string text)
+ {
+ TextComposition textComposition = new TextComposition(InputManager.Current, this, text);
+ TextCompositionEventArgs e = new TextCompositionEventArgs(Keyboard.PrimaryDevice, textComposition);
+ e.RoutedEvent = TextInputEvent;
+ PerformTextInput(e);
+ }
+
+ /// <summary>
+ /// Performs text input.
+ /// This raises the <see cref="TextEntering"/> event, replaces the selection with the text,
+ /// and then raises the <see cref="TextEntered"/> event.
+ /// </summary>
+ public void PerformTextInput(TextCompositionEventArgs e)
+ {
+ if (e == null)
+ throw new ArgumentNullException("e");
+ if (this.Document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ OnTextEntering(e);
+ if (!e.Handled) {
+ if (e.Text == "\n" || e.Text == "\r" || e.Text == "\r\n")
+ ReplaceSelectionWithNewLine();
+ else
+ ReplaceSelectionWithText(e.Text);
+ OnTextEntered(e);
+ caret.BringCaretToView();
+ }
+ }
+
+ void ReplaceSelectionWithNewLine()
+ {
+ string newLine = TextUtilities.GetNewLineFromDocument(this.Document, this.Caret.Line);
+ using (this.Document.RunUpdate()) {
+ ReplaceSelectionWithText(newLine);
+ if (this.IndentationStrategy != null) {
+ DocumentLine line = this.Document.GetLineByNumber(this.Caret.Line);
+ ISegment[] deletable = GetDeletableSegments(line);
+ if (deletable.Length == 1 && deletable[0].Offset == line.Offset && deletable[0].Length == line.Length) {
+ // use indentation strategy only if the line is not read-only
+ this.IndentationStrategy.IndentLine(this.Document, line);
+ }
+ }
+ }
+ }
+
+ internal void RemoveSelectedText()
+ {
+ if (this.Document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ selection.ReplaceSelectionWithText(string.Empty);
+ #if DEBUG
+ if (!selection.IsEmpty) {
+ foreach (ISegment s in selection.Segments) {
+ Debug.Assert(this.ReadOnlySectionProvider.GetDeletableSegments(s).Count() == 0);
+ }
+ }
+ #endif
+ }
+
+ internal void ReplaceSelectionWithText(string newText)
+ {
+ if (newText == null)
+ throw new ArgumentNullException("newText");
+ if (this.Document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ selection.ReplaceSelectionWithText(newText);
+ }
+
+ internal ISegment[] GetDeletableSegments(ISegment segment)
+ {
+ var deletableSegments = this.ReadOnlySectionProvider.GetDeletableSegments(segment);
+ if (deletableSegments == null)
+ throw new InvalidOperationException("ReadOnlySectionProvider.GetDeletableSegments returned null");
+ var array = deletableSegments.ToArray();
+ int lastIndex = segment.Offset;
+ for (int i = 0; i < array.Length; i++) {
+ if (array[i].Offset < lastIndex)
+ throw new InvalidOperationException("ReadOnlySectionProvider returned incorrect segments (outside of input segment / wrong order)");
+ lastIndex = array[i].EndOffset;
+ }
+ if (lastIndex > segment.EndOffset)
+ throw new InvalidOperationException("ReadOnlySectionProvider returned incorrect segments (outside of input segment / wrong order)");
+ return array;
+ }
+ #endregion
+
+ #region IndentationStrategy property
+ /// <summary>
+ /// IndentationStrategy property.
+ /// </summary>
+ public static readonly DependencyProperty IndentationStrategyProperty =
+ DependencyProperty.Register("IndentationStrategy", typeof(IIndentationStrategy), typeof(TextArea),
+ new FrameworkPropertyMetadata(new DefaultIndentationStrategy()));
+
+ /// <summary>
+ /// Gets/Sets the indentation strategy used when inserting new lines.
+ /// </summary>
+ public IIndentationStrategy IndentationStrategy {
+ get { return (IIndentationStrategy)GetValue(IndentationStrategyProperty); }
+ set { SetValue(IndentationStrategyProperty, value); }
+ }
+ #endregion
+
+ #region OnKeyDown/OnKeyUp
+ /// <inheritdoc/>
+ protected override void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ base.OnPreviewKeyDown(e);
+ foreach (TextAreaStackedInputHandler h in stackedInputHandlers) {
+ if (e.Handled)
+ break;
+ h.OnPreviewKeyDown(e);
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnPreviewKeyUp(KeyEventArgs e)
+ {
+ base.OnPreviewKeyUp(e);
+ foreach (TextAreaStackedInputHandler h in stackedInputHandlers) {
+ if (e.Handled)
+ break;
+ h.OnPreviewKeyUp(e);
+ }
+ }
+
+ // Make life easier for text editor extensions that use a different cursor based on the pressed modifier keys.
+ /// <inheritdoc/>
+ protected override void OnKeyDown(KeyEventArgs e)
+ {
+ base.OnKeyDown(e);
+ TextView.InvalidateCursor();
+ }
+
+ /// <inheritdoc/>
+ protected override void OnKeyUp(KeyEventArgs e)
+ {
+ base.OnKeyUp(e);
+ TextView.InvalidateCursor();
+ }
+ #endregion
+
+ /// <inheritdoc/>
+ protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
+ {
+ // accept clicks even where the text area draws no background
+ return new PointHitTestResult(this, hitTestParameters.HitPoint);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ base.OnPropertyChanged(e);
+ if (e.Property == SelectionBrushProperty
+ || e.Property == SelectionBorderProperty
+ || e.Property == SelectionForegroundProperty
+ || e.Property == SelectionCornerRadiusProperty)
+ {
+ textView.Redraw();
+ }
+ }
+
+ /// <summary>
+ /// Gets the requested service.
+ /// </summary>
+ /// <returns>Returns the requested service instance, or null if the service cannot be found.</returns>
+ public virtual object GetService(Type serviceType)
+ {
+ return textView.Services.GetService(serviceType);
+ }
+
+ /// <summary>
+ /// Occurs when text inside the TextArea was copied.
+ /// </summary>
+ public event EventHandler<TextEventArgs> TextCopied;
+
+ internal void OnTextCopied(TextEventArgs e)
+ {
+ if (TextCopied != null)
+ TextCopied(this, e);
+ }
+ }
+
+ /// <summary>
+ /// EventArgs with text.
+ /// </summary>
+ [Serializable]
+ public class TextEventArgs : EventArgs
+ {
+ string text;
+
+ /// <summary>
+ /// Gets the text.
+ /// </summary>
+ public string Text {
+ get {
+ return text;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new TextEventArgs instance.
+ /// </summary>
+ public TextEventArgs(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.text = text;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaDefaultInputHandlers.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaDefaultInputHandlers.cs
new file mode 100644
index 000000000..7101d16c8
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaDefaultInputHandlers.cs
@@ -0,0 +1,120 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Input;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Contains the predefined input handlers.
+ /// </summary>
+ public class TextAreaDefaultInputHandler : TextAreaInputHandler
+ {
+ /// <summary>
+ /// Gets the caret navigation input handler.
+ /// </summary>
+ public TextAreaInputHandler CaretNavigation { get; private set; }
+
+ /// <summary>
+ /// Gets the editing input handler.
+ /// </summary>
+ public TextAreaInputHandler Editing { get; private set; }
+
+ /// <summary>
+ /// Gets the mouse selection input handler.
+ /// </summary>
+ public ITextAreaInputHandler MouseSelection { get; private set; }
+
+ /// <summary>
+ /// Creates a new TextAreaDefaultInputHandler instance.
+ /// </summary>
+ public TextAreaDefaultInputHandler(TextArea textArea) : base(textArea)
+ {
+ this.NestedInputHandlers.Add(CaretNavigation = CaretNavigationCommandHandler.Create(textArea));
+ this.NestedInputHandlers.Add(Editing = EditingCommandHandler.Create(textArea));
+ this.NestedInputHandlers.Add(MouseSelection = new SelectionMouseHandler(textArea));
+
+ this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, ExecuteUndo, CanExecuteUndo));
+ this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, ExecuteRedo, CanExecuteRedo));
+ }
+
+ internal static KeyBinding CreateFrozenKeyBinding(ICommand command, ModifierKeys modifiers, Key key)
+ {
+ KeyBinding kb = new KeyBinding(command, key, modifiers);
+ // Mark KeyBindings as frozen because they're shared between multiple editor instances.
+ // KeyBinding derives from Freezable only in .NET 4, so we have to use this little trick:
+ Freezable f = ((object)kb) as Freezable;
+ if (f != null)
+ f.Freeze();
+ return kb;
+ }
+
+ internal static void WorkaroundWPFMemoryLeak(List<InputBinding> inputBindings)
+ {
+ // Work around WPF memory leak:
+ // KeyBinding retains a reference to whichever UIElement it is used in first.
+ // Using a dummy element for this purpose ensures that we don't leak
+ // a real text editor (with a potentially large document).
+ UIElement dummyElement = new UIElement();
+ dummyElement.InputBindings.AddRange(inputBindings);
+ }
+
+ #region Undo / Redo
+ UndoStack GetUndoStack()
+ {
+ TextDocument document = this.TextArea.Document;
+ if (document != null)
+ return document.UndoStack;
+ else
+ return null;
+ }
+
+ void ExecuteUndo(object sender, ExecutedRoutedEventArgs e)
+ {
+ var undoStack = GetUndoStack();
+ if (undoStack != null) {
+ if (undoStack.CanUndo) {
+ undoStack.Undo();
+ this.TextArea.Caret.BringCaretToView();
+ }
+ e.Handled = true;
+ }
+ }
+
+ void CanExecuteUndo(object sender, CanExecuteRoutedEventArgs e)
+ {
+ var undoStack = GetUndoStack();
+ if (undoStack != null) {
+ e.Handled = true;
+ e.CanExecute = undoStack.CanUndo;
+ }
+ }
+
+ void ExecuteRedo(object sender, ExecutedRoutedEventArgs e)
+ {
+ var undoStack = GetUndoStack();
+ if (undoStack != null) {
+ if (undoStack.CanRedo) {
+ undoStack.Redo();
+ this.TextArea.Caret.BringCaretToView();
+ }
+ e.Handled = true;
+ }
+ }
+
+ void CanExecuteRedo(object sender, CanExecuteRoutedEventArgs e)
+ {
+ var undoStack = GetUndoStack();
+ if (undoStack != null) {
+ e.Handled = true;
+ e.CanExecute = undoStack.CanRedo;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaInputHandler.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaInputHandler.cs
new file mode 100644
index 000000000..3256875be
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextAreaInputHandler.cs
@@ -0,0 +1,242 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// A set of input bindings and event handlers for the text area.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// There is one active input handler per text area (<see cref="Editing.TextArea.ActiveInputHandler"/>), plus
+ /// a number of active stacked input handlers.
+ /// </para>
+ /// <para>
+ /// The text area also stores a reference to a default input handler, but that is not necessarily active.
+ /// </para>
+ /// <para>
+ /// Stacked input handlers work in addition to the set of currently active handlers (without detaching them).
+ /// They are detached in the reverse order of being attached.
+ /// </para>
+ /// </remarks>
+ public interface ITextAreaInputHandler
+ {
+ /// <summary>
+ /// Gets the text area that the input handler belongs to.
+ /// </summary>
+ TextArea TextArea {
+ get;
+ }
+
+ /// <summary>
+ /// Attaches an input handler to the text area.
+ /// </summary>
+ void Attach();
+
+ /// <summary>
+ /// Detaches the input handler from the text area.
+ /// </summary>
+ void Detach();
+ }
+
+ /// <summary>
+ /// Stacked input handler.
+ /// Uses OnEvent-methods instead of registering event handlers to ensure that the events are handled in the correct order.
+ /// </summary>
+ public abstract class TextAreaStackedInputHandler : ITextAreaInputHandler
+ {
+ readonly TextArea textArea;
+
+ /// <inheritdoc/>
+ public TextArea TextArea {
+ get { return textArea; }
+ }
+
+ /// <summary>
+ /// Creates a new TextAreaInputHandler.
+ /// </summary>
+ protected TextAreaStackedInputHandler(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ }
+
+ /// <inheritdoc/>
+ public virtual void Attach()
+ {
+ }
+
+ /// <inheritdoc/>
+ public virtual void Detach()
+ {
+ }
+
+ /// <summary>
+ /// Called for the PreviewKeyDown event.
+ /// </summary>
+ public virtual void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ }
+
+ /// <summary>
+ /// Called for the PreviewKeyUp event.
+ /// </summary>
+ public virtual void OnPreviewKeyUp(KeyEventArgs e)
+ {
+ }
+ }
+
+ /// <summary>
+ /// Default-implementation of <see cref="ITextAreaInputHandler"/>.
+ /// </summary>
+ /// <remarks><inheritdoc cref="ITextAreaInputHandler"/></remarks>
+ public class TextAreaInputHandler : ITextAreaInputHandler
+ {
+ readonly ObserveAddRemoveCollection<CommandBinding> commandBindings;
+ readonly ObserveAddRemoveCollection<InputBinding> inputBindings;
+ readonly ObserveAddRemoveCollection<ITextAreaInputHandler> nestedInputHandlers;
+ readonly TextArea textArea;
+ bool isAttached;
+
+ /// <summary>
+ /// Creates a new TextAreaInputHandler.
+ /// </summary>
+ public TextAreaInputHandler(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ commandBindings = new ObserveAddRemoveCollection<CommandBinding>(CommandBinding_Added, CommandBinding_Removed);
+ inputBindings = new ObserveAddRemoveCollection<InputBinding>(InputBinding_Added, InputBinding_Removed);
+ nestedInputHandlers = new ObserveAddRemoveCollection<ITextAreaInputHandler>(NestedInputHandler_Added, NestedInputHandler_Removed);
+ }
+
+ /// <inheritdoc/>
+ public TextArea TextArea {
+ get { return textArea; }
+ }
+
+ /// <summary>
+ /// Gets whether the input handler is currently attached to the text area.
+ /// </summary>
+ public bool IsAttached {
+ get { return isAttached; }
+ }
+
+ #region CommandBindings / InputBindings
+ /// <summary>
+ /// Gets the command bindings of this input handler.
+ /// </summary>
+ public ICollection<CommandBinding> CommandBindings {
+ get { return commandBindings; }
+ }
+
+ void CommandBinding_Added(CommandBinding commandBinding)
+ {
+ if (isAttached)
+ textArea.CommandBindings.Add(commandBinding);
+ }
+
+ void CommandBinding_Removed(CommandBinding commandBinding)
+ {
+ if (isAttached)
+ textArea.CommandBindings.Remove(commandBinding);
+ }
+
+ /// <summary>
+ /// Gets the input bindings of this input handler.
+ /// </summary>
+ public ICollection<InputBinding> InputBindings {
+ get { return inputBindings; }
+ }
+
+ void InputBinding_Added(InputBinding inputBinding)
+ {
+ if (isAttached)
+ textArea.InputBindings.Add(inputBinding);
+ }
+
+ void InputBinding_Removed(InputBinding inputBinding)
+ {
+ if (isAttached)
+ textArea.InputBindings.Remove(inputBinding);
+ }
+
+ /// <summary>
+ /// Adds a command and input binding.
+ /// </summary>
+ /// <param name="command">The command ID.</param>
+ /// <param name="modifiers">The modifiers of the keyboard shortcut.</param>
+ /// <param name="key">The key of the keyboard shortcut.</param>
+ /// <param name="handler">The event handler to run when the command is executed.</param>
+ public void AddBinding(ICommand command, ModifierKeys modifiers, Key key, ExecutedRoutedEventHandler handler)
+ {
+ this.CommandBindings.Add(new CommandBinding(command, handler));
+ this.InputBindings.Add(new KeyBinding(command, key, modifiers));
+ }
+ #endregion
+
+ #region NestedInputHandlers
+ /// <summary>
+ /// Gets the collection of nested input handlers. NestedInputHandlers are activated and deactivated
+ /// together with this input handler.
+ /// </summary>
+ public ICollection<ITextAreaInputHandler> NestedInputHandlers {
+ get { return nestedInputHandlers; }
+ }
+
+ void NestedInputHandler_Added(ITextAreaInputHandler handler)
+ {
+ if (handler == null)
+ throw new ArgumentNullException("handler");
+ if (handler.TextArea != textArea)
+ throw new ArgumentException("The nested handler must be working for the same text area!");
+ if (isAttached)
+ handler.Attach();
+ }
+
+ void NestedInputHandler_Removed(ITextAreaInputHandler handler)
+ {
+ if (isAttached)
+ handler.Detach();
+ }
+ #endregion
+
+ #region Attach/Detach
+ /// <inheritdoc/>
+ public virtual void Attach()
+ {
+ if (isAttached)
+ throw new InvalidOperationException("Input handler is already attached");
+ isAttached = true;
+
+ textArea.CommandBindings.AddRange(commandBindings);
+ textArea.InputBindings.AddRange(inputBindings);
+ foreach (ITextAreaInputHandler handler in nestedInputHandlers)
+ handler.Attach();
+ }
+
+ /// <inheritdoc/>
+ public virtual void Detach()
+ {
+ if (!isAttached)
+ throw new InvalidOperationException("Input handler is not attached");
+ isAttached = false;
+
+ foreach (CommandBinding b in commandBindings)
+ textArea.CommandBindings.Remove(b);
+ foreach (InputBinding b in inputBindings)
+ textArea.InputBindings.Remove(b);
+ foreach (ITextAreaInputHandler handler in nestedInputHandlers)
+ handler.Detach();
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextSegmentReadOnlySectionProvider.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextSegmentReadOnlySectionProvider.cs
new file mode 100644
index 000000000..c582301d4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Editing/TextSegmentReadOnlySectionProvider.cs
@@ -0,0 +1,80 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Editing
+{
+ /// <summary>
+ /// Implementation for <see cref="IReadOnlySectionProvider"/> that stores the segments
+ /// in a <see cref="TextSegmentCollection{T}"/>.
+ /// </summary>
+ public class TextSegmentReadOnlySectionProvider<T> : IReadOnlySectionProvider where T : TextSegment
+ {
+ readonly TextSegmentCollection<T> segments;
+
+ /// <summary>
+ /// Gets the collection storing the read-only segments.
+ /// </summary>
+ public TextSegmentCollection<T> Segments {
+ get { return segments; }
+ }
+
+ /// <summary>
+ /// Creates a new TextSegmentReadOnlySectionProvider instance for the specified document.
+ /// </summary>
+ public TextSegmentReadOnlySectionProvider(TextDocument textDocument)
+ {
+ segments = new TextSegmentCollection<T>(textDocument);
+ }
+
+ /// <summary>
+ /// Creates a new TextSegmentReadOnlySectionProvider instance using the specified TextSegmentCollection.
+ /// </summary>
+ public TextSegmentReadOnlySectionProvider(TextSegmentCollection<T> segments)
+ {
+ if (segments == null)
+ throw new ArgumentNullException("segments");
+ this.segments = segments;
+ }
+
+ /// <summary>
+ /// Gets whether insertion is possible at the specified offset.
+ /// </summary>
+ public virtual bool CanInsert(int offset)
+ {
+ foreach (TextSegment segment in segments.FindSegmentsContaining(offset)) {
+ if (segment.StartOffset < offset && offset < segment.EndOffset)
+ return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Gets the deletable segments inside the given segment.
+ /// </summary>
+ public virtual IEnumerable<ISegment> GetDeletableSegments(ISegment segment)
+ {
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+
+ int readonlyUntil = segment.Offset;
+ foreach (TextSegment ts in segments.FindOverlappingSegments(segment)) {
+ int start = ts.StartOffset;
+ int end = start + ts.Length;
+ if (start > readonlyUntil) {
+ yield return new SimpleSegment(readonlyUntil, start - readonlyUntil);
+ }
+ if (end > readonlyUntil) {
+ readonlyUntil = end;
+ }
+ }
+ int endOffset = segment.EndOffset;
+ if (readonlyUntil < endOffset) {
+ yield return new SimpleSegment(readonlyUntil, endOffset - readonlyUntil);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/AbstractFoldingStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/AbstractFoldingStrategy.cs
new file mode 100644
index 000000000..42eddba47
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/AbstractFoldingStrategy.cs
@@ -0,0 +1,30 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+using System.Collections.Generic;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// Base class for folding strategies.
+ /// </summary>
+ public abstract class AbstractFoldingStrategy
+ {
+ /// <summary>
+ /// Create <see cref="NewFolding"/>s for the specified document and updates the folding manager with them.
+ /// </summary>
+ public void UpdateFoldings(FoldingManager manager, TextDocument document)
+ {
+ int firstErrorOffset;
+ IEnumerable<NewFolding> foldings = CreateNewFoldings(document, out firstErrorOffset);
+ manager.UpdateFoldings(foldings, firstErrorOffset);
+ }
+
+ /// <summary>
+ /// Create <see cref="NewFolding"/>s for the specified document.
+ /// </summary>
+ public abstract IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/BraceFoldingStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/BraceFoldingStrategy.cs
new file mode 100644
index 000000000..a20f36046
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/BraceFoldingStrategy.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// Allows producing foldings from a document based on braces.
+ /// </summary>
+ public class BraceFoldingStrategy
+ {
+ private class FoldingDefinition
+ {
+ public String Open { get; set; }
+ public String Close { get; set; }
+
+ public FoldingDefinition(String open, String close)
+ {
+ Open = open;
+ Close = close;
+ }
+ }
+
+ private class FoldingOffset
+ {
+ public FoldingDefinition Definition { get; set; }
+ public int Offset { get; set; }
+ }
+
+ private List<FoldingDefinition> foldings = new List<FoldingDefinition>()
+ {
+ new FoldingDefinition("namespace","}"),
+ new FoldingDefinition("public","}"),
+ new FoldingDefinition("private","}"),
+ new FoldingDefinition("internal","}"),
+ new FoldingDefinition("if","}"),
+ new FoldingDefinition("/// <summary>","/// </summary>"),
+ };
+
+ /// <summary>
+ /// Gets/Sets the opening brace. The default value is '{'.
+ /// </summary>
+ public char OpeningBrace { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the closing brace. The default value is '}'.
+ /// </summary>
+ public char ClosingBrace { get; set; }
+
+ /// <summary>
+ /// Creates a new BraceFoldingStrategy.
+ /// </summary>
+ public BraceFoldingStrategy()
+ {
+ this.OpeningBrace = '{';
+ this.ClosingBrace = '}';
+ }
+
+ public void UpdateFoldings(FoldingManager manager, TextDocument document)
+ {
+ int firstErrorOffset;
+ IEnumerable<NewFolding> newFoldings = CreateNewFoldings(document, out firstErrorOffset);
+ manager.UpdateFoldings(newFoldings, firstErrorOffset);
+ }
+
+ /// <summary>
+ /// Create <see cref="NewFolding"/>s for the specified document.
+ /// </summary>
+ public IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset)
+ {
+ firstErrorOffset = -1;
+ return CreateNewFoldings(document);
+ }
+
+ /// <summary>
+ /// Create <see cref="NewFolding"/>s for the specified document.
+ /// </summary>
+ public IEnumerable<NewFolding> CreateNewFoldings(ITextSource document)
+ {
+ List<NewFolding> newFoldings = new List<NewFolding>();
+
+ Stack<FoldingOffset> startOffsets = new Stack<FoldingOffset>();
+ int lastNewLineOffset = 0;
+ char openingBrace = this.OpeningBrace;
+ char closingBrace = this.ClosingBrace;
+
+ var doc = document as TextDocument;
+
+ String current_line = String.Empty;
+
+ foreach (var line in doc.Lines)
+ {
+ String lineTextFull = doc.GetText(line);
+ String lineText = lineTextFull.TrimStart('\t', ' ').TrimEnd('\t', ' ');
+
+ var open_definition = foldings.SingleOrDefault(x => lineText.StartsWith(x.Open + " ") || lineText == x.Open);
+
+ if (open_definition != null)
+ {
+ current_line = lineTextFull;
+ startOffsets.Push(new FoldingOffset()
+ {
+ Definition = open_definition,
+ Offset = line.EndOffset
+ });
+ }
+ else if (foldings.Any(x => lineText.EndsWith(x.Close)))
+ {
+ if (startOffsets.Count > 0)
+ {
+ var startOffset = startOffsets.Pop();
+ //if (startOffset < lastNewLineOffset)
+ //{
+ newFoldings.Add(new NewFolding(startOffset.Offset, Math.Min(line.EndOffset, doc.TextLength))
+ {
+ //Name = current_line,
+ });
+ }
+ //}
+ }
+ else if (lineText.EndsWith("\n") || lineText.EndsWith("\r"))
+ {
+ lastNewLineOffset = line.Offset + 1;
+ }
+ }
+
+ //for (int i = 0; i < document.TextLength; i++)
+ //{
+ // char c = document.GetCharAt(i);
+ // if (c == openingBrace)
+ // {
+ // startOffsets.Push(i);
+ // }
+ // else if (c == closingBrace && startOffsets.Count > 0)
+ // {
+ // int startOffset = startOffsets.Pop();
+ // // don't fold if opening and closing brace are on the same line
+ // if (startOffset < lastNewLineOffset)
+ // {
+ // newFoldings.Add(new NewFolding(startOffset, i + 1));
+ // }
+ // }
+ // else if (c == '\n' || c == '\r')
+ // {
+ // lastNewLineOffset = i + 1;
+ // }
+ //}
+
+ newFoldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
+ return newFoldings;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingElementGenerator.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingElementGenerator.cs
new file mode 100644
index 000000000..76a1da101
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingElementGenerator.cs
@@ -0,0 +1,194 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// A <see cref="VisualLineElementGenerator"/> that produces line elements for folded <see cref="FoldingSection"/>s.
+ /// </summary>
+ public sealed class FoldingElementGenerator : VisualLineElementGenerator, ITextViewConnect
+ {
+ readonly List<TextView> textViews = new List<TextView>();
+ FoldingManager foldingManager;
+
+ #region FoldingManager property / connecting with TextView
+ /// <summary>
+ /// Gets/Sets the folding manager from which the foldings should be shown.
+ /// </summary>
+ public FoldingManager FoldingManager {
+ get {
+ return foldingManager;
+ }
+ set {
+ if (foldingManager != value) {
+ if (foldingManager != null) {
+ foreach (TextView v in textViews)
+ foldingManager.RemoveFromTextView(v);
+ }
+ foldingManager = value;
+ if (foldingManager != null) {
+ foreach (TextView v in textViews)
+ foldingManager.AddToTextView(v);
+ }
+ }
+ }
+ }
+
+ void ITextViewConnect.AddToTextView(TextView textView)
+ {
+ textViews.Add(textView);
+ if (foldingManager != null)
+ foldingManager.AddToTextView(textView);
+ }
+
+ void ITextViewConnect.RemoveFromTextView(TextView textView)
+ {
+ textViews.Remove(textView);
+ if (foldingManager != null)
+ foldingManager.RemoveFromTextView(textView);
+ }
+ #endregion
+
+ /// <inheritdoc/>
+ public override void StartGeneration(ITextRunConstructionContext context)
+ {
+ base.StartGeneration(context);
+ if (foldingManager != null) {
+ if (!foldingManager.textViews.Contains(context.TextView))
+ throw new ArgumentException("Invalid TextView");
+ if (context.Document != foldingManager.document)
+ throw new ArgumentException("Invalid document");
+ }
+ }
+
+ /// <inheritdoc/>
+ public override int GetFirstInterestedOffset(int startOffset)
+ {
+ if (foldingManager != null) {
+ foreach (FoldingSection fs in foldingManager.GetFoldingsContaining(startOffset)) {
+ // Test whether we're currently within a folded folding (that didn't just end).
+ // If so, create the fold marker immediately.
+ // This is necessary if the actual beginning of the fold marker got skipped due to another VisualElementGenerator.
+ if (fs.IsFolded && fs.EndOffset > startOffset) {
+ //return startOffset;
+ }
+ }
+ return foldingManager.GetNextFoldedFoldingStart(startOffset);
+ } else {
+ return -1;
+ }
+ }
+
+ /// <inheritdoc/>
+ public override VisualLineElement ConstructElement(int offset)
+ {
+ if (foldingManager == null)
+ return null;
+ int foldedUntil = -1;
+ FoldingSection foldingSection = null;
+ foreach (FoldingSection fs in foldingManager.GetFoldingsContaining(offset)) {
+ if (fs.IsFolded) {
+ if (fs.EndOffset > foldedUntil) {
+ foldedUntil = fs.EndOffset;
+ foldingSection = fs;
+ }
+ }
+ }
+ if (foldedUntil > offset && foldingSection != null) {
+ // Handle overlapping foldings: if there's another folded folding
+ // (starting within the foldingSection) that continues after the end of the folded section,
+ // then we'll extend our fold element to cover that overlapping folding.
+ bool foundOverlappingFolding;
+ do {
+ foundOverlappingFolding = false;
+ foreach (FoldingSection fs in FoldingManager.GetFoldingsContaining(foldedUntil)) {
+ if (fs.IsFolded && fs.EndOffset > foldedUntil) {
+ foldedUntil = fs.EndOffset;
+ foundOverlappingFolding = true;
+ }
+ }
+ } while (foundOverlappingFolding);
+
+ string title = foldingSection.Title;
+ if (string.IsNullOrEmpty(title))
+ title = "...";
+ var p = new VisualLineElementTextRunProperties(CurrentContext.GlobalTextRunProperties);
+ p.SetForegroundBrush(textBrush);
+ var textFormatter = TextFormatterFactory.Create(CurrentContext.TextView);
+ var text = FormattedTextElement.PrepareText(textFormatter, title, p);
+ return new FoldingLineElement(foldingSection, text, foldedUntil - offset) { textBrush = textBrush };
+ } else {
+ return null;
+ }
+ }
+
+ sealed class FoldingLineElement : FormattedTextElement
+ {
+ readonly FoldingSection fs;
+
+ internal Brush textBrush;
+
+ public FoldingLineElement(FoldingSection fs, TextLine text, int documentLength) : base(text, documentLength)
+ {
+ this.fs = fs;
+ }
+
+ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
+ {
+ return new FoldingLineTextRun(this, this.TextRunProperties) { textBrush = textBrush };
+ }
+
+ protected internal override void OnMouseDown(MouseButtonEventArgs e)
+ {
+ if (e.ClickCount == 2 && e.ChangedButton == MouseButton.Left) {
+ fs.IsFolded = false;
+ e.Handled = true;
+ } else {
+ base.OnMouseDown(e);
+ }
+ }
+ }
+
+ sealed class FoldingLineTextRun : FormattedTextRun
+ {
+ internal Brush textBrush;
+
+ public FoldingLineTextRun(FormattedTextElement element, TextRunProperties properties)
+ : base(element, properties)
+ {
+ }
+
+ public override void Draw(DrawingContext drawingContext, Point origin, bool rightToLeft, bool sideways)
+ {
+ var metrics = Format(double.PositiveInfinity);
+ Rect r = new Rect(origin.X, origin.Y - metrics.Baseline, metrics.Width, metrics.Height);
+ drawingContext.DrawRectangle(null, new Pen(textBrush, 1), r);
+ base.Draw(drawingContext, origin, rightToLeft, sideways);
+ }
+ }
+
+ /// <summary>
+ /// Default brush for folding element text. Value: Brushes.Gray
+ /// </summary>
+ public static readonly Brush DefaultTextBrush = Brushes.Gray;
+
+ static Brush textBrush = DefaultTextBrush;
+
+ /// <summary>
+ /// Gets/sets the brush used for folding element text.
+ /// </summary>
+ public static Brush TextBrush {
+ get { return textBrush; }
+ set { textBrush = value; }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingManager.cs
new file mode 100644
index 000000000..4035928b4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingManager.cs
@@ -0,0 +1,388 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// Stores a list of foldings for a specific TextView and TextDocument.
+ /// </summary>
+ public class FoldingManager : IWeakEventListener
+ {
+ internal readonly TextDocument document;
+
+ internal readonly List<TextView> textViews = new List<TextView>();
+ readonly TextSegmentCollection<FoldingSection> foldings;
+
+ #region Constructor
+ /// <summary>
+ /// Creates a new FoldingManager instance.
+ /// </summary>
+ public FoldingManager(TextDocument document)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ this.document = document;
+ this.foldings = new TextSegmentCollection<FoldingSection>();
+ document.VerifyAccess();
+ TextDocumentWeakEventManager.Changed.AddListener(document, this);
+ }
+
+ /// <summary>
+ /// Creates a new FoldingManager instance.
+ /// </summary>
+ [Obsolete("Use the (TextDocument) constructor instead.")]
+ public FoldingManager(TextView textView, TextDocument document)
+ : this(document)
+ {
+ }
+ #endregion
+
+ #region ReceiveWeakEvent
+ /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
+ protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(TextDocumentWeakEventManager.Changed)) {
+ OnDocumentChanged((DocumentChangeEventArgs)e);
+ return true;
+ }
+ return false;
+ }
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ return ReceiveWeakEvent(managerType, sender, e);
+ }
+
+ void OnDocumentChanged(DocumentChangeEventArgs e)
+ {
+ foldings.UpdateOffsets(e);
+ int newEndOffset = e.Offset + e.InsertionLength;
+ // extend end offset to the end of the line (including delimiter)
+ var endLine = document.GetLineByOffset(newEndOffset);
+ newEndOffset = endLine.Offset + endLine.TotalLength;
+ foreach (var affectedFolding in foldings.FindOverlappingSegments(e.Offset, newEndOffset - e.Offset)) {
+ if (affectedFolding.Length == 0) {
+ RemoveFolding(affectedFolding);
+ } else {
+ affectedFolding.ValidateCollapsedLineSections();
+ }
+ }
+ }
+ #endregion
+
+ #region Manage TextViews
+ internal void AddToTextView(TextView textView)
+ {
+ if (textView == null || textViews.Contains(textView))
+ throw new ArgumentException();
+ textViews.Add(textView);
+ foreach (FoldingSection fs in foldings) {
+ if (fs.collapsedSections != null) {
+ Array.Resize(ref fs.collapsedSections, textViews.Count);
+ fs.ValidateCollapsedLineSections();
+ }
+ }
+ }
+
+ internal void RemoveFromTextView(TextView textView)
+ {
+ int pos = textViews.IndexOf(textView);
+ if (pos < 0)
+ throw new ArgumentException();
+ textViews.RemoveAt(pos);
+ foreach (FoldingSection fs in foldings) {
+ if (fs.collapsedSections != null) {
+ var c = new CollapsedLineSection[textViews.Count];
+ Array.Copy(fs.collapsedSections, 0, c, 0, pos);
+ fs.collapsedSections[pos].Uncollapse();
+ Array.Copy(fs.collapsedSections, pos + 1, c, pos, c.Length - pos);
+ fs.collapsedSections = c;
+ }
+ }
+ }
+
+ internal void Redraw()
+ {
+ foreach (TextView textView in textViews)
+ textView.Redraw();
+ }
+
+ internal void Redraw(FoldingSection fs)
+ {
+ foreach (TextView textView in textViews)
+ textView.Redraw(fs);
+ }
+ #endregion
+
+ #region Create / Remove / Clear
+ /// <summary>
+ /// Creates a folding for the specified text section.
+ /// </summary>
+ public FoldingSection CreateFolding(int startOffset, int endOffset)
+ {
+ if (startOffset >= endOffset)
+ throw new ArgumentException("startOffset must be less than endOffset");
+ if (startOffset < 0 || endOffset > document.TextLength)
+ throw new ArgumentException("Folding must be within document boundary");
+ FoldingSection fs = new FoldingSection(this, startOffset, endOffset);
+ foldings.Add(fs);
+ Redraw(fs);
+ return fs;
+ }
+
+ /// <summary>
+ /// Removes a folding section from this manager.
+ /// </summary>
+ public void RemoveFolding(FoldingSection fs)
+ {
+ if (fs == null)
+ throw new ArgumentNullException("fs");
+ fs.IsFolded = false;
+ foldings.Remove(fs);
+ Redraw(fs);
+ }
+
+ /// <summary>
+ /// Removes all folding sections.
+ /// </summary>
+ public void Clear()
+ {
+ document.VerifyAccess();
+ foreach (FoldingSection s in foldings)
+ s.IsFolded = false;
+ foldings.Clear();
+ Redraw();
+ }
+ #endregion
+
+ #region Get...Folding
+ /// <summary>
+ /// Gets all foldings in this manager.
+ /// The foldings are returned sorted by start offset;
+ /// for multiple foldings at the same offset the order is undefined.
+ /// </summary>
+ public IEnumerable<FoldingSection> AllFoldings {
+ get { return foldings; }
+ }
+
+ /// <summary>
+ /// Gets the first offset greater or equal to <paramref name="startOffset"/> where a folded folding starts.
+ /// Returns -1 if there are no foldings after <paramref name="startOffset"/>.
+ /// </summary>
+ public int GetNextFoldedFoldingStart(int startOffset)
+ {
+ FoldingSection fs = foldings.FindFirstSegmentWithStartAfter(startOffset);
+ while (fs != null && !fs.IsFolded)
+ fs = foldings.GetNextSegment(fs);
+ return fs != null ? fs.StartOffset : -1;
+ }
+
+ /// <summary>
+ /// Gets the first folding with a <see cref="TextSegment.StartOffset"/> greater or equal to
+ /// <paramref name="startOffset"/>.
+ /// Returns null if there are no foldings after <paramref name="startOffset"/>.
+ /// </summary>
+ public FoldingSection GetNextFolding(int startOffset)
+ {
+ // TODO: returns the longest folding instead of any folding at the first position after startOffset
+ return foldings.FindFirstSegmentWithStartAfter(startOffset);
+ }
+
+ /// <summary>
+ /// Gets all foldings that start exactly at <paramref name="startOffset"/>.
+ /// </summary>
+ public ReadOnlyCollection<FoldingSection> GetFoldingsAt(int startOffset)
+ {
+ List<FoldingSection> result = new List<FoldingSection>();
+ FoldingSection fs = foldings.FindFirstSegmentWithStartAfter(startOffset);
+ while (fs != null && fs.StartOffset == startOffset) {
+ result.Add(fs);
+ fs = foldings.GetNextSegment(fs);
+ }
+ return result.AsReadOnly();
+ }
+
+ /// <summary>
+ /// Gets all foldings that contain <paramref name="offset" />.
+ /// </summary>
+ public ReadOnlyCollection<FoldingSection> GetFoldingsContaining(int offset)
+ {
+ return foldings.FindSegmentsContaining(offset);
+ }
+ #endregion
+
+ #region UpdateFoldings
+ /// <summary>
+ /// Updates the foldings in this <see cref="FoldingManager"/> using the given new foldings.
+ /// This method will try to detect which new foldings correspond to which existing foldings; and will keep the state
+ /// (<see cref="FoldingSection.IsFolded"/>) for existing foldings.
+ /// </summary>
+ /// <param name="newFoldings">The new set of foldings. These must be sorted by starting offset.</param>
+ /// <param name="firstErrorOffset">The first position of a parse error. Existing foldings starting after
+ /// this offset will be kept even if they don't appear in <paramref name="newFoldings"/>.
+ /// Use -1 for this parameter if there were no parse errors.</param>
+ public void UpdateFoldings(IEnumerable<NewFolding> newFoldings, int firstErrorOffset)
+ {
+ if (newFoldings == null)
+ throw new ArgumentNullException("newFoldings");
+
+ if (firstErrorOffset < 0)
+ firstErrorOffset = int.MaxValue;
+
+ var oldFoldings = this.AllFoldings.ToArray();
+ int oldFoldingIndex = 0;
+ int previousStartOffset = 0;
+ // merge new foldings into old foldings so that sections keep being collapsed
+ // both oldFoldings and newFoldings are sorted by start offset
+ foreach (NewFolding newFolding in newFoldings) {
+ // ensure newFoldings are sorted correctly
+ if (newFolding.StartOffset < previousStartOffset)
+ throw new ArgumentException("newFoldings must be sorted by start offset");
+ previousStartOffset = newFolding.StartOffset;
+
+ int startOffset = newFolding.StartOffset.CoerceValue(0, document.TextLength);
+ int endOffset = newFolding.EndOffset.CoerceValue(0, document.TextLength);
+
+ if (newFolding.StartOffset == newFolding.EndOffset)
+ continue; // ignore zero-length foldings
+
+ // remove old foldings that were skipped
+ while (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset > oldFoldings[oldFoldingIndex].StartOffset) {
+ this.RemoveFolding(oldFoldings[oldFoldingIndex++]);
+ }
+ FoldingSection section;
+ // reuse current folding if its matching:
+ if (oldFoldingIndex < oldFoldings.Length && newFolding.StartOffset == oldFoldings[oldFoldingIndex].StartOffset) {
+ section = oldFoldings[oldFoldingIndex++];
+ section.Length = newFolding.EndOffset - newFolding.StartOffset;
+ } else {
+ // no matching current folding; create a new one:
+ section = this.CreateFolding(newFolding.StartOffset, newFolding.EndOffset);
+ // auto-close #regions only when opening the document
+ section.IsFolded = newFolding.DefaultClosed;
+ section.Tag = newFolding;
+ }
+ section.Title = newFolding.Name;
+ }
+ // remove all outstanding old foldings:
+ while (oldFoldingIndex < oldFoldings.Length) {
+ FoldingSection oldSection = oldFoldings[oldFoldingIndex++];
+ if (oldSection.StartOffset >= firstErrorOffset)
+ break;
+ this.RemoveFolding(oldSection);
+ }
+ }
+ #endregion
+
+ #region Install
+ /// <summary>
+ /// Adds Folding support to the specified text area.
+ /// Warning: The folding manager is only valid for the text area's current document. The folding manager
+ /// must be uninstalled before the text area is bound to a different document.
+ /// </summary>
+ /// <returns>The <see cref="FoldingManager"/> that manages the list of foldings inside the text area.</returns>
+ public static FoldingManager Install(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ return new FoldingManagerInstallation(textArea);
+ }
+
+ /// <summary>
+ /// Uninstalls the folding manager.
+ /// </summary>
+ /// <exception cref="ArgumentException">The specified manager was not created using <see cref="Install"/>.</exception>
+ public static void Uninstall(FoldingManager manager)
+ {
+ if (manager == null)
+ throw new ArgumentNullException("manager");
+ FoldingManagerInstallation installation = manager as FoldingManagerInstallation;
+ if (installation != null) {
+ installation.Uninstall();
+ } else {
+ throw new ArgumentException("FoldingManager was not created using FoldingManager.Install");
+ }
+ }
+
+ sealed class FoldingManagerInstallation : FoldingManager
+ {
+ TextArea textArea;
+ FoldingMargin margin;
+ FoldingElementGenerator generator;
+
+ public FoldingManagerInstallation(TextArea textArea) : base(textArea.Document)
+ {
+ this.textArea = textArea;
+ margin = new FoldingMargin() { FoldingManager = this };
+ generator = new FoldingElementGenerator() { FoldingManager = this };
+ textArea.LeftMargins.Add(margin);
+ textArea.TextView.Services.AddService(typeof(FoldingManager), this);
+ // HACK: folding only works correctly when it has highest priority
+ textArea.TextView.ElementGenerators.Insert(0, generator);
+ textArea.Caret.PositionChanged += textArea_Caret_PositionChanged;
+ }
+
+ /*
+ void DemoMode()
+ {
+ foldingGenerator = new FoldingElementGenerator() { FoldingManager = fm };
+ foldingMargin = new FoldingMargin { FoldingManager = fm };
+ foldingMarginBorder = new Border {
+ Child = foldingMargin,
+ Background = new LinearGradientBrush(Colors.White, Colors.Transparent, 0)
+ };
+ foldingMarginBorder.SizeChanged += UpdateTextViewClip;
+ textEditor.TextArea.TextView.ElementGenerators.Add(foldingGenerator);
+ textEditor.TextArea.LeftMargins.Add(foldingMarginBorder);
+ }
+
+ void UpdateTextViewClip(object sender, SizeChangedEventArgs e)
+ {
+ textEditor.TextArea.TextView.Clip = new RectangleGeometry(
+ new Rect(-foldingMarginBorder.ActualWidth,
+ 0,
+ textEditor.TextArea.TextView.ActualWidth + foldingMarginBorder.ActualWidth,
+ textEditor.TextArea.TextView.ActualHeight));
+ }
+ */
+
+ public void Uninstall()
+ {
+ Clear();
+ if (textArea != null) {
+ textArea.Caret.PositionChanged -= textArea_Caret_PositionChanged;
+ textArea.LeftMargins.Remove(margin);
+ textArea.TextView.ElementGenerators.Remove(generator);
+ textArea.TextView.Services.RemoveService(typeof(FoldingManager));
+ margin = null;
+ generator = null;
+ textArea = null;
+ }
+ }
+
+ void textArea_Caret_PositionChanged(object sender, EventArgs e)
+ {
+ // Expand Foldings when Caret is moved into them.
+ int caretOffset = textArea.Caret.Offset;
+ foreach (FoldingSection s in GetFoldingsContaining(caretOffset)) {
+ if (s.IsFolded && s.StartOffset < caretOffset && caretOffset < s.EndOffset) {
+ s.IsFolded = false;
+ }
+ }
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs
new file mode 100644
index 000000000..c3d03646c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs
@@ -0,0 +1,343 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// A margin that shows markers for foldings and allows to expand/collapse the foldings.
+ /// </summary>
+ public class FoldingMargin : AbstractMargin
+ {
+ /// <summary>
+ /// Gets/Sets the folding manager from which the foldings should be shown.
+ /// </summary>
+ public FoldingManager FoldingManager { get; set; }
+
+ internal const double SizeFactor = Constants.PixelPerPoint;
+
+ #region Brushes
+ /// <summary>
+ /// FoldingMarkerBrush dependency property.
+ /// </summary>
+ public static readonly DependencyProperty FoldingMarkerBrushProperty =
+ DependencyProperty.RegisterAttached("FoldingMarkerBrush", typeof(Brush), typeof(FoldingMargin),
+ new FrameworkPropertyMetadata(Brushes.Gainsboro));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying the lines of folding markers.
+ /// </summary>
+ public Brush FoldingMarkerBrush {
+ get { return (Brush)GetValue(FoldingMarkerBrushProperty); }
+ set { SetValue(FoldingMarkerBrushProperty, value); }
+ }
+
+ /// <summary>
+ /// FoldingMarkerBackgroundBrush dependency property.
+ /// </summary>
+ public static readonly DependencyProperty FoldingMarkerBackgroundBrushProperty =
+ DependencyProperty.RegisterAttached("FoldingMarkerBackgroundBrush", typeof(Brush), typeof(FoldingMargin),
+ new FrameworkPropertyMetadata(Brushes.Transparent));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying the background of folding markers.
+ /// </summary>
+ public Brush FoldingMarkerBackgroundBrush {
+ get { return (Brush)GetValue(FoldingMarkerBackgroundBrushProperty); }
+ set { SetValue(FoldingMarkerBackgroundBrushProperty, value); }
+ }
+
+ /// <summary>
+ /// SelectedFoldingMarkerBrush dependency property.
+ /// </summary>
+ public static readonly DependencyProperty SelectedFoldingMarkerBrushProperty =
+ DependencyProperty.RegisterAttached("SelectedFoldingMarkerBrush",
+ typeof(Brush), typeof(FoldingMargin),
+ new FrameworkPropertyMetadata(Brushes.Gainsboro));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying the lines of selected folding markers.
+ /// </summary>
+ public Brush SelectedFoldingMarkerBrush {
+ get { return (Brush)GetValue(SelectedFoldingMarkerBrushProperty); }
+ set { SetValue(SelectedFoldingMarkerBrushProperty, value); }
+ }
+
+ /// <summary>
+ /// SelectedFoldingMarkerBackgroundBrush dependency property.
+ /// </summary>
+ public static readonly DependencyProperty SelectedFoldingMarkerBackgroundBrushProperty =
+ DependencyProperty.RegisterAttached("SelectedFoldingMarkerBackgroundBrush",
+ typeof(Brush), typeof(FoldingMargin),
+ new FrameworkPropertyMetadata(Brushes.Transparent));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying the background of selected folding markers.
+ /// </summary>
+ public Brush SelectedFoldingMarkerBackgroundBrush {
+ get { return (Brush)GetValue(SelectedFoldingMarkerBackgroundBrushProperty); }
+ set { SetValue(SelectedFoldingMarkerBackgroundBrushProperty, value); }
+ }
+
+ static void OnUpdateBrushes(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ FoldingMargin m = null;
+ if (d is FoldingMargin)
+ m = (FoldingMargin)d;
+ else if (d is TextEditor)
+ m = ((TextEditor)d).TextArea.LeftMargins.FirstOrDefault(c => c is FoldingMargin) as FoldingMargin;
+ if (m == null) return;
+ if (e.Property.Name == FoldingMarkerBrushProperty.Name)
+ m.foldingControlPen = MakeFrozenPen((Brush)e.NewValue);
+ if (e.Property.Name == SelectedFoldingMarkerBrushProperty.Name)
+ m.selectedFoldingControlPen = MakeFrozenPen((Brush)e.NewValue);
+ }
+ #endregion
+
+ /// <inheritdoc/>
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ foreach (FoldingMarginMarker m in markers) {
+ m.Measure(availableSize);
+ }
+ double width = SizeFactor * (double)GetValue(TextBlock.FontSizeProperty);
+ return new Size(PixelSnapHelpers.RoundToOdd(width, PixelSnapHelpers.GetPixelSize(this).Width), 0);
+ }
+
+ /// <inheritdoc/>
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
+ foreach (FoldingMarginMarker m in markers) {
+ int visualColumn = m.VisualLine.GetVisualColumn(m.FoldingSection.StartOffset - m.VisualLine.FirstDocumentLine.Offset);
+ TextLine textLine = m.VisualLine.GetTextLine(visualColumn);
+ double yPos = m.VisualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextMiddle) - TextView.VerticalOffset;
+ yPos -= m.DesiredSize.Height / 2;
+ double xPos = (finalSize.Width - m.DesiredSize.Width) / 2;
+ m.Arrange(new Rect(PixelSnapHelpers.Round(new Point(xPos, yPos), pixelSize), m.DesiredSize));
+ }
+ return base.ArrangeOverride(finalSize);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView)
+ {
+ if (oldTextView != null) {
+ oldTextView.VisualLinesChanged -= TextViewVisualLinesChanged;
+ }
+ base.OnTextViewChanged(oldTextView, newTextView);
+ if (newTextView != null) {
+ newTextView.VisualLinesChanged += TextViewVisualLinesChanged;
+ }
+ TextViewVisualLinesChanged(null, null);
+ }
+
+ List<FoldingMarginMarker> markers = new List<FoldingMarginMarker>();
+
+ void TextViewVisualLinesChanged(object sender, EventArgs e)
+ {
+ foreach (FoldingMarginMarker m in markers) {
+ RemoveVisualChild(m);
+ }
+ markers.Clear();
+ InvalidateVisual();
+ if (TextView != null && FoldingManager != null && TextView.VisualLinesValid) {
+ foreach (VisualLine line in TextView.VisualLines) {
+ FoldingSection fs = FoldingManager.GetNextFolding(line.FirstDocumentLine.Offset);
+ if (fs == null)
+ continue;
+ if (fs.StartOffset <= line.LastDocumentLine.Offset + line.LastDocumentLine.Length) {
+ FoldingMarginMarker m = new FoldingMarginMarker {
+ IsExpanded = !fs.IsFolded,
+ VisualLine = line,
+ FoldingSection = fs
+ };
+
+ markers.Add(m);
+ AddVisualChild(m);
+
+ m.IsMouseDirectlyOverChanged += delegate { InvalidateVisual(); };
+
+ InvalidateMeasure();
+ continue;
+ }
+ }
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override int VisualChildrenCount {
+ get { return markers.Count; }
+ }
+
+ /// <inheritdoc/>
+ protected override Visual GetVisualChild(int index)
+ {
+ return markers[index];
+ }
+
+ Pen foldingControlPen = MakeFrozenPen((Brush)FoldingMarkerBrushProperty.DefaultMetadata.DefaultValue);
+ Pen selectedFoldingControlPen = MakeFrozenPen((Brush)SelectedFoldingMarkerBrushProperty.DefaultMetadata.DefaultValue);
+
+ static Pen MakeFrozenPen(Brush brush)
+ {
+ Pen pen = new Pen(brush, 1);
+ pen.Freeze();
+ return pen;
+ }
+
+ /// <inheritdoc/>
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ if (TextView == null || !TextView.VisualLinesValid)
+ return;
+ if (TextView.VisualLines.Count == 0 || FoldingManager == null)
+ return;
+
+ var allTextLines = TextView.VisualLines.SelectMany(vl => vl.TextLines).ToList();
+ Pen[] colors = new Pen[allTextLines.Count + 1];
+ Pen[] endMarker = new Pen[allTextLines.Count];
+
+ CalculateFoldLinesForFoldingsActiveAtStart(allTextLines, colors, endMarker);
+ CalculateFoldLinesForMarkers(allTextLines, colors, endMarker);
+ DrawFoldLines(drawingContext, colors, endMarker);
+
+ base.OnRender(drawingContext);
+ }
+
+ /// <summary>
+ /// Calculates fold lines for all folding sections that start in front of the current view
+ /// and run into the current view.
+ /// </summary>
+ void CalculateFoldLinesForFoldingsActiveAtStart(List<TextLine> allTextLines, Pen[] colors, Pen[] endMarker)
+ {
+ int viewStartOffset = TextView.VisualLines[0].FirstDocumentLine.Offset;
+ int viewEndOffset = TextView.VisualLines.Last().LastDocumentLine.EndOffset;
+ var foldings = FoldingManager.GetFoldingsContaining(viewStartOffset);
+ int maxEndOffset = 0;
+ foreach (FoldingSection fs in foldings) {
+ int end = fs.EndOffset;
+ if (end <= viewEndOffset && !fs.IsFolded) {
+ int textLineNr = GetTextLineIndexFromOffset(allTextLines, end);
+ if (textLineNr >= 0) {
+ endMarker[textLineNr] = foldingControlPen;
+ }
+ }
+ if (end > maxEndOffset && fs.StartOffset < viewStartOffset) {
+ maxEndOffset = end;
+ }
+ }
+ if (maxEndOffset > 0) {
+ if (maxEndOffset > viewEndOffset) {
+ for (int i = 0; i < colors.Length; i++) {
+ colors[i] = foldingControlPen;
+ }
+ } else {
+ int maxTextLine = GetTextLineIndexFromOffset(allTextLines, maxEndOffset);
+ for (int i = 0; i <= maxTextLine; i++) {
+ colors[i] = foldingControlPen;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Calculates fold lines for all folding sections that start inside the current view
+ /// </summary>
+ void CalculateFoldLinesForMarkers(List<TextLine> allTextLines, Pen[] colors, Pen[] endMarker)
+ {
+ foreach (FoldingMarginMarker marker in markers) {
+ int end = marker.FoldingSection.EndOffset;
+ int endTextLineNr = GetTextLineIndexFromOffset(allTextLines, end);
+ if (!marker.FoldingSection.IsFolded && endTextLineNr >= 0) {
+ if (marker.IsMouseDirectlyOver)
+ endMarker[endTextLineNr] = selectedFoldingControlPen;
+ else if (endMarker[endTextLineNr] == null)
+ endMarker[endTextLineNr] = foldingControlPen;
+ }
+ int startTextLineNr = GetTextLineIndexFromOffset(allTextLines, marker.FoldingSection.StartOffset);
+ if (startTextLineNr >= 0) {
+ for (int i = startTextLineNr + 1; i < colors.Length && i - 1 != endTextLineNr; i++) {
+ if (marker.IsMouseDirectlyOver)
+ colors[i] = selectedFoldingControlPen;
+ else if (colors[i] == null)
+ colors[i] = foldingControlPen;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Draws the lines for the folding sections (vertical line with 'color', horizontal lines with 'endMarker')
+ /// Each entry in the input arrays corresponds to one TextLine.
+ /// </summary>
+ void DrawFoldLines(DrawingContext drawingContext, Pen[] colors, Pen[] endMarker)
+ {
+ // Because we are using PenLineCap.Flat (the default), for vertical lines,
+ // Y coordinates must be on pixel boundaries, whereas the X coordinate must be in the
+ // middle of a pixel. (and the other way round for horizontal lines)
+ Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
+ double markerXPos = PixelSnapHelpers.PixelAlign(RenderSize.Width / 2, pixelSize.Width);
+ double startY = 0;
+ Pen currentPen = colors[0];
+
+ Pen p = new Pen(FoldingMarkerBrush, 1);
+
+ int tlNumber = 0;
+ foreach (VisualLine vl in TextView.VisualLines) {
+ foreach (TextLine tl in vl.TextLines) {
+ if (endMarker[tlNumber] != null) {
+ double visualPos = GetVisualPos(vl, tl, pixelSize.Height);
+ drawingContext.DrawLine(p, new Point(markerXPos - pixelSize.Width / 2, visualPos), new Point(RenderSize.Width - 4, visualPos));
+ }
+ if (colors[tlNumber + 1] != currentPen) {
+ double visualPos = GetVisualPos(vl, tl, pixelSize.Height);
+ if (currentPen != null) {
+ drawingContext.DrawLine(p, new Point(markerXPos, startY + pixelSize.Height), new Point(markerXPos, visualPos - pixelSize.Height / 2));
+ }
+ currentPen = colors[tlNumber + 1];
+ startY = visualPos;
+ }
+ tlNumber++;
+ }
+ }
+ if (currentPen != null) {
+ drawingContext.DrawLine(currentPen, new Point(markerXPos, startY + pixelSize.Height / 2), new Point(markerXPos, RenderSize.Height));
+ }
+ }
+
+ double GetVisualPos(VisualLine vl, TextLine tl, double pixelHeight)
+ {
+ double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.TextMiddle) - TextView.VerticalOffset;
+ return PixelSnapHelpers.PixelAlign(pos, pixelHeight);
+ }
+
+ int GetTextLineIndexFromOffset(List<TextLine> textLines, int offset)
+ {
+ int lineNumber = TextView.Document.GetLineByOffset(offset).LineNumber;
+ VisualLine vl = TextView.GetVisualLine(lineNumber);
+ if (vl != null) {
+ int relOffset = offset - vl.FirstDocumentLine.Offset;
+ TextLine line = vl.GetTextLine(vl.GetVisualColumn(relOffset));
+ return textLines.IndexOf(line);
+ }
+ return -1;
+ }
+
+ static FoldingMargin()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(FoldingMargin), new FrameworkPropertyMetadata(typeof(FoldingMargin)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMarginMarker.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMarginMarker.cs
new file mode 100644
index 000000000..5b69d7485
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingMarginMarker.cs
@@ -0,0 +1,90 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ sealed class FoldingMarginMarker : UIElement
+ {
+ internal VisualLine VisualLine;
+ internal FoldingSection FoldingSection;
+
+ bool isExpanded;
+
+ public bool IsExpanded {
+ get { return isExpanded; }
+ set {
+ if (isExpanded != value) {
+ isExpanded = value;
+ InvalidateVisual();
+ }
+ if (FoldingSection != null)
+ FoldingSection.IsFolded = !value;
+ }
+ }
+
+ protected override void OnMouseDown(MouseButtonEventArgs e)
+ {
+ base.OnMouseDown(e);
+ if (!e.Handled) {
+ if (e.ChangedButton == MouseButton.Left) {
+ IsExpanded = !IsExpanded;
+ e.Handled = true;
+ }
+ }
+ }
+
+ const double MarginSizeFactor = 0.7;
+
+ protected override Size MeasureCore(Size availableSize)
+ {
+ double size = MarginSizeFactor * FoldingMargin.SizeFactor * (double)GetValue(TextBlock.FontSizeProperty);
+ size = PixelSnapHelpers.RoundToOdd(size, PixelSnapHelpers.GetPixelSize(this).Width);
+ return new Size(size, size);
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ FoldingMargin margin = VisualParent as FoldingMargin;
+ Pen activePen = new Pen(margin.SelectedFoldingMarkerBrush, 1);
+
+ Pen inactivePen = new Pen(margin.FoldingMarkerBrush, 1);
+ activePen.StartLineCap = inactivePen.StartLineCap = PenLineCap.Square;
+ activePen.EndLineCap = inactivePen.EndLineCap = PenLineCap.Square;
+ Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
+ Rect rect = new Rect(pixelSize.Width / 2,
+ pixelSize.Height / 2,
+ this.RenderSize.Width - pixelSize.Width - 2,
+ this.RenderSize.Height - pixelSize.Height - 2);
+
+ drawingContext.DrawRectangle(
+ IsMouseDirectlyOver ? margin.FoldingMarkerBackgroundBrush : margin.FoldingMarkerBackgroundBrush,
+ IsMouseDirectlyOver ? activePen : inactivePen, rect);
+ double middleX = rect.Left + rect.Width / 2;
+ double middleY = rect.Top + rect.Height / 2;
+ double space = PixelSnapHelpers.Round(rect.Width / 8, pixelSize.Width) + pixelSize.Width;
+ drawingContext.DrawLine(activePen,
+ new Point(rect.Left + space, middleY),
+ new Point(rect.Right - space, middleY));
+ if (!isExpanded) {
+ drawingContext.DrawLine(activePen,
+ new Point(middleX, rect.Top + space),
+ new Point(middleX, rect.Bottom - space));
+ }
+ }
+
+ protected override void OnIsMouseDirectlyOverChanged(DependencyPropertyChangedEventArgs e)
+ {
+ base.OnIsMouseDirectlyOverChanged(e);
+ InvalidateVisual();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingSection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingSection.cs
new file mode 100644
index 000000000..fd27e9b3b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/FoldingSection.cs
@@ -0,0 +1,186 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Text;
+using System.Windows.Threading;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// A section that can be folded.
+ /// </summary>
+ public sealed class FoldingSection : TextSegment
+ {
+ readonly FoldingManager manager;
+ bool isFolded;
+ internal CollapsedLineSection[] collapsedSections;
+ string title;
+
+ /// <summary>
+ /// Gets/sets if the section is folded.
+ /// </summary>
+ public bool IsFolded {
+ get { return isFolded; }
+ set {
+ if (isFolded != value) {
+ isFolded = value;
+ ValidateCollapsedLineSections(); // create/destroy CollapsedLineSection
+ manager.Redraw(this);
+ }
+ }
+ }
+
+ internal void ValidateCollapsedLineSections()
+ {
+ if (!isFolded) {
+ RemoveCollapsedLineSection();
+ return;
+ }
+ // It is possible that StartOffset/EndOffset get set to invalid values via the property setters in TextSegment,
+ // so we coerce those values into the valid range.
+ DocumentLine startLine = manager.document.GetLineByOffset(StartOffset.CoerceValue(0, manager.document.TextLength));
+ DocumentLine endLine = manager.document.GetLineByOffset(EndOffset.CoerceValue(0, manager.document.TextLength));
+ if (startLine == endLine) {
+ RemoveCollapsedLineSection();
+ } else {
+ if (collapsedSections == null)
+ collapsedSections = new CollapsedLineSection[manager.textViews.Count];
+ // Validate collapsed line sections
+ DocumentLine startLinePlusOne = startLine.NextLine;
+ for (int i = 0; i < collapsedSections.Length; i++) {
+ var collapsedSection = collapsedSections[i];
+ if (collapsedSection == null || collapsedSection.Start != startLinePlusOne || collapsedSection.End != endLine) {
+ // recreate this collapsed section
+ if (collapsedSection != null) {
+ Debug.WriteLine("CollapsedLineSection validation - recreate collapsed section from " + startLinePlusOne + " to " + endLine);
+ collapsedSection.Uncollapse();
+ }
+ collapsedSections[i] = manager.textViews[i].CollapseLines(startLinePlusOne, endLine);
+ }
+ }
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnSegmentChanged()
+ {
+ ValidateCollapsedLineSections();
+ base.OnSegmentChanged();
+ // don't redraw if the FoldingSection wasn't added to the FoldingManager's collection yet
+ if (IsConnectedToCollection)
+ manager.Redraw(this);
+ }
+
+ /// <summary>
+ /// Gets/Sets the text used to display the collapsed version of the folding section.
+ /// </summary>
+ public string Title {
+ get {
+ return title;
+ }
+ set {
+ if (title != value) {
+ title = value;
+ if (this.IsFolded)
+ manager.Redraw(this);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the content of the collapsed lines as text.
+ /// </summary>
+ public string TextContent {
+ get {
+ return manager.document.GetText(StartOffset, EndOffset - StartOffset);
+ }
+ }
+
+ /// <summary>
+ /// Gets the content of the collapsed lines as tooltip text.
+ /// </summary>
+ [Obsolete]
+ public string TooltipText {
+ get {
+ // This fixes SD-1394:
+ // Each line is checked for leading indentation whitespaces. If
+ // a line has the same or more indentation than the first line,
+ // it is reduced. If a line is less indented than the first line
+ // the indentation is removed completely.
+ //
+ // See the following example:
+ // line 1
+ // line 2
+ // line 3
+ // line 4
+ //
+ // is reduced to:
+ // line 1
+ // line 2
+ // line 3
+ // line 4
+
+ var startLine = manager.document.GetLineByOffset(StartOffset);
+ var endLine = manager.document.GetLineByOffset(EndOffset);
+ var builder = new StringBuilder();
+
+ var current = startLine;
+ ISegment startIndent = TextUtilities.GetLeadingWhitespace(manager.document, startLine);
+
+ while (current != endLine.NextLine) {
+ ISegment currentIndent = TextUtilities.GetLeadingWhitespace(manager.document, current);
+
+ if (current == startLine && current == endLine)
+ builder.Append(manager.document.GetText(StartOffset, EndOffset - StartOffset));
+ else if (current == startLine) {
+ if (current.EndOffset - StartOffset > 0)
+ builder.AppendLine(manager.document.GetText(StartOffset, current.EndOffset - StartOffset).TrimStart());
+ } else if (current == endLine) {
+ if (startIndent.Length <= currentIndent.Length)
+ builder.Append(manager.document.GetText(current.Offset + startIndent.Length, EndOffset - current.Offset - startIndent.Length));
+ else
+ builder.Append(manager.document.GetText(current.Offset + currentIndent.Length, EndOffset - current.Offset - currentIndent.Length));
+ } else {
+ if (startIndent.Length <= currentIndent.Length)
+ builder.AppendLine(manager.document.GetText(current.Offset + startIndent.Length, current.Length - startIndent.Length));
+ else
+ builder.AppendLine(manager.document.GetText(current.Offset + currentIndent.Length, current.Length - currentIndent.Length));
+ }
+
+ current = current.NextLine;
+ }
+
+ return builder.ToString();
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets an additional object associated with this folding section.
+ /// </summary>
+ public object Tag { get; set; }
+
+ internal FoldingSection(FoldingManager manager, int startOffset, int endOffset)
+ {
+ Debug.Assert(manager != null);
+ this.manager = manager;
+ this.StartOffset = startOffset;
+ this.Length = endOffset - startOffset;
+ }
+
+ void RemoveCollapsedLineSection()
+ {
+ if (collapsedSections != null) {
+ foreach (var collapsedSection in collapsedSections) {
+ if (collapsedSection != null && collapsedSection.Start != null)
+ collapsedSection.Uncollapse();
+ }
+ collapsedSections = null;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/NewFolding.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/NewFolding.cs
new file mode 100644
index 000000000..7f397c82e
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/NewFolding.cs
@@ -0,0 +1,70 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Windows;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// Helper class used for <see cref="FoldingManager.UpdateFoldings"/>.
+ /// </summary>
+ public class NewFolding : ISegment
+ {
+ /// <summary>
+ /// Gets/Sets the start offset.
+ /// </summary>
+ public int StartOffset { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the end offset.
+ /// </summary>
+ public int EndOffset { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the name displayed for the folding.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether the folding is closed by default.
+ /// </summary>
+ public bool DefaultClosed { get; set; }
+
+ /// <summary>
+ /// Creates a new NewFolding instance.
+ /// </summary>
+ public NewFolding()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new NewFolding instance.
+ /// </summary>
+ public NewFolding(int start, int end)
+ {
+ if (!(start <= end))
+ throw new ArgumentException("'start' must be less than 'end'");
+ this.StartOffset = start;
+ this.EndOffset = end;
+ this.Name = null;
+ this.DefaultClosed = false;
+ }
+
+ int ISegment.Offset {
+ get { return this.StartOffset; }
+ }
+
+ int ISegment.Length {
+ get { return this.EndOffset - this.StartOffset; }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/XmlFoldingStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/XmlFoldingStrategy.cs
new file mode 100644
index 000000000..0f888a572
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Folding/XmlFoldingStrategy.cs
@@ -0,0 +1,215 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Folding
+{
+ /// <summary>
+ /// Holds information about the start of a fold in an xml string.
+ /// </summary>
+ sealed class XmlFoldStart : NewFolding
+ {
+ internal int StartLine;
+ }
+
+ /// <summary>
+ /// Determines folds for an xml string in the editor.
+ /// </summary>
+ public class XmlFoldingStrategy : AbstractFoldingStrategy
+ {
+ /// <summary>
+ /// Flag indicating whether attributes should be displayed on folded
+ /// elements.
+ /// </summary>
+ public bool ShowAttributesWhenFolded { get; set; }
+
+ /// <summary>
+ /// Create <see cref="NewFolding"/>s for the specified document.
+ /// </summary>
+ public override IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset)
+ {
+ try {
+ XmlTextReader reader = new XmlTextReader(document.CreateReader());
+ reader.XmlResolver = null; // don't resolve DTDs
+ return CreateNewFoldings(document, reader, out firstErrorOffset);
+ } catch (XmlException) {
+ firstErrorOffset = 0;
+ return Enumerable.Empty<NewFolding>();
+ }
+ }
+
+ /// <summary>
+ /// Create <see cref="NewFolding"/>s for the specified document.
+ /// </summary>
+ public IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, XmlReader reader, out int firstErrorOffset)
+ {
+ Stack<XmlFoldStart> stack = new Stack<XmlFoldStart>();
+ List<NewFolding> foldMarkers = new List<NewFolding>();
+ try {
+ while (reader.Read()) {
+ switch (reader.NodeType) {
+ case XmlNodeType.Element:
+ if (!reader.IsEmptyElement) {
+ XmlFoldStart newFoldStart = CreateElementFoldStart(document, reader);
+ stack.Push(newFoldStart);
+ }
+ break;
+
+ case XmlNodeType.EndElement:
+ XmlFoldStart foldStart = stack.Pop();
+ CreateElementFold(document, foldMarkers, reader, foldStart);
+ break;
+
+ case XmlNodeType.Comment:
+ CreateCommentFold(document, foldMarkers, reader);
+ break;
+ }
+ }
+ firstErrorOffset = -1;
+ } catch (XmlException ex) {
+ // ignore errors at invalid positions (prevent ArgumentOutOfRangeException)
+ if (ex.LineNumber >= 1 && ex.LineNumber <= document.LineCount)
+ firstErrorOffset = document.GetOffset(ex.LineNumber, ex.LinePosition);
+ else
+ firstErrorOffset = 0;
+ }
+ foldMarkers.Sort((a,b) => a.StartOffset.CompareTo(b.StartOffset));
+ return foldMarkers;
+ }
+
+ static int GetOffset(TextDocument document, XmlReader reader)
+ {
+ IXmlLineInfo info = reader as IXmlLineInfo;
+ if (info != null && info.HasLineInfo()) {
+ return document.GetOffset(info.LineNumber, info.LinePosition);
+ } else {
+ throw new ArgumentException("XmlReader does not have positioning information.");
+ }
+ }
+
+ /// <summary>
+ /// Creates a comment fold if the comment spans more than one line.
+ /// </summary>
+ /// <remarks>The text displayed when the comment is folded is the first
+ /// line of the comment.</remarks>
+ static void CreateCommentFold(TextDocument document, List<NewFolding> foldMarkers, XmlReader reader)
+ {
+ string comment = reader.Value;
+ if (comment != null) {
+ int firstNewLine = comment.IndexOf('\n');
+ if (firstNewLine >= 0) {
+
+ // Take off 4 chars to get the actual comment start (takes
+ // into account the <!-- chars.
+
+ int startOffset = GetOffset(document, reader) - 4;
+ int endOffset = startOffset + comment.Length + 7;
+
+ string foldText = String.Concat("<!--", comment.Substring(0, firstNewLine).TrimEnd('\r') , "-->");
+ foldMarkers.Add(new NewFolding(startOffset, endOffset) { Name = foldText } );
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates an XmlFoldStart for the start tag of an element.
+ /// </summary>
+ XmlFoldStart CreateElementFoldStart(TextDocument document, XmlReader reader)
+ {
+ // Take off 1 from the offset returned
+ // from the xml since it points to the start
+ // of the element name and not the beginning
+ // tag.
+ //XmlFoldStart newFoldStart = new XmlFoldStart(reader.Prefix, reader.LocalName, reader.LineNumber - 1, reader.LinePosition - 2);
+ XmlFoldStart newFoldStart = new XmlFoldStart();
+
+ IXmlLineInfo lineInfo = (IXmlLineInfo)reader;
+ newFoldStart.StartLine = lineInfo.LineNumber;
+ newFoldStart.StartOffset = document.GetOffset(newFoldStart.StartLine, lineInfo.LinePosition - 1);
+
+ if (this.ShowAttributesWhenFolded && reader.HasAttributes) {
+ newFoldStart.Name = String.Concat("<", reader.Name, " ", GetAttributeFoldText(reader), ">");
+ } else {
+ newFoldStart.Name = String.Concat("<", reader.Name, ">");
+ }
+
+ return newFoldStart;
+ }
+
+ /// <summary>
+ /// Create an element fold if the start and end tag are on
+ /// different lines.
+ /// </summary>
+ static void CreateElementFold(TextDocument document, List<NewFolding> foldMarkers, XmlReader reader, XmlFoldStart foldStart)
+ {
+ IXmlLineInfo lineInfo = (IXmlLineInfo)reader;
+ int endLine = lineInfo.LineNumber;
+ if (endLine > foldStart.StartLine) {
+ int endCol = lineInfo.LinePosition + reader.Name.Length + 1;
+ foldStart.EndOffset = document.GetOffset(endLine, endCol);
+ foldMarkers.Add(foldStart);
+ }
+ }
+
+ /// <summary>
+ /// Gets the element's attributes as a string on one line that will
+ /// be displayed when the element is folded.
+ /// </summary>
+ /// <remarks>
+ /// Currently this puts all attributes from an element on the same
+ /// line of the start tag. It does not cater for elements where attributes
+ /// are not on the same line as the start tag.
+ /// </remarks>
+ static string GetAttributeFoldText(XmlReader reader)
+ {
+ StringBuilder text = new StringBuilder();
+
+ for (int i = 0; i < reader.AttributeCount; ++i) {
+ reader.MoveToAttribute(i);
+
+ text.Append(reader.Name);
+ text.Append("=");
+ text.Append(reader.QuoteChar.ToString());
+ text.Append(XmlEncodeAttributeValue(reader.Value, reader.QuoteChar));
+ text.Append(reader.QuoteChar.ToString());
+
+ // Append a space if this is not the
+ // last attribute.
+ if (i < reader.AttributeCount - 1) {
+ text.Append(" ");
+ }
+ }
+
+ return text.ToString();
+ }
+
+ /// <summary>
+ /// Xml encode the attribute string since the string returned from
+ /// the XmlTextReader is the plain unencoded string and .NET
+ /// does not provide us with an xml encode method.
+ /// </summary>
+ static string XmlEncodeAttributeValue(string attributeValue, char quoteChar)
+ {
+ StringBuilder encodedValue = new StringBuilder(attributeValue);
+
+ encodedValue.Replace("&", "&amp;");
+ encodedValue.Replace("<", "&lt;");
+ encodedValue.Replace(">", "&gt;");
+
+ if (quoteChar == '"') {
+ encodedValue.Replace("\"", "&quot;");
+ } else {
+ encodedValue.Replace("'", "&apos;");
+ }
+
+ return encodedValue.ToString();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/DocumentHighlighter.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/DocumentHighlighter.cs
new file mode 100644
index 000000000..393c0a930
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/DocumentHighlighter.cs
@@ -0,0 +1,468 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+using SpanStack = Tango.Scripting.Editors.Utils.ImmutableStack<Tango.Scripting.Editors.Highlighting.HighlightingSpan>;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// This class can syntax-highlight a document.
+ /// It automatically manages invalidating the highlighting when the document changes.
+ /// </summary>
+ public class DocumentHighlighter : ILineTracker, IHighlighter
+ {
+ /// <summary>
+ /// Stores the span state at the end of each line.
+ /// storedSpanStacks[0] = state at beginning of document
+ /// storedSpanStacks[i] = state after line i
+ /// </summary>
+ readonly CompressingTreeList<SpanStack> storedSpanStacks = new CompressingTreeList<SpanStack>(object.ReferenceEquals);
+ readonly CompressingTreeList<bool> isValid = new CompressingTreeList<bool>((a, b) => a == b);
+ readonly TextDocument document;
+ readonly HighlightingRuleSet baseRuleSet;
+ bool isHighlighting;
+
+ /// <summary>
+ /// Gets the document that this DocumentHighlighter is highlighting.
+ /// </summary>
+ public TextDocument Document {
+ get { return document; }
+ }
+
+ /// <summary>
+ /// Creates a new DocumentHighlighter instance.
+ /// </summary>
+ public DocumentHighlighter(TextDocument document, HighlightingRuleSet baseRuleSet)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ if (baseRuleSet == null)
+ throw new ArgumentNullException("baseRuleSet");
+ this.document = document;
+ this.baseRuleSet = baseRuleSet;
+ WeakLineTracker.Register(document, this);
+ InvalidateHighlighting();
+ }
+
+ void ILineTracker.BeforeRemoveLine(DocumentLine line)
+ {
+ CheckIsHighlighting();
+ int number = line.LineNumber;
+ storedSpanStacks.RemoveAt(number);
+ isValid.RemoveAt(number);
+ if (number < isValid.Count) {
+ isValid[number] = false;
+ if (number < firstInvalidLine)
+ firstInvalidLine = number;
+ }
+ }
+
+ void ILineTracker.SetLineLength(DocumentLine line, int newTotalLength)
+ {
+ CheckIsHighlighting();
+ int number = line.LineNumber;
+ isValid[number] = false;
+ if (number < firstInvalidLine)
+ firstInvalidLine = number;
+ }
+
+ void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine)
+ {
+ CheckIsHighlighting();
+ Debug.Assert(insertionPos.LineNumber + 1 == newLine.LineNumber);
+ int lineNumber = newLine.LineNumber;
+ storedSpanStacks.Insert(lineNumber, null);
+ isValid.Insert(lineNumber, false);
+ if (lineNumber < firstInvalidLine)
+ firstInvalidLine = lineNumber;
+ }
+
+ void ILineTracker.RebuildDocument()
+ {
+ InvalidateHighlighting();
+ }
+
+ ImmutableStack<HighlightingSpan> initialSpanStack = SpanStack.Empty;
+
+ /// <summary>
+ /// Gets/sets the the initial span stack of the document. Default value is <see cref="SpanStack.Empty" />.
+ /// </summary>
+ public ImmutableStack<HighlightingSpan> InitialSpanStack {
+ get { return initialSpanStack; }
+ set {
+ if (value == null)
+ initialSpanStack = SpanStack.Empty;
+ else
+ initialSpanStack = value;
+ InvalidateHighlighting();
+ }
+ }
+
+ /// <summary>
+ /// Invalidates all stored highlighting info.
+ /// When the document changes, the highlighting is invalidated automatically, this method
+ /// needs to be called only when there are changes to the highlighting rule set.
+ /// </summary>
+ public void InvalidateHighlighting()
+ {
+ CheckIsHighlighting();
+ storedSpanStacks.Clear();
+ storedSpanStacks.Add(initialSpanStack);
+ storedSpanStacks.InsertRange(1, document.LineCount, null);
+ isValid.Clear();
+ isValid.Add(true);
+ isValid.InsertRange(1, document.LineCount, false);
+ firstInvalidLine = 1;
+ }
+
+ int firstInvalidLine;
+
+ /// <summary>
+ /// Highlights the specified document line.
+ /// </summary>
+ /// <param name="line">The line to highlight.</param>
+ /// <returns>A <see cref="HighlightedLine"/> line object that represents the highlighted sections.</returns>
+ [ObsoleteAttribute("Use the (int lineNumber) overload instead")]
+ public HighlightedLine HighlightLine(DocumentLine line)
+ {
+ 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 {
+ HighlightUpTo(lineNumber);
+ DocumentLine line = document.GetLineByNumber(lineNumber);
+ highlightedLine = new HighlightedLine(document, line);
+ HighlightLineAndUpdateTreeList(line, lineNumber);
+ return highlightedLine;
+ } finally {
+ highlightedLine = null;
+ isHighlighting = false;
+ }
+ }
+
+ /// <inheritdoc/>
+ public SpanStack GetSpanStack(int lineNumber)
+ {
+ ThrowUtil.CheckInRangeInclusive(lineNumber, "lineNumber", 0, document.LineCount);
+ if (firstInvalidLine <= lineNumber) {
+ CheckIsHighlighting();
+ isHighlighting = true;
+ try {
+ HighlightUpTo(lineNumber + 1);
+ } finally {
+ isHighlighting = false;
+ }
+ }
+ return storedSpanStacks[lineNumber];
+ }
+
+ void CheckIsHighlighting()
+ {
+ if (isHighlighting) {
+ throw new InvalidOperationException("Invalid call - a highlighting operation is currently running.");
+ }
+ }
+
+ void HighlightUpTo(int targetLineNumber)
+ {
+ Debug.Assert(highlightedLine == null); // ensure this method is only used for
+ while (firstInvalidLine < targetLineNumber) {
+ HighlightLineAndUpdateTreeList(document.GetLineByNumber(firstInvalidLine), firstInvalidLine);
+ }
+ }
+
+ void HighlightLineAndUpdateTreeList(DocumentLine line, int lineNumber)
+ {
+ //Debug.WriteLine("Highlight line " + lineNumber + (highlightedLine != null ? "" : " (span stack only)"));
+ spanStack = storedSpanStacks[lineNumber - 1];
+ HighlightLineInternal(line);
+ if (!EqualSpanStacks(spanStack, storedSpanStacks[lineNumber])) {
+ isValid[lineNumber] = true;
+ //Debug.WriteLine("Span stack in line " + lineNumber + " changed from " + storedSpanStacks[lineNumber] + " to " + spanStack);
+ storedSpanStacks[lineNumber] = spanStack;
+ if (lineNumber + 1 < isValid.Count) {
+ isValid[lineNumber + 1] = false;
+ firstInvalidLine = lineNumber + 1;
+ } else {
+ firstInvalidLine = int.MaxValue;
+ }
+ OnHighlightStateChanged(line, lineNumber);
+ } else if (firstInvalidLine == lineNumber) {
+ isValid[lineNumber] = true;
+ firstInvalidLine = isValid.IndexOf(false);
+ if (firstInvalidLine < 0)
+ firstInvalidLine = int.MaxValue;
+ }
+ }
+
+ static bool EqualSpanStacks(SpanStack a, SpanStack b)
+ {
+ // We must use value equality between the stacks because TextViewDocumentHighlighter.OnHighlightStateChanged
+ // depends on the fact that equal input state + unchanged line contents produce equal output state.
+ if (a == b)
+ return true;
+ if (a == null || b == null)
+ return false;
+ while (!a.IsEmpty && !b.IsEmpty) {
+ if (a.Peek() != b.Peek())
+ return false;
+ a = a.Pop();
+ b = b.Pop();
+ if (a == b)
+ return true;
+ }
+ return a.IsEmpty && b.IsEmpty;
+ }
+
+ /// <summary>
+ /// Is called when the highlighting state at the end of the specified line has changed.
+ /// </summary>
+ /// <remarks>This callback must not call HighlightLine or InvalidateHighlighting.
+ /// It may call GetSpanStack, but only for the changed line and lines above.
+ /// This method must not modify the document.</remarks>
+ protected virtual void OnHighlightStateChanged(DocumentLine line, int lineNumber)
+ {
+ }
+
+ #region Highlighting Engine
+ SpanStack spanStack;
+
+ // local variables from HighlightLineInternal (are member because they are accessed by HighlighLine helper methods)
+ string lineText;
+ int lineStartOffset;
+ int position;
+
+ /// <summary>
+ /// the HighlightedLine where highlighting output is being written to.
+ /// if this variable is null, nothing is highlighted and only the span state is updated
+ /// </summary>
+ HighlightedLine highlightedLine;
+
+ void HighlightLineInternal(DocumentLine line)
+ {
+ lineStartOffset = line.Offset;
+ lineText = document.GetText(line.Offset, line.Length);
+ position = 0;
+ ResetColorStack();
+ HighlightingRuleSet currentRuleSet = this.CurrentRuleSet;
+ Stack<Match[]> storedMatchArrays = new Stack<Match[]>();
+ Match[] matches = AllocateMatchArray(currentRuleSet.Spans.Count);
+ Match endSpanMatch = null;
+
+ while (true) {
+ for (int i = 0; i < matches.Length; i++) {
+ if (matches[i] == null || (matches[i].Success && matches[i].Index < position))
+ matches[i] = currentRuleSet.Spans[i].StartExpression.Match(lineText, position);
+ }
+ if (endSpanMatch == null && !spanStack.IsEmpty)
+ endSpanMatch = spanStack.Peek().EndExpression.Match(lineText, position);
+
+ Match firstMatch = Minimum(matches, endSpanMatch);
+ if (firstMatch == null)
+ break;
+
+ HighlightNonSpans(firstMatch.Index);
+
+ Debug.Assert(position == firstMatch.Index);
+
+ if (firstMatch == endSpanMatch) {
+ HighlightingSpan poppedSpan = spanStack.Peek();
+ if (!poppedSpan.SpanColorIncludesEnd)
+ PopColor(); // pop SpanColor
+ PushColor(poppedSpan.EndColor);
+ position = firstMatch.Index + firstMatch.Length;
+ PopColor(); // pop EndColor
+ if (poppedSpan.SpanColorIncludesEnd)
+ PopColor(); // pop SpanColor
+ spanStack = spanStack.Pop();
+ currentRuleSet = this.CurrentRuleSet;
+ //FreeMatchArray(matches);
+ if (storedMatchArrays.Count > 0) {
+ matches = storedMatchArrays.Pop();
+ int index = currentRuleSet.Spans.IndexOf(poppedSpan);
+ Debug.Assert(index >= 0 && index < matches.Length);
+ if (matches[index].Index == position) {
+ throw new InvalidOperationException(
+ "A highlighting span matched 0 characters, which would cause an endless loop.\n" +
+ "Change the highlighting definition so that either the start or the end regex matches at least one character.\n" +
+ "Start regex: " + poppedSpan.StartExpression + "\n" +
+ "End regex: " + poppedSpan.EndExpression);
+ }
+ } else {
+ matches = AllocateMatchArray(currentRuleSet.Spans.Count);
+ }
+ } else {
+ int index = Array.IndexOf(matches, firstMatch);
+ Debug.Assert(index >= 0);
+ HighlightingSpan newSpan = currentRuleSet.Spans[index];
+ spanStack = spanStack.Push(newSpan);
+ currentRuleSet = this.CurrentRuleSet;
+ storedMatchArrays.Push(matches);
+ matches = AllocateMatchArray(currentRuleSet.Spans.Count);
+ if (newSpan.SpanColorIncludesStart)
+ PushColor(newSpan.SpanColor);
+ PushColor(newSpan.StartColor);
+ position = firstMatch.Index + firstMatch.Length;
+ PopColor();
+ if (!newSpan.SpanColorIncludesStart)
+ PushColor(newSpan.SpanColor);
+ }
+ endSpanMatch = null;
+ }
+ HighlightNonSpans(line.Length);
+
+ PopAllColors();
+ }
+
+ void HighlightNonSpans(int until)
+ {
+ Debug.Assert(position <= until);
+ if (position == until)
+ return;
+ if (highlightedLine != null) {
+ IList<HighlightingRule> rules = CurrentRuleSet.Rules;
+ Match[] matches = AllocateMatchArray(rules.Count);
+ while (true) {
+ for (int i = 0; i < matches.Length; i++) {
+ if (matches[i] == null || (matches[i].Success && matches[i].Index < position))
+ matches[i] = rules[i].Regex.Match(lineText, position, until - position);
+ }
+ Match firstMatch = Minimum(matches, null);
+ if (firstMatch == null)
+ break;
+
+ position = firstMatch.Index;
+ int ruleIndex = Array.IndexOf(matches, firstMatch);
+ if (firstMatch.Length == 0) {
+ throw new InvalidOperationException(
+ "A highlighting rule matched 0 characters, which would cause an endless loop.\n" +
+ "Change the highlighting definition so that the rule matches at least one character.\n" +
+ "Regex: " + rules[ruleIndex].Regex);
+ }
+ PushColor(rules[ruleIndex].Color);
+ position = firstMatch.Index + firstMatch.Length;
+ PopColor();
+ }
+ //FreeMatchArray(matches);
+ }
+ position = until;
+ }
+
+ static readonly HighlightingRuleSet emptyRuleSet = new HighlightingRuleSet() { Name = "EmptyRuleSet" };
+
+ HighlightingRuleSet CurrentRuleSet {
+ get {
+ if (spanStack.IsEmpty)
+ return baseRuleSet;
+ else
+ return spanStack.Peek().RuleSet ?? emptyRuleSet;
+ }
+ }
+ #endregion
+
+ #region Color Stack Management
+ Stack<HighlightedSection> highlightedSectionStack;
+ HighlightedSection lastPoppedSection;
+
+ void ResetColorStack()
+ {
+ Debug.Assert(position == 0);
+ lastPoppedSection = null;
+ if (highlightedLine == null) {
+ highlightedSectionStack = null;
+ } else {
+ highlightedSectionStack = new Stack<HighlightedSection>();
+ foreach (HighlightingSpan span in spanStack.Reverse()) {
+ PushColor(span.SpanColor);
+ }
+ }
+ }
+
+ void PushColor(HighlightingColor color)
+ {
+ if (highlightedLine == null)
+ return;
+ if (color == null) {
+ highlightedSectionStack.Push(null);
+ } else if (lastPoppedSection != null && lastPoppedSection.Color == color
+ && lastPoppedSection.Offset + lastPoppedSection.Length == position + lineStartOffset)
+ {
+ highlightedSectionStack.Push(lastPoppedSection);
+ lastPoppedSection = null;
+ } else {
+ HighlightedSection hs = new HighlightedSection {
+ Offset = position + lineStartOffset,
+ Color = color
+ };
+ highlightedLine.Sections.Add(hs);
+ highlightedSectionStack.Push(hs);
+ lastPoppedSection = null;
+ }
+ }
+
+ void PopColor()
+ {
+ if (highlightedLine == null)
+ return;
+ HighlightedSection s = highlightedSectionStack.Pop();
+ if (s != null) {
+ s.Length = (position + lineStartOffset) - s.Offset;
+ if (s.Length == 0)
+ highlightedLine.Sections.Remove(s);
+ else
+ lastPoppedSection = s;
+ }
+ }
+
+ void PopAllColors()
+ {
+ if (highlightedSectionStack != null) {
+ while (highlightedSectionStack.Count > 0)
+ PopColor();
+ }
+ }
+ #endregion
+
+ #region Match helpers
+ /// <summary>
+ /// Returns the first match from the array or endSpanMatch.
+ /// </summary>
+ static Match Minimum(Match[] arr, Match endSpanMatch)
+ {
+ Match min = null;
+ foreach (Match v in arr) {
+ if (v.Success && (min == null || v.Index < min.Index))
+ min = v;
+ }
+ if (endSpanMatch != null && endSpanMatch.Success && (min == null || endSpanMatch.Index < min.Index))
+ return endSpanMatch;
+ else
+ return min;
+ }
+
+ static Match[] AllocateMatchArray(int count)
+ {
+ if (count == 0)
+ return Empty<Match>.Array;
+ else
+ return new Match[count];
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedInlineBuilder.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedInlineBuilder.cs
new file mode 100644
index 000000000..ccfce3d49
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedInlineBuilder.cs
@@ -0,0 +1,214 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// Takes a series of highlighting commands and stores them.
+ /// Later, it can build inline objects (for use with WPF TextBlock) from the commands.
+ /// </summary>
+ /// <remarks>
+ /// This class is not used in AvalonEdit - but it is useful for someone who wants to put a HighlightedLine
+ /// into a TextBlock.
+ /// In SharpDevelop, we use it to provide syntax highlighting inside the search results pad.
+ /// </remarks>
+ public sealed class HighlightedInlineBuilder
+ {
+ sealed class HighlightingState
+ {
+ internal Brush Foreground;
+ internal Brush Background;
+ internal FontFamily Family;
+ internal FontWeight? Weight;
+ internal FontStyle? Style;
+
+ public HighlightingState Clone()
+ {
+ return new HighlightingState {
+ Foreground = this.Foreground,
+ Background = this.Background,
+ Family = this.Family,
+ Weight = this.Weight,
+ Style = this.Style
+ };
+ }
+ }
+
+ readonly string text;
+ List<int> stateChangeOffsets = new List<int>();
+ List<HighlightingState> stateChanges = new List<HighlightingState>();
+
+ int GetIndexForOffset(int offset)
+ {
+ if (offset < 0 || offset > text.Length)
+ throw new ArgumentOutOfRangeException("offset");
+ int index = stateChangeOffsets.BinarySearch(offset);
+ if (index < 0) {
+ index = ~index;
+ if (offset < text.Length) {
+ stateChanges.Insert(index, stateChanges[index - 1].Clone());
+ stateChangeOffsets.Insert(index, offset);
+ }
+ }
+ return index;
+ }
+
+ /// <summary>
+ /// Creates a new HighlightedInlineBuilder instance.
+ /// </summary>
+ public HighlightedInlineBuilder(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.text = text;
+ stateChangeOffsets.Add(0);
+ stateChanges.Add(new HighlightingState());
+ }
+
+ HighlightedInlineBuilder(string text, int[] offsets, HighlightingState[] states)
+ {
+ this.text = text;
+ stateChangeOffsets.AddRange(offsets);
+ stateChanges.AddRange(states);
+ }
+
+ /// <summary>
+ /// Gets the text.
+ /// </summary>
+ public string Text {
+ get { return text; }
+ }
+
+ /// <summary>
+ /// Applies the properties from the HighlightingColor to the specified text segment.
+ /// </summary>
+ public void SetHighlighting(int offset, int length, HighlightingColor color)
+ {
+ if (color == null)
+ throw new ArgumentNullException("color");
+ if (color.Foreground == null && color.FontStyle == null && color.FontWeight == null) {
+ // Optimization: don't split the HighlightingState when we're not changing
+ // any property. For example, the "Punctuation" color in C# is
+ // empty by default.
+ return;
+ }
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ HighlightingState state = stateChanges[i];
+ if (color.Foreground != null)
+ state.Foreground = color.Foreground.GetBrush(null);
+ if (color.Background != null)
+ state.Background = color.Background.GetBrush(null);
+ if (color.FontStyle != null)
+ state.Style = color.FontStyle;
+ if (color.FontWeight != null)
+ state.Weight = color.FontWeight;
+ }
+ }
+
+ /// <summary>
+ /// Sets the foreground brush on the specified text segment.
+ /// </summary>
+ public void SetForeground(int offset, int length, Brush brush)
+ {
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].Foreground = brush;
+ }
+ }
+
+ /// <summary>
+ /// Sets the background brush on the specified text segment.
+ /// </summary>
+ public void SetBackground(int offset, int length, Brush brush)
+ {
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].Background = brush;
+ }
+ }
+
+ /// <summary>
+ /// Sets the font weight on the specified text segment.
+ /// </summary>
+ public void SetFontWeight(int offset, int length, FontWeight weight)
+ {
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].Weight = weight;
+ }
+ }
+
+ /// <summary>
+ /// Sets the font style on the specified text segment.
+ /// </summary>
+ public void SetFontStyle(int offset, int length, FontStyle style)
+ {
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].Style = style;
+ }
+ }
+
+ /// <summary>
+ /// Sets the font family on the specified text segment.
+ /// </summary>
+ public void SetFontFamily(int offset, int length, FontFamily family)
+ {
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].Family = family;
+ }
+ }
+
+ /// <summary>
+ /// Creates WPF Run instances that can be used for TextBlock.Inlines.
+ /// </summary>
+ public Run[] CreateRuns()
+ {
+ Run[] runs = new Run[stateChanges.Count];
+ for (int i = 0; i < runs.Length; i++) {
+ int startOffset = stateChangeOffsets[i];
+ int endOffset = i + 1 < stateChangeOffsets.Count ? stateChangeOffsets[i + 1] : text.Length;
+ Run r = new Run(text.Substring(startOffset, endOffset - startOffset));
+ HighlightingState state = stateChanges[i];
+ if (state.Foreground != null)
+ r.Foreground = state.Foreground;
+ if (state.Background != null)
+ r.Background = state.Background;
+ if (state.Weight != null)
+ r.FontWeight = state.Weight.Value;
+ if (state.Family != null)
+ r.FontFamily = state.Family;
+ if (state.Style != null)
+ r.FontStyle = state.Style.Value;
+ runs[i] = r;
+ }
+ return runs;
+ }
+
+ /// <summary>
+ /// Clones this HighlightedInlineBuilder.
+ /// </summary>
+ public HighlightedInlineBuilder Clone()
+ {
+ return new HighlightedInlineBuilder(this.text,
+ stateChangeOffsets.ToArray(),
+ stateChanges.Select(sc => sc.Clone()).ToArray());
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedLine.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedLine.cs
new file mode 100644
index 000000000..e6932c0aa
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedLine.cs
@@ -0,0 +1,154 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// Represents a highlighted document line.
+ /// </summary>
+ public class HighlightedLine
+ {
+ /// <summary>
+ /// Creates a new HighlightedLine instance.
+ /// </summary>
+ public HighlightedLine(TextDocument document, DocumentLine documentLine)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ if (!document.Lines.Contains(documentLine))
+ throw new ArgumentException("Line is null or not part of document");
+ this.Document = document;
+ this.DocumentLine = documentLine;
+ this.Sections = new NullSafeCollection<HighlightedSection>();
+ }
+
+ /// <summary>
+ /// Gets the document associated with this HighlightedLine.
+ /// </summary>
+ public TextDocument Document { get; private set; }
+
+ /// <summary>
+ /// Gets the document line associated with this HighlightedLine.
+ /// </summary>
+ public DocumentLine DocumentLine { get; private set; }
+
+ /// <summary>
+ /// Gets the highlighted sections.
+ /// The sections are not overlapping, but they may be nested.
+ /// In that case, outer sections come in the list before inner sections.
+ /// The sections are sorted by start offset.
+ /// </summary>
+ public IList<HighlightedSection> Sections { get; private set; }
+
+ /// <summary>
+ /// Gets the default color of all text outside a <see cref="HighlightedSection"/>.
+ /// </summary>
+ public HighlightingColor DefaultTextColor { get; set; }
+
+ sealed class HtmlElement : IComparable<HtmlElement>
+ {
+ internal readonly int Offset;
+ internal readonly int Nesting;
+ internal readonly bool IsEnd;
+ internal readonly HighlightingColor Color;
+
+ public HtmlElement(int offset, int nesting, bool isEnd, HighlightingColor color)
+ {
+ this.Offset = offset;
+ this.Nesting = nesting;
+ this.IsEnd = isEnd;
+ this.Color = color;
+ }
+
+ public int CompareTo(HtmlElement other)
+ {
+ int r = Offset.CompareTo(other.Offset);
+ if (r != 0)
+ return r;
+ if (IsEnd != other.IsEnd) {
+ if (IsEnd)
+ return -1;
+ else
+ return 1;
+ } else {
+ if (IsEnd)
+ return other.Nesting.CompareTo(Nesting);
+ else
+ return Nesting.CompareTo(other.Nesting);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Produces HTML code for the line, with &lt;span class="colorName"&gt; tags.
+ /// </summary>
+ public string ToHtml(HtmlOptions options)
+ {
+ int startOffset = this.DocumentLine.Offset;
+ return ToHtml(startOffset, startOffset + this.DocumentLine.Length, options);
+ }
+
+ /// <summary>
+ /// Produces HTML code for a section of the line, with &lt;span class="colorName"&gt; tags.
+ /// </summary>
+ public string ToHtml(int startOffset, int endOffset, HtmlOptions options)
+ {
+ if (options == null)
+ throw new ArgumentNullException("options");
+ int documentLineStartOffset = this.DocumentLine.Offset;
+ int documentLineEndOffset = documentLineStartOffset + this.DocumentLine.Length;
+ if (startOffset < documentLineStartOffset || startOffset > documentLineEndOffset)
+ throw new ArgumentOutOfRangeException("startOffset", startOffset, "Value must be between " + documentLineStartOffset + " and " + documentLineEndOffset);
+ if (endOffset < startOffset || endOffset > documentLineEndOffset)
+ throw new ArgumentOutOfRangeException("endOffset", endOffset, "Value must be between startOffset and " + documentLineEndOffset);
+ ISegment requestedSegment = new SimpleSegment(startOffset, endOffset - startOffset);
+
+ List<HtmlElement> elements = new List<HtmlElement>();
+ for (int i = 0; i < this.Sections.Count; i++) {
+ HighlightedSection s = this.Sections[i];
+ if (s.GetOverlap(requestedSegment).Length > 0) {
+ elements.Add(new HtmlElement(s.Offset, i, false, s.Color));
+ elements.Add(new HtmlElement(s.Offset + s.Length, i, true, s.Color));
+ }
+ }
+ elements.Sort();
+
+ TextDocument document = this.Document;
+ StringWriter w = new StringWriter(CultureInfo.InvariantCulture);
+ int textOffset = startOffset;
+ foreach (HtmlElement e in elements) {
+ int newOffset = Math.Min(e.Offset, endOffset);
+ if (newOffset > startOffset) {
+ HtmlClipboard.EscapeHtml(w, document.GetText(textOffset, newOffset - textOffset), options);
+ }
+ textOffset = Math.Max(textOffset, newOffset);
+ if (options.ColorNeedsSpanForStyling(e.Color)) {
+ if (e.IsEnd) {
+ w.Write("</span>");
+ } else {
+ w.Write("<span");
+ options.WriteStyleAttributeForColor(w, e.Color);
+ w.Write('>');
+ }
+ }
+ }
+ HtmlClipboard.EscapeHtml(w, document.GetText(textOffset, endOffset - textOffset), options);
+ return w.ToString();
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[" + GetType().Name + " " + ToHtml(new HtmlOptions()) + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedSection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedSection.cs
new file mode 100644
index 000000000..9e6fb56f1
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightedSection.cs
@@ -0,0 +1,33 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A text section with syntax highlighting information.
+ /// </summary>
+ public class HighlightedSection : ISegment
+ {
+ /// <summary>
+ /// Gets/sets the document offset of the section.
+ /// </summary>
+ public int Offset { get; set; }
+
+ /// <summary>
+ /// Gets/sets the length of the section.
+ /// </summary>
+ public int Length { get; set; }
+
+ int ISegment.EndOffset {
+ get { return this.Offset + this.Length; }
+ }
+
+ /// <summary>
+ /// Gets the highlighting color associated with the highlighted section.
+ /// </summary>
+ public HighlightingColor Color { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingBrush.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingBrush.cs
new file mode 100644
index 000000000..2e99013f3
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingBrush.cs
@@ -0,0 +1,117 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Windows;
+using System.Windows.Media;
+
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A brush used for syntax highlighting. Can retrieve a real brush on-demand.
+ /// </summary>
+ [Serializable]
+ public abstract class HighlightingBrush
+ {
+ /// <summary>
+ /// Gets the real brush.
+ /// </summary>
+ /// <param name="context">The construction context. context can be null!</param>
+ public abstract Brush GetBrush(ITextRunConstructionContext context);
+
+ /// <summary>
+ /// Gets the color of the brush.
+ /// </summary>
+ /// <param name="context">The construction context. context can be null!</param>
+ public virtual Color? GetColor(ITextRunConstructionContext context)
+ {
+ SolidColorBrush scb = GetBrush(context) as SolidColorBrush;
+ if (scb != null)
+ return scb.Color;
+ else
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Highlighting brush implementation that takes a frozen brush.
+ /// </summary>
+ [Serializable]
+ sealed class SimpleHighlightingBrush : HighlightingBrush, ISerializable
+ {
+ readonly SolidColorBrush brush;
+
+ public SimpleHighlightingBrush(SolidColorBrush brush)
+ {
+ brush.Freeze();
+ this.brush = brush;
+ }
+
+ public SimpleHighlightingBrush(Color color) : this(new SolidColorBrush(color)) {}
+
+ public override Brush GetBrush(ITextRunConstructionContext context)
+ {
+ return brush;
+ }
+
+ public override string ToString()
+ {
+ return brush.ToString();
+ }
+
+ SimpleHighlightingBrush(SerializationInfo info, StreamingContext context)
+ {
+ this.brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(info.GetString("color")));
+ brush.Freeze();
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("color", brush.Color.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+
+ /// <summary>
+ /// HighlightingBrush implementation that finds a brush using a resource.
+ /// </summary>
+ [Serializable]
+ sealed class SystemColorHighlightingBrush : HighlightingBrush, ISerializable
+ {
+ readonly PropertyInfo property;
+
+ public SystemColorHighlightingBrush(PropertyInfo property)
+ {
+ Debug.Assert(property.ReflectedType == typeof(SystemColors));
+ Debug.Assert(typeof(Brush).IsAssignableFrom(property.PropertyType));
+ this.property = property;
+ }
+
+ public override Brush GetBrush(ITextRunConstructionContext context)
+ {
+ return (Brush)property.GetValue(null, null);
+ }
+
+ public override string ToString()
+ {
+ return property.Name;
+ }
+
+ SystemColorHighlightingBrush(SerializationInfo info, StreamingContext context)
+ {
+ property = typeof(SystemColors).GetProperty(info.GetString("propertyName"));
+ if (property == null)
+ throw new ArgumentException("Error deserializing SystemColorHighlightingBrush");
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("propertyName", property.Name);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColor.cs
new file mode 100644
index 000000000..f1b1a25c8
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColor.cs
@@ -0,0 +1,119 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Security.Permissions;
+using System.Text;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A highlighting color is a set of font properties and foreground and background color.
+ /// </summary>
+ [Serializable]
+ public class HighlightingColor : ISerializable
+ {
+ /// <summary>
+ /// Gets/Sets the name of the color.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets/sets the font weight. Null if the highlighting color does not change the font weight.
+ /// </summary>
+ public FontWeight? FontWeight { get; set; }
+
+ /// <summary>
+ /// Gets/sets the font style. Null if the highlighting color does not change the font style.
+ /// </summary>
+ public FontStyle? FontStyle { get; set; }
+
+ /// <summary>
+ /// Gets/sets the foreground color applied by the highlighting.
+ /// </summary>
+ public HighlightingBrush Foreground { get; set; }
+
+ /// <summary>
+ /// Gets/sets the background color applied by the highlighting.
+ /// </summary>
+ public HighlightingBrush Background { get; set; }
+
+ /// <summary>
+ /// Creates a new HighlightingColor instance.
+ /// </summary>
+ public HighlightingColor()
+ {
+ }
+
+ /// <summary>
+ /// Deserializes a HighlightingColor.
+ /// </summary>
+ protected HighlightingColor(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException("info");
+ this.Name = info.GetString("Name");
+ if (info.GetBoolean("HasWeight"))
+ this.FontWeight = System.Windows.FontWeight.FromOpenTypeWeight(info.GetInt32("Weight"));
+ if (info.GetBoolean("HasStyle"))
+ this.FontStyle = (FontStyle?)new FontStyleConverter().ConvertFromInvariantString(info.GetString("Style"));
+ this.Foreground = (HighlightingBrush)info.GetValue("Foreground", typeof(HighlightingBrush));
+ this.Background = (HighlightingBrush)info.GetValue("Background", typeof(HighlightingBrush));
+ }
+
+ /// <summary>
+ /// Serializes this HighlightingColor instance.
+ /// </summary>
+ [System.Security.SecurityCritical]
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException("info");
+ info.AddValue("Name", this.Name);
+ info.AddValue("HasWeight", this.FontWeight.HasValue);
+ if (this.FontWeight.HasValue)
+ info.AddValue("Weight", this.FontWeight.Value.ToOpenTypeWeight());
+ info.AddValue("HasStyle", this.FontStyle.HasValue);
+ if (this.FontStyle.HasValue)
+ info.AddValue("Style", this.FontStyle.Value.ToString());
+ info.AddValue("Foreground", this.Foreground);
+ info.AddValue("Background", this.Background);
+ }
+
+ /// <summary>
+ /// Gets CSS code for the color.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "CSS usually uses lowercase, and all possible values are English-only")]
+ public virtual string ToCss()
+ {
+ StringBuilder b = new StringBuilder();
+ if (Foreground != null) {
+ Color? c = Foreground.GetColor(null);
+ if (c != null) {
+ b.AppendFormat(CultureInfo.InvariantCulture, "color: #{0:x2}{1:x2}{2:x2}; ", c.Value.R, c.Value.G, c.Value.B);
+ }
+ }
+ if (FontWeight != null) {
+ b.Append("font-weight: ");
+ b.Append(FontWeight.Value.ToString().ToLowerInvariant());
+ b.Append("; ");
+ }
+ if (FontStyle != null) {
+ b.Append("font-style: ");
+ b.Append(FontStyle.Value.ToString().ToLowerInvariant());
+ b.Append("; ");
+ }
+ return b.ToString();
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[" + GetType() + " " + (string.IsNullOrEmpty(this.Name) ? ToCss() : this.Name) + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColorizer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColorizer.cs
new file mode 100644
index 000000000..4bfacc96c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingColorizer.cs
@@ -0,0 +1,286 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A colorizes that interprets a highlighting rule set and colors the document accordingly.
+ /// </summary>
+ public class HighlightingColorizer : DocumentColorizingTransformer
+ {
+ readonly HighlightingRuleSet ruleSet;
+
+ /// <summary>
+ /// Creates a new HighlightingColorizer instance.
+ /// </summary>
+ /// <param name="ruleSet">The root highlighting rule set.</param>
+ public HighlightingColorizer(HighlightingRuleSet ruleSet)
+ {
+ if (ruleSet == null)
+ throw new ArgumentNullException("ruleSet");
+ this.ruleSet = ruleSet;
+ }
+
+ /// <summary>
+ /// This constructor is obsolete - please use the other overload instead.
+ /// </summary>
+ /// <param name="textView">UNUSED</param>
+ /// <param name="ruleSet">The root highlighting rule set.</param>
+ [Obsolete("The TextView parameter is no longer used, please use the constructor taking only HighlightingRuleSet instead")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "textView")]
+ public HighlightingColorizer(TextView textView, HighlightingRuleSet ruleSet)
+ : this(ruleSet)
+ {
+ }
+
+ void textView_DocumentChanged(object sender, EventArgs e)
+ {
+ OnDocumentChanged((TextView)sender);
+ }
+
+ void OnDocumentChanged(TextView textView)
+ {
+ // remove existing highlighter, if any exists
+ textView.Services.RemoveService(typeof(IHighlighter));
+ textView.Services.RemoveService(typeof(DocumentHighlighter));
+
+ TextDocument document = textView.Document;
+ if (document != null) {
+ IHighlighter highlighter = CreateHighlighter(textView, document);
+ textView.Services.AddService(typeof(IHighlighter), highlighter);
+ // for backward compatiblity, we're registering using both the interface and concrete types
+ if (highlighter is DocumentHighlighter)
+ textView.Services.AddService(typeof(DocumentHighlighter), highlighter);
+ }
+ }
+
+ /// <summary>
+ /// Creates the IHighlighter instance for the specified text document.
+ /// </summary>
+ protected virtual IHighlighter CreateHighlighter(TextView textView, TextDocument document)
+ {
+ return new TextViewDocumentHighlighter(this, textView, document, ruleSet);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnAddToTextView(TextView textView)
+ {
+ base.OnAddToTextView(textView);
+ textView.DocumentChanged += textView_DocumentChanged;
+ textView.VisualLineConstructionStarting += textView_VisualLineConstructionStarting;
+ OnDocumentChanged(textView);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnRemoveFromTextView(TextView textView)
+ {
+ base.OnRemoveFromTextView(textView);
+ textView.Services.RemoveService(typeof(IHighlighter));
+ textView.Services.RemoveService(typeof(DocumentHighlighter));
+ textView.DocumentChanged -= textView_DocumentChanged;
+ textView.VisualLineConstructionStarting -= textView_VisualLineConstructionStarting;
+ }
+
+ void textView_VisualLineConstructionStarting(object sender, VisualLineConstructionStartEventArgs e)
+ {
+ IHighlighter highlighter = ((TextView)sender).Services.GetService(typeof(IHighlighter)) as IHighlighter;
+ if (highlighter != null) {
+ // Force update of highlighting state up to the position where we start generating visual lines.
+ // This is necessary in case the document gets modified above the FirstLineInView so that the highlighting state changes.
+ // We need to detect this case and issue a redraw (through TextViewDocumentHighligher.OnHighlightStateChanged)
+ // before the visual line construction reuses existing lines that were built using the invalid highlighting state.
+ lineNumberBeingColorized = e.FirstLineInView.LineNumber - 1;
+ highlighter.GetSpanStack(lineNumberBeingColorized);
+ lineNumberBeingColorized = 0;
+ }
+ }
+
+ DocumentLine lastColorizedLine;
+
+ /// <inheritdoc/>
+ protected override void Colorize(ITextRunConstructionContext context)
+ {
+ this.lastColorizedLine = null;
+ base.Colorize(context);
+ if (this.lastColorizedLine != context.VisualLine.LastDocumentLine) {
+ IHighlighter highlighter = context.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter;
+ if (highlighter != null) {
+ // In some cases, it is possible that we didn't highlight the last document line within the visual line
+ // (e.g. when the line ends with a fold marker).
+ // But even if we didn't highlight it, we'll have to update the highlighting state for it so that the
+ // proof inside TextViewDocumentHighlighter.OnHighlightStateChanged holds.
+ lineNumberBeingColorized = context.VisualLine.LastDocumentLine.LineNumber;
+ highlighter.GetSpanStack(lineNumberBeingColorized);
+ lineNumberBeingColorized = 0;
+ }
+ }
+ this.lastColorizedLine = null;
+ }
+
+ int lineNumberBeingColorized;
+
+ /// <inheritdoc/>
+ protected override void ColorizeLine(DocumentLine line)
+ {
+ IHighlighter highlighter = CurrentContext.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter;
+ if (highlighter != null) {
+ lineNumberBeingColorized = line.LineNumber;
+ HighlightedLine hl = highlighter.HighlightLine(lineNumberBeingColorized);
+ lineNumberBeingColorized = 0;
+ foreach (HighlightedSection section in hl.Sections) {
+ if (IsEmptyColor(section.Color))
+ continue;
+ ChangeLinePart(section.Offset, section.Offset + section.Length,
+ visualLineElement => ApplyColorToElement(visualLineElement, section.Color));
+ }
+ }
+ this.lastColorizedLine = line;
+ }
+
+ /// <summary>
+ /// Gets whether the color is empty (has no effect on a VisualLineTextElement).
+ /// For example, the C# "Punctuation" is an empty color.
+ /// </summary>
+ bool IsEmptyColor(HighlightingColor color)
+ {
+ if (color == null)
+ return true;
+ return color.Background == null && color.Foreground == null
+ && color.FontStyle == null && color.FontWeight == null;
+ }
+
+ /// <summary>
+ /// Applies a highlighting color to a visual line element.
+ /// </summary>
+ protected virtual void ApplyColorToElement(VisualLineElement element, HighlightingColor color)
+ {
+ if (color.Foreground != null) {
+ Brush b = color.Foreground.GetBrush(CurrentContext);
+ if (b != null)
+ element.TextRunProperties.SetForegroundBrush(b);
+ }
+ if (color.Background != null) {
+ Brush b = color.Background.GetBrush(CurrentContext);
+ if (b != null)
+ element.BackgroundBrush = b;
+ }
+ if (color.FontStyle != null || color.FontWeight != null) {
+ Typeface tf = element.TextRunProperties.Typeface;
+ element.TextRunProperties.SetTypeface(new Typeface(
+ tf.FontFamily,
+ color.FontStyle ?? tf.Style,
+ color.FontWeight ?? tf.Weight,
+ tf.Stretch
+ ));
+ }
+ }
+
+ /// <summary>
+ /// This class is responsible for telling the TextView to redraw lines when the highlighting state has changed.
+ /// </summary>
+ /// <remarks>
+ /// Creation of a VisualLine triggers the syntax highlighter (which works on-demand), so it says:
+ /// Hey, the user typed "/*". Don't just recreate that line, but also the next one
+ /// because my highlighting state (at end of line) changed!
+ /// </remarks>
+ sealed class TextViewDocumentHighlighter : DocumentHighlighter
+ {
+ readonly HighlightingColorizer colorizer;
+ readonly TextView textView;
+
+ public TextViewDocumentHighlighter(HighlightingColorizer colorizer, TextView textView, TextDocument document, HighlightingRuleSet baseRuleSet)
+ : base(document, baseRuleSet)
+ {
+ Debug.Assert(colorizer != null);
+ Debug.Assert(textView != null);
+ this.colorizer = colorizer;
+ this.textView = textView;
+ }
+
+ protected override void OnHighlightStateChanged(DocumentLine line, int lineNumber)
+ {
+ base.OnHighlightStateChanged(line, lineNumber);
+ if (colorizer.lineNumberBeingColorized != lineNumber) {
+ // Ignore notifications for any line except the one we're interested in.
+ // This improves the performance as Redraw() can take quite some time when called repeatedly
+ // while scanning the document (above the visible area) for highlighting changes.
+ return;
+ }
+ if (textView.Document != this.Document) {
+ // May happen if document on text view was changed but some user code is still using the
+ // existing IHighlighter instance.
+ return;
+ }
+
+ // The user may have inserted "/*" into the current line, and so far only that line got redrawn.
+ // So when the highlighting state is changed, we issue a redraw for the line immediately below.
+ // If the highlighting state change applies to the lines below, too, the construction of each line
+ // will invalidate the next line, and the construction pass will regenerate all lines.
+
+ Debug.WriteLine("OnHighlightStateChanged forces redraw of line " + (lineNumber + 1));
+
+ // If the VisualLine construction is in progress, we have to avoid sending redraw commands for
+ // anything above the line currently being constructed.
+ // It takes some explanation to see why this cannot happen.
+ // VisualLines always get constructed from top to bottom.
+ // Each VisualLine construction calls into the highlighter and thus forces an update of the
+ // highlighting state for all lines up to the one being constructed.
+
+ // To guarantee that we don't redraw lines we just constructed, we need to show that when
+ // a VisualLine is being reused, the highlighting state at that location is still up-to-date.
+
+ // This isn't exactly trivial and the initial implementation was incorrect in the presence of external document changes
+ // (e.g. split view).
+
+ // For the first line in the view, the TextView.VisualLineConstructionStarting event is used to check that the
+ // highlighting state is up-to-date. If it isn't, this method will be executed, and it'll mark the first line
+ // in the view as requiring a redraw. This is safely possible because that event occurs before any lines are reused.
+
+ // Once we take care of the first visual line, we won't get in trouble with other lines due to the top-to-bottom
+ // construction process.
+
+ // We'll prove that: if line N is being reused, then the highlighting state is up-to-date until (end of) line N-1.
+
+ // Start of induction: the first line in view is reused only if the highlighting state was up-to-date
+ // until line N-1 (no change detected in VisualLineConstructionStarting event).
+
+ // Induction step:
+ // If another line N+1 is being reused, then either
+ // a) the previous line (the visual line containing document line N) was newly constructed
+ // or b) the previous line was reused
+ // In case a, the construction updated the highlighting state. This means the stack at end of line N is up-to-date.
+ // In case b, the highlighting state at N-1 was up-to-date, and the text of line N was not changed.
+ // (if the text was changed, the line could not have been reused).
+ // From this follows that the highlighting state at N is still up-to-date.
+
+ // The above proof holds even in the presence of folding: folding only ever hides text in the middle of a visual line.
+ // Our Colorize-override ensures that the highlighting state is always updated for the LastDocumentLine,
+ // so it will always invalidate the next visual line when a folded line is constructed
+ // and the highlighting stack has changed.
+
+ textView.Redraw(line.NextLine, DispatcherPriority.Normal);
+
+ /*
+ * Meta-comment: "why does this have to be so complicated?"
+ *
+ * The problem is that I want to re-highlight only on-demand and incrementally;
+ * and at the same time only repaint changed lines.
+ * So the highlighter and the VisualLine construction both have to run in a single pass.
+ * The highlighter must take care that it never touches already constructed visual lines;
+ * if it detects that something must be redrawn because the highlighting state changed,
+ * it must do so early enough in the construction process.
+ * But doing it too early means it doesn't have the information necessary to re-highlight and redraw only the desired parts.
+ */
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionInvalidException.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionInvalidException.cs
new file mode 100644
index 000000000..dccd6a481
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionInvalidException.cs
@@ -0,0 +1,43 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// Indicates that the highlighting definition that was tried to load was invalid.
+ /// </summary>
+ [Serializable()]
+ public class HighlightingDefinitionInvalidException : Exception
+ {
+ /// <summary>
+ /// Creates a new HighlightingDefinitionInvalidException instance.
+ /// </summary>
+ public HighlightingDefinitionInvalidException() : base()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new HighlightingDefinitionInvalidException instance.
+ /// </summary>
+ public HighlightingDefinitionInvalidException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new HighlightingDefinitionInvalidException instance.
+ /// </summary>
+ public HighlightingDefinitionInvalidException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new HighlightingDefinitionInvalidException instance.
+ /// </summary>
+ protected HighlightingDefinitionInvalidException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionTypeConverter.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionTypeConverter.cs
new file mode 100644
index 000000000..7463d9353
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingDefinitionTypeConverter.cs
@@ -0,0 +1,54 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// Converts between strings and <see cref="IHighlightingDefinition"/> by treating the string as the definition name
+ /// and calling <c>HighlightingManager.Instance.<see cref="HighlightingManager.GetDefinition">GetDefinition</see>(name)</c>.
+ /// </summary>
+ public sealed class HighlightingDefinitionTypeConverter : TypeConverter
+ {
+ /// <inheritdoc/>
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ if (sourceType == typeof(string))
+ return true;
+ else
+ return base.CanConvertFrom(context, sourceType);
+ }
+
+ /// <inheritdoc/>
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ string definitionName = value as string;
+ if (definitionName != null)
+ return HighlightingManager.Instance.GetDefinition(definitionName);
+ else
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ /// <inheritdoc/>
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ return true;
+ else
+ return base.CanConvertTo(context, destinationType);
+ }
+
+ /// <inheritdoc/>
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ IHighlightingDefinition definition = value as IHighlightingDefinition;
+ if (definition != null && destinationType == typeof(string))
+ return definition.Name;
+ else
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingManager.cs
new file mode 100644
index 000000000..8db4787d7
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingManager.cs
@@ -0,0 +1,290 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Xml;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// Manages a list of syntax highlighting definitions.
+ /// </summary>
+ /// <remarks>
+ /// All memers on this class (including instance members) are thread-safe.
+ /// </remarks>
+ public class HighlightingManager : IHighlightingDefinitionReferenceResolver
+ {
+ sealed class DelayLoadedHighlightingDefinition : IHighlightingDefinition2
+ {
+ readonly object lockObj = new object();
+ readonly string name;
+ Func<IHighlightingDefinition> lazyLoadingFunction;
+ IHighlightingDefinition definition;
+ Exception storedException;
+
+ public DelayLoadedHighlightingDefinition(string name, Func<IHighlightingDefinition> lazyLoadingFunction)
+ {
+ this.name = name;
+ this.lazyLoadingFunction = lazyLoadingFunction;
+ }
+
+ public string Name {
+ get {
+ if (name != null)
+ return name;
+ else
+ return GetDefinition().Name;
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "The exception will be rethrown")]
+ IHighlightingDefinition GetDefinition()
+ {
+ Func<IHighlightingDefinition> func;
+ lock (lockObj) {
+ if (this.definition != null)
+ return this.definition;
+ func = this.lazyLoadingFunction;
+ }
+ Exception exception = null;
+ IHighlightingDefinition def = null;
+ try {
+ using (var busyLock = BusyManager.Enter(this)) {
+ if (!busyLock.Success)
+ throw new InvalidOperationException("Tried to create delay-loaded highlighting definition recursively. Make sure the are no cyclic references between the highlighting definitions.");
+ def = func();
+ }
+ if (def == null)
+ throw new InvalidOperationException("Function for delay-loading highlighting definition returned null");
+ } catch (Exception ex) {
+ exception = ex;
+ }
+ lock (lockObj) {
+ this.lazyLoadingFunction = null;
+ if (this.definition == null && this.storedException == null) {
+ this.definition = def;
+ this.storedException = exception;
+ }
+ if (this.storedException != null)
+ throw new HighlightingDefinitionInvalidException("Error delay-loading highlighting definition", this.storedException);
+ return this.definition;
+ }
+ }
+
+ public HighlightingRuleSet MainRuleSet {
+ get {
+ return GetDefinition().MainRuleSet;
+ }
+ }
+
+ public HighlightingRuleSet GetNamedRuleSet(string name)
+ {
+ return GetDefinition().GetNamedRuleSet(name);
+ }
+
+ public HighlightingColor GetNamedColor(string name)
+ {
+ return GetDefinition().GetNamedColor(name);
+ }
+
+ public IEnumerable<HighlightingColor> NamedHighlightingColors {
+ get {
+ return GetDefinition().NamedHighlightingColors;
+ }
+ }
+
+ public override string ToString()
+ {
+ return this.Name;
+ }
+
+ public IDictionary<string, string> Properties {
+ get {
+ var def = GetDefinition() as IHighlightingDefinition2;
+ if (def != null)
+ return def.Properties;
+ return null;
+ }
+ }
+ }
+
+ readonly object lockObj = new object();
+ Dictionary<string, IHighlightingDefinition> highlightingsByName = new Dictionary<string, IHighlightingDefinition>();
+ Dictionary<string, IHighlightingDefinition> highlightingsByExtension = new Dictionary<string, IHighlightingDefinition>(StringComparer.OrdinalIgnoreCase);
+ List<IHighlightingDefinition> allHighlightings = new List<IHighlightingDefinition>();
+
+ /// <summary>
+ /// Gets a highlighting definition by name.
+ /// Returns null if the definition is not found.
+ /// </summary>
+ public IHighlightingDefinition GetDefinition(string name)
+ {
+ lock (lockObj) {
+ IHighlightingDefinition rh;
+ if (highlightingsByName.TryGetValue(name, out rh))
+ return rh;
+ else
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Gets a copy of all highlightings.
+ /// </summary>
+ public ReadOnlyCollection<IHighlightingDefinition> HighlightingDefinitions {
+ get {
+ lock (lockObj) {
+ return Array.AsReadOnly(allHighlightings.ToArray());
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the names of the registered highlightings.
+ /// </summary>
+ [ObsoleteAttribute("Use the HighlightingDefinitions property instead.")]
+ public IEnumerable<string> HighlightingNames {
+ get {
+ lock (lockObj) {
+ return new List<string>(highlightingsByName.Keys);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a highlighting definition by extension.
+ /// Returns null if the definition is not found.
+ /// </summary>
+ public IHighlightingDefinition GetDefinitionByExtension(string extension)
+ {
+ lock (lockObj) {
+ IHighlightingDefinition rh;
+ if (highlightingsByExtension.TryGetValue(extension, out rh))
+ return rh;
+ else
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Registers a highlighting definition.
+ /// </summary>
+ /// <param name="name">The name to register the definition with.</param>
+ /// <param name="extensions">The file extensions to register the definition for.</param>
+ /// <param name="highlighting">The highlighting definition.</param>
+ public void RegisterHighlighting(string name, string[] extensions, IHighlightingDefinition highlighting)
+ {
+ if (highlighting == null)
+ throw new ArgumentNullException("highlighting");
+
+ lock (lockObj) {
+ allHighlightings.Add(highlighting);
+ if (name != null) {
+ highlightingsByName[name] = highlighting;
+ }
+ if (extensions != null) {
+ foreach (string ext in extensions) {
+ highlightingsByExtension[ext] = highlighting;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Registers a highlighting definition.
+ /// </summary>
+ /// <param name="name">The name to register the definition with.</param>
+ /// <param name="extensions">The file extensions to register the definition for.</param>
+ /// <param name="lazyLoadedHighlighting">A function that loads the highlighting definition.</param>
+ public void RegisterHighlighting(string name, string[] extensions, Func<IHighlightingDefinition> lazyLoadedHighlighting)
+ {
+ if (lazyLoadedHighlighting == null)
+ throw new ArgumentNullException("lazyLoadedHighlighting");
+ RegisterHighlighting(name, extensions, new DelayLoadedHighlightingDefinition(name, lazyLoadedHighlighting));
+ }
+
+ /// <summary>
+ /// Gets the default HighlightingManager instance.
+ /// The default HighlightingManager comes with built-in highlightings.
+ /// </summary>
+ public static HighlightingManager Instance {
+ get {
+ return DefaultHighlightingManager.Instance;
+ }
+ }
+
+ internal sealed class DefaultHighlightingManager : HighlightingManager
+ {
+ public new static readonly DefaultHighlightingManager Instance = new DefaultHighlightingManager();
+
+ public DefaultHighlightingManager()
+ {
+ Resources.RegisterBuiltInHighlightings(this);
+ }
+
+ // Registering a built-in highlighting
+ internal void RegisterHighlighting(string name, string[] extensions, string resourceName)
+ {
+ try {
+ #if DEBUG
+ // don't use lazy-loading in debug builds, show errors immediately
+ Xshd.XshdSyntaxDefinition xshd;
+ using (Stream s = Resources.OpenStream(resourceName)) {
+ using (XmlTextReader reader = new XmlTextReader(s)) {
+ xshd = Xshd.HighlightingLoader.LoadXshd(reader, false);
+ }
+ }
+ Debug.Assert(name == xshd.Name);
+ if (extensions != null)
+ Debug.Assert(System.Linq.Enumerable.SequenceEqual(extensions, xshd.Extensions));
+ else
+ Debug.Assert(xshd.Extensions.Count == 0);
+
+ // round-trip xshd:
+// string resourceFileName = Path.Combine(Path.GetTempPath(), resourceName);
+// using (XmlTextWriter writer = new XmlTextWriter(resourceFileName, System.Text.Encoding.UTF8)) {
+// writer.Formatting = Formatting.Indented;
+// new Xshd.SaveXshdVisitor(writer).WriteDefinition(xshd);
+// }
+// using (FileStream fs = File.Create(resourceFileName + ".bin")) {
+// new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(fs, xshd);
+// }
+// using (FileStream fs = File.Create(resourceFileName + ".compiled")) {
+// new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(fs, Xshd.HighlightingLoader.Load(xshd, this));
+// }
+
+ RegisterHighlighting(name, extensions, Xshd.HighlightingLoader.Load(xshd, this));
+ #else
+ RegisterHighlighting(name, extensions, LoadHighlighting(resourceName));
+ #endif
+ } catch (HighlightingDefinitionInvalidException ex) {
+ throw new InvalidOperationException("The built-in highlighting '" + name + "' is invalid.", ex);
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode",
+ Justification = "LoadHighlighting is used only in release builds")]
+ Func<IHighlightingDefinition> LoadHighlighting(string resourceName)
+ {
+ Func<IHighlightingDefinition> func = delegate {
+ Xshd.XshdSyntaxDefinition xshd;
+ using (Stream s = Resources.OpenStream(resourceName)) {
+ using (XmlTextReader reader = new XmlTextReader(s)) {
+ // in release builds, skip validating the built-in highlightings
+ xshd = Xshd.HighlightingLoader.LoadXshd(reader, true);
+ }
+ }
+ return Xshd.HighlightingLoader.Load(xshd, this);
+ };
+ return func;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRule.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRule.cs
new file mode 100644
index 000000000..18edf4974
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRule.cs
@@ -0,0 +1,31 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text.RegularExpressions;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A highlighting rule.
+ /// </summary>
+ [Serializable]
+ public class HighlightingRule
+ {
+ /// <summary>
+ /// Gets/Sets the regular expression for the rule.
+ /// </summary>
+ public Regex Regex { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the highlighting color.
+ /// </summary>
+ public HighlightingColor Color { get; set; }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[" + GetType().Name + " " + Regex + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRuleSet.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRuleSet.cs
new file mode 100644
index 000000000..f887b9d54
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingRuleSet.cs
@@ -0,0 +1,46 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A highlighting rule set describes a set of spans that are valid at a given code location.
+ /// </summary>
+ [Serializable]
+ public class HighlightingRuleSet
+ {
+ /// <summary>
+ /// Creates a new RuleSet instance.
+ /// </summary>
+ public HighlightingRuleSet()
+ {
+ this.Spans = new NullSafeCollection<HighlightingSpan>();
+ this.Rules = new NullSafeCollection<HighlightingRule>();
+ }
+
+ /// <summary>
+ /// Gets/Sets the name of the rule set.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets the list of spans.
+ /// </summary>
+ public IList<HighlightingSpan> Spans { get; private set; }
+
+ /// <summary>
+ /// Gets the list of rules.
+ /// </summary>
+ public IList<HighlightingRule> Rules { get; private set; }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[" + GetType().Name + " " + Name + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingSpan.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingSpan.cs
new file mode 100644
index 000000000..b57e70a93
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HighlightingSpan.cs
@@ -0,0 +1,64 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text.RegularExpressions;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A highlighting span is a region with start+end expression that has a different RuleSet inside
+ /// and colors the region.
+ /// </summary>
+ [Serializable]
+ public class HighlightingSpan
+ {
+ /// <summary>
+ /// Gets/Sets the start expression.
+ /// </summary>
+ public Regex StartExpression { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the end expression.
+ /// </summary>
+ public Regex EndExpression { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the rule set that applies inside this span.
+ /// </summary>
+ public HighlightingRuleSet RuleSet { get; set; }
+
+ /// <summary>
+ /// Gets the color used for the text matching the start expression.
+ /// </summary>
+ public HighlightingColor StartColor { get; set; }
+
+ /// <summary>
+ /// Gets the color used for the text between start and end.
+ /// </summary>
+ public HighlightingColor SpanColor { get; set; }
+
+ /// <summary>
+ /// Gets the color used for the text matching the end expression.
+ /// </summary>
+ public HighlightingColor EndColor { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether the span color includes the start.
+ /// The default is <c>false</c>.
+ /// </summary>
+ public bool SpanColorIncludesStart { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether the span color includes the end.
+ /// The default is <c>false</c>.
+ /// </summary>
+ public bool SpanColorIncludesEnd { get; set; }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return "[" + GetType().Name + " Start=" + StartExpression + ", End=" + EndExpression + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HtmlClipboard.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HtmlClipboard.cs
new file mode 100644
index 000000000..a656308b7
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/HtmlClipboard.cs
@@ -0,0 +1,201 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Windows;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// Allows copying HTML text to the clipboard.
+ /// </summary>
+ public static class HtmlClipboard
+ {
+ /// <summary>
+ /// Builds a header for the CF_HTML clipboard format.
+ /// </summary>
+ static string BuildHeader(int startHTML, int endHTML, int startFragment, int endFragment)
+ {
+ StringBuilder b = new StringBuilder();
+ b.AppendLine("Version:0.9");
+ b.AppendLine("StartHTML:" + startHTML.ToString("d8", CultureInfo.InvariantCulture));
+ b.AppendLine("EndHTML:" + endHTML.ToString("d8", CultureInfo.InvariantCulture));
+ b.AppendLine("StartFragment:" + startFragment.ToString("d8", CultureInfo.InvariantCulture));
+ b.AppendLine("EndFragment:" + endFragment.ToString("d8", CultureInfo.InvariantCulture));
+ return b.ToString();
+ }
+
+ /// <summary>
+ /// Sets the TextDataFormat.Html on the data object to the specified html fragment.
+ /// This helper methods takes care of creating the necessary CF_HTML header.
+ /// </summary>
+ public static void SetHtml(DataObject dataObject, string htmlFragment)
+ {
+ if (dataObject == null)
+ throw new ArgumentNullException("dataObject");
+ if (htmlFragment == null)
+ throw new ArgumentNullException("htmlFragment");
+
+ string htmlStart = @"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">" + Environment.NewLine
+ + "<HTML>" + Environment.NewLine
+ + "<BODY>" + Environment.NewLine
+ + "<!--StartFragment-->" + Environment.NewLine;
+ string htmlEnd = "<!--EndFragment-->" + Environment.NewLine + "</BODY>" + Environment.NewLine + "</HTML>" + Environment.NewLine;
+ string dummyHeader = BuildHeader(0, 0, 0, 0);
+ // the offsets are stored as UTF-8 bytes (see CF_HTML documentation)
+ int startHTML = dummyHeader.Length;
+ int startFragment = startHTML + htmlStart.Length;
+ int endFragment = startFragment + Encoding.UTF8.GetByteCount(htmlFragment);
+ int endHTML = endFragment + htmlEnd.Length;
+ string cf_html = BuildHeader(startHTML, endHTML, startFragment, endFragment) + htmlStart + htmlFragment + htmlEnd;
+ Debug.WriteLine(cf_html);
+ dataObject.SetText(cf_html, TextDataFormat.Html);
+ }
+
+ /// <summary>
+ /// 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. <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, 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);
+
+ StringBuilder html = new StringBuilder();
+ int segmentEndOffset = segment.EndOffset;
+ DocumentLine line = document.GetLineByOffset(segment.Offset);
+ while (line != null && line.Offset < segmentEndOffset) {
+ HighlightedLine highlightedLine;
+ if (highlighter != null)
+ highlightedLine = highlighter.HighlightLine(line.LineNumber);
+ else
+ highlightedLine = new HighlightedLine(document, line);
+ SimpleSegment s = segment.GetOverlap(line);
+ if (html.Length > 0)
+ html.AppendLine("<br>");
+ html.Append(highlightedLine.ToHtml(s.Offset, s.EndOffset, options));
+ line = line.NextLine;
+ }
+ return html.ToString();
+ }
+
+ /// <summary>
+ /// Escapes text and writes the result to the StringBuilder.
+ /// </summary>
+ internal static void EscapeHtml(StringWriter w, string text, HtmlOptions options)
+ {
+ int spaceCount = -1;
+ foreach (char c in text) {
+ if (c == ' ') {
+ if (spaceCount < 0)
+ w.Write("&nbsp;");
+ else
+ spaceCount++;
+ } else if (c == '\t') {
+ if (spaceCount < 0)
+ spaceCount = 0;
+ spaceCount += options.TabSize;
+ } else {
+ if (spaceCount == 1) {
+ w.Write(' ');
+ } else if (spaceCount >= 1) {
+ for (int i = 0; i < spaceCount; i++) {
+ w.Write("&nbsp;");
+ }
+ }
+ spaceCount = 0;
+ switch (c) {
+ case '<':
+ w.Write("&lt;");
+ break;
+ case '>':
+ w.Write("&gt;");
+ break;
+ case '&':
+ w.Write("&amp;");
+ break;
+ case '"':
+ w.Write("&quot;");
+ break;
+ default:
+ w.Write(c);
+ break;
+ }
+ }
+ }
+ for (int i = 0; i < spaceCount; i++) {
+ w.Write("&nbsp;");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Holds options for converting text to HTML.
+ /// </summary>
+ public class HtmlOptions
+ {
+ /// <summary>
+ /// Creates a default HtmlOptions instance.
+ /// </summary>
+ public HtmlOptions()
+ {
+ this.TabSize = 4;
+ }
+
+ /// <summary>
+ /// Creates a new HtmlOptions instance that copies applicable options from the <see cref="TextEditorOptions"/>.
+ /// </summary>
+ public HtmlOptions(TextEditorOptions options)
+ : this()
+ {
+ if (options == null)
+ throw new ArgumentNullException("options");
+ this.TabSize = options.IndentationSize;
+ }
+
+ /// <summary>
+ /// The amount of spaces a tab gets converted to.
+ /// </summary>
+ public int TabSize { get; set; }
+
+ /// <summary>
+ /// Writes the HTML attribute for the style to the text writer.
+ /// </summary>
+ public virtual void WriteStyleAttributeForColor(TextWriter writer, HighlightingColor color)
+ {
+ if (writer == null)
+ throw new ArgumentNullException("writer");
+ if (color == null)
+ throw new ArgumentNullException("color");
+ writer.Write(" style=\"");
+ writer.Write(color.ToCss());
+ writer.Write("\"");
+ }
+
+ /// <summary>
+ /// Gets whether the color needs to be written out to HTML.
+ /// </summary>
+ public virtual bool ColorNeedsSpanForStyling(HighlightingColor color)
+ {
+ if (color == null)
+ throw new ArgumentNullException("color");
+ return !string.IsNullOrEmpty(color.ToCss());
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlighter.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlighter.cs
new file mode 100644
index 000000000..1b5cab138
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlighter.cs
@@ -0,0 +1,35 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.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/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinition.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinition.cs
new file mode 100644
index 000000000..0b42cd426
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinition.cs
@@ -0,0 +1,54 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// A highlighting definition.
+ /// </summary>
+ [TypeConverter(typeof(HighlightingDefinitionTypeConverter))]
+ public interface IHighlightingDefinition
+ {
+ /// <summary>
+ /// Gets the name of the highlighting definition.
+ /// </summary>
+ string Name { get; }
+
+ /// <summary>
+ /// Gets the main rule set.
+ /// </summary>
+ HighlightingRuleSet MainRuleSet { get; }
+
+ /// <summary>
+ /// Gets a rule set by name.
+ /// </summary>
+ /// <returns>The rule set, or null if it is not found.</returns>
+ HighlightingRuleSet GetNamedRuleSet(string name);
+
+ /// <summary>
+ /// Gets a named highlighting color.
+ /// </summary>
+ /// <returns>The highlighting color, or null if it is not found.</returns>
+ HighlightingColor GetNamedColor(string name);
+
+ /// <summary>
+ /// Gets the list of named highlighting colors.
+ /// </summary>
+ IEnumerable<HighlightingColor> NamedHighlightingColors { get; }
+ }
+
+ /// <summary>
+ /// Extension of IHighlightingDefinition to avoid breaking changes in the API.
+ /// </summary>
+ public interface IHighlightingDefinition2 : IHighlightingDefinition
+ {
+ /// <summary>
+ /// Gets the list of properties.
+ /// </summary>
+ IDictionary<string, string> Properties { get; }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinitionReferenceResolver.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinitionReferenceResolver.cs
new file mode 100644
index 000000000..d378ae1b7
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/IHighlightingDefinitionReferenceResolver.cs
@@ -0,0 +1,18 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ /// <summary>
+ /// Interface for resolvers that can solve cross-definition references.
+ /// </summary>
+ public interface IHighlightingDefinitionReferenceResolver
+ {
+ /// <summary>
+ /// Gets the highlighting definition by name, or null if it is not found.
+ /// </summary>
+ IHighlightingDefinition GetDefinition(string name);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ASPX.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ASPX.xshd
new file mode 100644
index 000000000..bd0c922ac
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ASPX.xshd
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!-- converted to AvalonEdit format by Siegfried Pammer in 2010 -->
+<SyntaxDefinition name="ASP/XHTML" extensions=".asp;.aspx;.asax;.asmx;.ascx;.master" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="ASPSectionStartEndTags" foreground="Black" background="Yellow" exampleText="&lt;% System.Console.WriteLine(&quot;Hello World!&quot;); %&gt;" />
+ <Color name="ASPSection" foreground="Black" background="#FFF7F2E3" exampleText="&lt;% System.Console.WriteLine(&quot;Hello World!&quot;); %&gt;" />
+ <RuleSet ignoreCase="true">
+ <Span ruleSet="ASP" multiline="true">
+ <Begin color="ASPSectionStartEndTags">&lt;%</Begin>
+ <End color="ASPSectionStartEndTags">%&gt;</End>
+ </Span>
+ <Import ruleSet="HTML/" />
+ </RuleSet>
+ <RuleSet name="ASP">
+ <Import ruleSet="C#/" />
+ </RuleSet>
+</SyntaxDefinition>
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Boo.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Boo.xshd
new file mode 100644
index 000000000..a4e555198
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Boo.xshd
@@ -0,0 +1,212 @@
+<SyntaxDefinition name="Boo" extensions=".boo" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="Green" />
+ <Color name="String" foreground="Blue" />
+ <Color name="Regex" foreground="#FFFF6600" />
+ <RuleSet>
+ <Span color="Comment" multiline="true">
+ <Begin>"""</Begin>
+ <End>"""</End>
+ </Span>
+ <Span foreground="Gray">
+ <Begin>\#</Begin>
+ </Span>
+ <Span foreground="#FF999999">
+ <Begin>//</Begin>
+ </Span>
+ <Span color="Comment" ruleSet="comments set" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ <Span foreground="#FF993366" ruleSet="">
+ <!-- ruleSet="" reference this file's main ruleset, allowing normal boo code inside ${} -->
+ <Begin>\$\{</Begin>
+ <End>}</End>
+ </Span>
+ </RuleSet>
+ </Span>
+ <Span color="String">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Span color="Regex" multiline="true">
+ <Begin>@/</Begin>
+ <End>/</End>
+ </Span>
+ <Span color="Regex">
+ <!-- this is a span (not a rule) because " within a regex doesn't start a string literal -->
+ <Begin>/\S+/</Begin>
+ <End></End> <!-- end immediately after Begin -->
+ </Span>
+ <Keywords foreground="Black" fontWeight="bold">
+ <Word>self</Word>
+ <Word>super</Word>
+ </Keywords>
+ <Keywords foreground="DarkCyan" fontWeight="bold">
+ <Word>is</Word>
+ <Word>isa</Word>
+ <Word>and</Word>
+ <Word>or</Word>
+ <Word>not</Word>
+ </Keywords>
+ <Keywords foreground="Blue" fontWeight="bold">
+ <Word>else</Word>
+ <Word>elif</Word>
+ <Word>if</Word>
+ <Word>match</Word>
+ <Word>case</Word>
+ <Word>unless</Word>
+ <Word>otherwise</Word>
+ <Word>for</Word>
+ <Word>in</Word>
+ <Word>while</Word>
+ </Keywords>
+ <Keywords foreground="Navy">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>return</Word>
+ <Word>yield</Word>
+ <Word>goto</Word>
+ </Keywords>
+ <Keywords foreground="Teal" fontWeight="bold">
+ <Word>try</Word>
+ <Word>raise</Word>
+ <Word>ensure</Word>
+ <Word>except</Word>
+ <Word>retry</Word>
+ <Word>success</Word>
+ </Keywords>
+ <Keywords foreground="Olive">
+ <Word>fixed</Word>
+ <Word>unsafe</Word>
+ </Keywords>
+ <Keywords foreground="Purple" fontWeight="bold">
+ <Word>bool</Word>
+ <Word>double</Word>
+ <Word>single</Word>
+ <Word>byte</Word>
+ <Word>sbyte</Word>
+ <Word>short</Word>
+ <Word>ushort</Word>
+ <Word>int</Word>
+ <Word>uint</Word>
+ <Word>long</Word>
+ <Word>ulong</Word>
+ <Word>date</Word>
+ <Word>timespan</Word>
+ <Word>decimal</Word>
+ <Word>char</Word>
+ <Word>object</Word>
+ <Word>duck</Word>
+ <Word>string</Word>
+ <Word>regex</Word>
+ </Keywords>
+ <Keywords foreground="Red">
+ <Word>void</Word>
+ </Keywords>
+ <Keywords foreground="Blue" fontWeight="bold">
+ <Word>cast</Word>
+ <Word>as</Word>
+ </Keywords>
+ <Keywords foreground="Brown">
+ <Word>override</Word>
+ <Word>static</Word>
+ <Word>virtual</Word>
+ <Word>abstract</Word>
+ <Word>final</Word>
+ <Word>transient</Word>
+ <Word>partial</Word>
+ </Keywords>
+ <Keywords foreground="Blue" fontWeight="bold">
+ <Word>public</Word>
+ <Word>protected</Word>
+ <Word>private</Word>
+ <Word>internal</Word>
+ </Keywords>
+ <Keywords foreground="Green" fontWeight="bold">
+ <Word>namespace</Word>
+ <Word>import</Word>
+ <Word>from</Word>
+ </Keywords>
+ <Keywords foreground="SaddleBrown">
+ <Word>get</Word>
+ <Word>set</Word>
+ </Keywords>
+ <Keywords foreground="Black" fontWeight="bold">
+ <Word>null</Word>
+ <Word>value</Word>
+ <Word>true</Word>
+ <Word>false</Word>
+ <Word>ast</Word>
+ </Keywords>
+ <Keywords foreground="Maroon">
+ <Word>using</Word>
+ <Word>unchecked</Word>
+ <Word>checked</Word>
+ <Word>lock</Word>
+ <Word>getter</Word>
+ <Word>required</Word>
+ <Word>rawArrayIndexing</Word>
+ <Word>normalArrayIndexing</Word>
+ <Word>yieldAll</Word>
+ </Keywords>
+ <Keywords foreground="Purple">
+ <Word>assert</Word>
+ <Word>array</Word>
+ <Word>matrix</Word>
+ <Word>print</Word>
+ <Word>gets</Word>
+ <Word>prompt</Word>
+ <Word>enumerate</Word>
+ <Word>zip</Word>
+ <Word>filter</Word>
+ <Word>map</Word>
+ <Word>cat</Word>
+ <Word>__eval__</Word>
+ <Word>__switch__</Word>
+ </Keywords>
+ <Keywords foreground="Blue" fontWeight="bold">
+ <Word>constructor</Word>
+ <Word>destructor</Word>
+ <Word>def</Word>
+ <Word>include</Word>
+ <Word>event</Word>
+ <Word>ref</Word>
+ </Keywords>
+ <Keywords foreground="Gray">
+ <Word>pass</Word>
+ </Keywords>
+ <Keywords foreground="Blue" fontWeight="bold">
+ <Word>enum</Word>
+ <Word>class</Word>
+ <Word>struct</Word>
+ <Word>interface</Word>
+ <Word>mixin</Word>
+ <Word>callable</Word>
+ <Word>do</Word>
+ <Word>of</Word>
+ </Keywords>
+ <Rule foreground="MidnightBlue">[\d\w_]+(?=(\s*\())</Rule>
+ <Rule foreground="DarkBlue">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="comments set">
+ <!-- allows nested /**/ comments, coloring them Green/Teal alternately -->
+ <Span foreground="Teal" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ <RuleSet>
+ <Span color="Comment" ruleSet="comments set" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ </RuleSet>
+ </Span>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CPP-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CPP-Mode.xshd
new file mode 100644
index 000000000..f552ad413
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CPP-Mode.xshd
@@ -0,0 +1,195 @@
+<?xml version="1.0"?>
+<!-- syntaxdefinition for C/C++ 2001 by Andrea Paatz and Mike Krueger -->
+<!-- converted to AvalonEdit format by Siegfried Pammer in 2010 -->
+<SyntaxDefinition name="C++" extensions=".c;.h;.cc;.cpp;.hpp" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="Green" />
+ <Color name="Character" foreground="Fuchsia" />
+ <Color name="String" foreground="Fuchsia" />
+ <Color name="Preprocessor" foreground="Green" />
+ <Color name="Punctuation" foreground="DarkGreen" />
+ <Color name="MethodName" foreground="MidnightBlue" fontWeight="bold" />
+ <Color name="Digits" foreground="DarkBlue" />
+ <Color name="CompoundKeywords" foreground="Black" fontWeight="bold" />
+ <Color name="This" foreground="Black" fontWeight="bold" />
+ <Color name="Operators" foreground="#FF008B8B" fontWeight="bold" />
+ <Color name="Namespace" foreground="#FF008000" fontWeight="bold" />
+ <Color name="Friend" foreground="#FFA52A2A" />
+ <Color name="Modifiers" foreground="#FF0000FF" fontWeight="bold" />
+ <Color name="TypeKeywords" foreground="#FFFF0000" />
+ <Color name="BooleanConstants" foreground="#FF000000" fontWeight="bold" />
+ <Color name="Keywords" foreground="#FF0000FF" fontWeight="bold" />
+ <Color name="LoopKeywords" foreground="#FF0000FF" fontWeight="bold" />
+ <Color name="JumpKeywords" foreground="#FF000080" />
+ <Color name="ExceptionHandling" foreground="#FF008080" fontWeight="bold" />
+ <Color name="ControlFlow" foreground="#FF0000FF" fontWeight="bold" />
+ <RuleSet ignoreCase="false">
+ <Rule color="Punctuation">
+ [?,.;()\[\]{}+\-/%*&lt;&gt;^=~!&amp;]+
+ </Rule>
+ <Keywords color="CompoundKeywords">
+ <Word>__abstract</Word>
+ <Word>__box</Word>
+ <Word>__delegate</Word>
+ <Word>__gc</Word>
+ <Word>__identifier</Word>
+ <Word>__nogc</Word>
+ <Word>__pin</Word>
+ <Word>__property</Word>
+ <Word>__sealed</Word>
+ <Word>__try_cast</Word>
+ <Word>__typeof</Word>
+ <Word>__value</Word>
+ <Word>__event</Word>
+ <Word>__hook</Word>
+ <Word>__raise</Word>
+ <Word>__unhook</Word>
+ <Word>__interface</Word>
+ <Word>ref class</Word>
+ <Word>ref struct</Word>
+ <Word>value class</Word>
+ <Word>value struct</Word>
+ <Word>interface class</Word>
+ <Word>interface struct</Word>
+ <Word>enum class</Word>
+ <Word>enum struct</Word>
+ <Word>delegate</Word>
+ <Word>event</Word>
+ <Word>property</Word>
+ <Word>abstract</Word>
+ <Word>override</Word>
+ <Word>sealed</Word>
+ <Word>generic</Word>
+ <Word>where</Word>
+ <Word>finally</Word>
+ <Word>for each</Word>
+ <Word>gcnew</Word>
+ <Word>in</Word>
+ <Word>initonly</Word>
+ <Word>literal</Word>
+ <Word>nullptr</Word>
+ </Keywords>
+ <Keywords color="This">
+ <Word>this</Word>
+ </Keywords>
+ <Keywords color="Operators">
+ <Word>and</Word>
+ <Word>and_eq</Word>
+ <Word>bitand</Word>
+ <Word>bitor</Word>
+ <Word>new</Word>
+ <Word>not</Word>
+ <Word>not_eq</Word>
+ <Word>or</Word>
+ <Word>or_eq</Word>
+ <Word>xor</Word>
+ <Word>xor_eq</Word>
+ </Keywords>
+ <Keywords color="Namespace">
+ <Word>using</Word>
+ <Word>namespace</Word>
+ </Keywords>
+ <Keywords color="Friend">
+ <Word>friend</Word>
+ </Keywords>
+ <Keywords color="Modifiers">
+ <Word>private</Word>
+ <Word>protected</Word>
+ <Word>public</Word>
+ <Word>const</Word>
+ <Word>volatile</Word>
+ <Word>static</Word>
+ </Keywords>
+ <Keywords color="TypeKeywords">
+ <Word>bool</Word>
+ <Word>char</Word>
+ <Word>unsigned</Word>
+ <Word>union</Word>
+ <Word>virtual</Word>
+ <Word>double</Word>
+ <Word>float</Word>
+ <Word>short</Word>
+ <Word>signed</Word>
+ <Word>void</Word>
+ <Word>class</Word>
+ <Word>enum</Word>
+ <Word>struct</Word>
+ </Keywords>
+ <Keywords color="BooleanConstants">
+ <Word>false</Word>
+ <Word>true</Word>
+ </Keywords>
+ <Keywords color="LoopKeywords">
+ <Word>do</Word>
+ <Word>for</Word>
+ <Word>while</Word>
+ </Keywords>
+ <Keywords color="JumpKeywords">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>goto</Word>
+ <Word>return</Word>
+ </Keywords>
+ <Keywords color="ExceptionHandling">
+ <Word>catch</Word>
+ <Word>throw</Word>
+ <Word>try</Word>
+ </Keywords>
+ <Keywords color="ControlFlow">
+ <Word>case</Word>
+ <Word>else</Word>
+ <Word>if</Word>
+ <Word>switch</Word>
+ <Word>default</Word>
+ </Keywords>
+ <Keywords color="Keywords">
+ <Word>asm</Word>
+ <Word>auto</Word>
+ <Word>compl</Word>
+ <Word>mutable</Word>
+ <Word>const_cast</Word>
+ <Word>delete</Word>
+ <Word>dynamic_cast</Word>
+ <Word>explicit</Word>
+ <Word>export</Word>
+ <Word>extern</Word>
+ <Word>inline</Word>
+ <Word>int</Word>
+ <Word>long</Word>
+ <Word>operator</Word>
+ <Word>register</Word>
+ <Word>reinterpret_cast</Word>
+ <Word>sizeof</Word>
+ <Word>static_cast</Word>
+ <Word>template</Word>
+ <Word>typedef</Word>
+ <Word>typeid</Word>
+ <Word>typename</Word>
+ </Keywords>
+ <Span color="Preprocessor">
+ <Begin>\#</Begin>
+ </Span>
+ <Span color="Comment">
+ <Begin>//</Begin>
+ </Span>
+ <Span color="Comment" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Span color="Character">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Rule color="MethodName">[\d\w_]+(?=(\s*\())</Rule>
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSS-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSS-Mode.xshd
new file mode 100644
index 000000000..3ef562f0d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSS-Mode.xshd
@@ -0,0 +1,57 @@
+<SyntaxDefinition name="CSS" extensions=".css" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="Gray" />
+ <Color name="String" foreground="Green" />
+ <Color name="Selector" foreground="DarkBlue" fontWeight="bold" />
+ <Color name="Class" foreground="DarkMagenta" />
+ <Color name="Property" foreground="Red"/>
+ <Color name="Value" foreground="Blue" />
+ <Color name="Default" foreground="Pink" />
+ <Color name="CurlyBraces" foreground="Black" />
+ <Color name="Colon" foreground="Black" />
+ <RuleSet ignoreCase="true">
+ <Span color="Comment" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ <Span
+ color="Property"
+ multiline="true"
+ ruleSet="CSSBlock">
+ <Begin color="CurlyBraces">\{</Begin>
+ <End color="CurlyBraces">\}</End>
+ </Span>
+ <Span color="Class">
+ <Begin>\#</Begin>
+ <End>\s</End>
+ </Span>
+ <Rule color="Selector">[\d\w]</Rule>
+ </RuleSet>
+
+ <RuleSet name="CSSBlock">
+ <Span color="Comment" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ <Span color="Value" multiline="true">
+ <Begin color="Colon">\:</Begin>
+ <End color="CurlyBraces">\;|(?=\})</End>
+ <RuleSet>
+ <Span color="String" multiline="true">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Span color="String" multiline="true">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ </RuleSet>
+ </Span>
+ </RuleSet>
+</SyntaxDefinition>
+
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSharp-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSharp-Mode.xshd
new file mode 100644
index 000000000..2635d162c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/CSharp-Mode.xshd
@@ -0,0 +1,315 @@
+<?xml version="1.0"?>
+<SyntaxDefinition name="C#" extensions=".cs" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <!-- The named colors 'Comment' and 'String' are used in SharpDevelop to detect if a line is inside a multiline string/comment -->
+ <Color name="Comment" foreground="#57A33A" exampleText="// comment" />
+ <Color name="String" foreground="#D69D85" exampleText="string text = &quot;Hello, World!&quot;"/>
+ <Color name="Char" foreground="#D69D85" exampleText="char linefeed = '\n';"/>
+ <Color name="Preprocessor" foreground="DimGray" exampleText="#region Title" />
+ <Color name="Punctuation" exampleText="a(b.c);" />
+ <Color name="ValueTypes" foreground="#3F8FD6" exampleText="bool b = true;" />
+ <Color name="ReferenceTypes" foreground="#4EC9B0" exampleText="object o;" />
+ <Color name="MethodCall" foreground="Gainsboro" exampleText="o.ToString();"/>
+ <Color name="NumberLiteral" foreground="#B5CE8A" exampleText="3.1415f"/>
+ <Color name="ThisOrBaseReference" exampleText="this.Do(); base.Do();"/>
+ <Color name="NullOrValueKeywords" exampleText="if (value == null)"/>
+ <Color name="Keywords" foreground="#3F8FD6" exampleText="if (a) {} else {}"/>
+ <Color name="GotoKeywords" foreground="#3F8FD6" exampleText="continue; return null;"/>
+ <Color name="ContextKeywords" foreground="#3F8FD6" exampleText="var a = from x in y select z;"/>
+ <Color name="ExceptionKeywords" foreground="#3F8FD6" exampleText="try {} catch {} finally {}"/>
+ <Color name="CheckedKeyword" foreground="DarkGray" exampleText="checked {}"/>
+ <Color name="UnsafeKeywords" foreground="#3F8FD6" exampleText="unsafe { fixed (..) {} }"/>
+ <Color name="OperatorKeywords" foreground="Pink" exampleText="public static implicit operator..."/>
+ <Color name="ParameterModifiers" foreground="DeepPink" exampleText="(ref int a, params int[] b)"/>
+ <Color name="Modifiers" foreground="#3F8FD6" exampleText="static readonly int a;"/>
+ <Color name="CustomTypes" foreground="#4EC9B0" />
+ <Color name="CustomKeywords" foreground="#FAFF00" />
+ <Color name="Visibility" foreground="#3F8FD6" exampleText="public override void ToString();"/>
+ <Color name="NamespaceKeywords" foreground="#569CD6" exampleText="namespace A.B { using System; }"/>
+ <Color name="GetSetAddRemove" foreground="#3F8FD6" exampleText="int Prop { get; set; }"/>
+ <Color name="TrueFalse" foreground="#3F8FD6" exampleText="b = false; a = true;" />
+ <Color name="TypeKeywords" foreground="#3F8FD6" exampleText="if (x is int) { a = x as int; type = typeof(int); size = sizeof(int); c = new object(); }"/>
+
+ <!--<Property name="DocCommentMarker" value="///" />-->
+
+ <RuleSet name="CommentMarkerSet">
+ <Keywords foreground="Red">
+ <Word>TODO</Word>
+ <Word>FIXME</Word>
+ </Keywords>
+ <Keywords foreground="#E0E000">
+ <Word>HACK</Word>
+ <Word>UNDONE</Word>
+ </Keywords>
+ </RuleSet>
+
+ <!-- This is the main ruleset. -->
+ <RuleSet>
+ <Span color="Preprocessor">
+ <Begin>\#</Begin>
+ <RuleSet name="PreprocessorSet">
+ <Span> <!-- preprocessor directives that allows comments -->
+ <Begin >
+ (define|undef|if|elif|else|endif|line)\b
+ </Begin>
+ <RuleSet>
+ <Span color="Comment" ruleSet="CommentMarkerSet">
+ <Begin>//</Begin>
+ </Span>
+ </RuleSet>
+ </Span>
+ <Span> <!-- preprocessor directives that don't allow comments -->
+ <Begin >
+ (region|endregion|error|warning|pragma)\b
+ </Begin>
+ </Span>
+ </RuleSet>
+ </Span>
+
+ <!--<Span color="Comment">
+ <Begin color="XmlDoc/DocComment">///</Begin>
+ <RuleSet>
+ <Import ruleSet="XmlDoc/DocCommentSet"/>
+ <Import ruleSet="CommentMarkerSet"/>
+ </RuleSet>
+ </Span>-->
+
+ <Span color="Comment" ruleSet="CommentMarkerSet">
+ <Begin>//</Begin>
+ </Span>
+
+ <Span color="Comment" ruleSet="CommentMarkerSet" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <Span color="Char">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <Span color="String" multiline="true">
+ <Begin>@"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin='""' end=""/>
+ </RuleSet>
+ </Span>
+
+ <!-- don't highlight "@int" as keyword -->
+ <Rule>
+ @[\w\d_]+
+ </Rule>
+
+ <Keywords color="CustomTypes">
+ <Word>String</Word>
+ <Word>Thread</Word>
+ <Word>DateTime</Word>
+ <Word>Task</Word>
+ <Word>Bitmap</Word>
+ <Word>BitmapSource</Word>
+ <Word>TimeSpan</Word>
+ <Word>Dispatcher</Word>
+ <Word>StubManager</Word>
+ <Word>Int32</Word>
+ <Word>Double</Word>
+ </Keywords>
+
+ <Keywords color="CustomKeywords">
+ <Word>include</Word>
+ <Word>import</Word>
+ </Keywords>
+
+ <Keywords color="ThisOrBaseReference">
+ <Word>this</Word>
+ <Word>base</Word>
+ </Keywords>
+
+ <Keywords color="TypeKeywords">
+ <Word>as</Word>
+ <Word>is</Word>
+ <Word>new</Word>
+ <Word>sizeof</Word>
+ <Word>typeof</Word>
+ <Word>stackalloc</Word>
+ </Keywords>
+
+ <Keywords color="TrueFalse">
+ <Word>true</Word>
+ <Word>false</Word>
+ </Keywords>
+
+ <Keywords color="Keywords">
+ <Word>else</Word>
+ <Word>if</Word>
+ <Word>switch</Word>
+ <Word>case</Word>
+ <Word>default</Word>
+ <Word>do</Word>
+ <Word>for</Word>
+ <Word>foreach</Word>
+ <Word>in</Word>
+ <Word>while</Word>
+ <Word>lock</Word>
+ </Keywords>
+
+ <Keywords color="GotoKeywords">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>goto</Word>
+ <Word>return</Word>
+ </Keywords>
+
+ <Keywords color="ContextKeywords">
+ <Word>yield</Word>
+ <Word>partial</Word>
+ <Word>class</Word>
+ <Word>global</Word>
+ <Word>where</Word>
+ <Word>select</Word>
+ <Word>group</Word>
+ <Word>by</Word>
+ <Word>into</Word>
+ <Word>from</Word>
+ <Word>ascending</Word>
+ <Word>descending</Word>
+ <Word>orderby</Word>
+ <Word>let</Word>
+ <Word>join</Word>
+ <Word>on</Word>
+ <Word>equals</Word>
+ <Word>var</Word>
+ <Word>dynamic</Word>
+ <Word>await</Word>
+ <Word>void</Word>
+ </Keywords>
+
+ <Keywords color="ExceptionKeywords">
+ <Word>try</Word>
+ <Word>throw</Word>
+ <Word>catch</Word>
+ <Word>finally</Word>
+ </Keywords>
+
+ <Keywords color="CheckedKeyword">
+ <Word>checked</Word>
+ <Word>unchecked</Word>
+ </Keywords>
+
+ <Keywords color="UnsafeKeywords">
+ <Word>fixed</Word>
+ <Word>unsafe</Word>
+ </Keywords>
+
+ <Keywords color="ValueTypes">
+ <Word>bool</Word>
+ <Word>byte</Word>
+ <Word>char</Word>
+ <Word>decimal</Word>
+ <Word>double</Word>
+ <Word>enum</Word>
+ <Word>float</Word>
+ <Word>int</Word>
+ <Word>string</Word>
+ <Word>long</Word>
+ <Word>sbyte</Word>
+ <Word>short</Word>
+ <Word>struct</Word>
+ <Word>uint</Word>
+ <Word>ushort</Word>
+ <Word>ulong</Word>
+ </Keywords>
+
+ <Keywords color="ReferenceTypes">
+ <Word>Object</Word>
+ <Word>String</Word>
+ <Word>Int32</Word>
+ <Word>Int64</Word>
+ <Word>Double</Word>
+ </Keywords>
+
+ <Keywords color="OperatorKeywords">
+ <Word>explicit</Word>
+ <Word>implicit</Word>
+ <Word>operator</Word>
+ </Keywords>
+
+ <Keywords color="ParameterModifiers">
+ <Word>params</Word>
+ <Word>ref</Word>
+ <Word>out</Word>
+ </Keywords>
+
+ <Keywords color="Modifiers">
+ <Word>abstract</Word>
+ <Word>const</Word>
+ <Word>event</Word>
+ <Word>extern</Word>
+ <Word>override</Word>
+ <Word>readonly</Word>
+ <Word>sealed</Word>
+ <Word>static</Word>
+ <Word>virtual</Word>
+ <Word>volatile</Word>
+ <Word>async</Word>
+ </Keywords>
+
+ <Keywords color="Visibility">
+ <Word>public</Word>
+ <Word>protected</Word>
+ <Word>private</Word>
+ <Word>internal</Word>
+ </Keywords>
+
+ <Keywords color="NamespaceKeywords">
+ <Word>namespace</Word>
+ <Word>using</Word>
+ </Keywords>
+
+ <Keywords color="GetSetAddRemove">
+ <Word>get</Word>
+ <Word>set</Word>
+ <Word>add</Word>
+ <Word>remove</Word>
+ </Keywords>
+
+ <Keywords color="NullOrValueKeywords">
+ <Word>null</Word>
+ <Word>value</Word>
+ </Keywords>
+
+ <!-- Mark previous rule-->
+ <Rule color="MethodCall">
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\() # followed by (
+ </Rule>
+
+ <!-- Digits -->
+ <Rule color="NumberLiteral">
+ \b0[xX][0-9a-fA-F]+ # hex number
+ |
+ ( \b\d+(\.[0-9]+)? #number with optional floating point
+ | \.[0-9]+ #or just starting with floating point
+ )
+ ([eE][+-]?[0-9]+)? # optional exponent
+ </Rule>
+
+ <Rule color="Punctuation">
+ [?,.;()\[\]{}+\-/%*&lt;&gt;^+~!|&amp;]+
+ </Rule>
+ </RuleSet>
+</SyntaxDefinition>
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Coco-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Coco-Mode.xshd
new file mode 100644
index 000000000..9395198b5
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Coco-Mode.xshd
@@ -0,0 +1,74 @@
+<?xml version="1.0" ?>
+<!-- syntaxdefinition for Coco/R 2003 by Mike Krueger -->
+<!-- converted to AvalonEdit format by Siegfried Pammer in 2010 -->
+<SyntaxDefinition name="Coco" extensions=".atg" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="#FF808080" fontStyle="italic" />
+ <Color name="Punctuation" foreground="#FF000000" fontWeight="bold" />
+ <Color name="Keywords" foreground="#FF0000FF" fontWeight="bold" />
+ <Color name="String" foreground="#FF006400" />
+ <RuleSet ignoreCase="false">
+ <Rule color="Punctuation">
+ [{}\(\)\[\]|+\-=\.]+
+ </Rule>
+ <Keywords color="Keywords">
+ <Word>ANY</Word>
+ <Word>CHARACTERS</Word>
+ <Word>COMMENTS</Word>
+ <Word>COMPILER</Word>
+ <Word>CONTEXT</Word>
+ <Word>END</Word>
+ <Word>FROM</Word>
+ <Word>IF</Word>
+ <Word>IGNORE</Word>
+ <Word>NAMESPACE</Word>
+ <Word>NESTED</Word>
+ <Word>PRAGMAS</Word>
+ <Word>PRODUCTIONS</Word>
+ <Word>SYNC</Word>
+ <Word>TO</Word>
+ <Word>TOKENS</Word>
+ <Word>TOKENNAMES</Word>
+ <Word>WEAK</Word>
+ <Word>using</Word>
+ </Keywords>
+ <Span color="Comment">
+ <Begin>//</Begin>
+ </Span>
+ <Span color="Comment" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ <Span foreground="Black" ruleSet="CSharp" multiline="true">
+ <Begin color="Keywords">COMPILER</Begin>
+ <End color="Keywords">TOKENNAMES</End>
+ </Span>
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ </Span>
+ <Span color="String">
+ <Begin>'</Begin>
+ <End>'</End>
+ </Span>
+ <Span foreground="#FF000000" ruleSet="ParamList">
+ <Begin foreground="#FF000000" fontWeight="bold">&lt;</Begin>
+ <End foreground="#FF000000" fontWeight="bold">&gt;</End>
+ </Span>
+ <Span foreground="#FF2F4F4F" fontWeight="normal" fontStyle="normal" ruleSet="CSharp" multiline="true">
+ <Begin foreground="#FF008000" fontWeight="bold" fontStyle="italic">\(\.</Begin>
+ <End foreground="#FF008000" fontWeight="bold" fontStyle="italic">\.\)</End>
+ </Span>
+ <Rule foreground="#FF00008B" fontWeight="normal" fontStyle="normal">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="CSharp" ignoreCase="false">
+ <Import ruleSet="C#/" />
+ <Rule foreground="#FF00008B" fontWeight="normal" fontStyle="normal">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="ParamList" ignoreCase="false">
+ <Import ruleSet="C#/" />
+ <Rule foreground="#FF00008B" fontWeight="normal" fontStyle="normal">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="RuleDefinition" ignoreCase="false">
+ <Rule foreground="#FF00008B" fontWeight="normal" fontStyle="normal">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/HTML-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/HTML-Mode.xshd
new file mode 100644
index 000000000..fd8211fe4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/HTML-Mode.xshd
@@ -0,0 +1,388 @@
+<?xml version="1.0"?>
+<!-- syntaxdefinition for HTML 2000 by Mike Krueger -->
+<!-- converted to AvalonEdit format by Siegfried Pammer in 2010 -->
+<SyntaxDefinition name="HTML" extensions=".htm;.html" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="green" exampleText="&lt;!-- comment --&gt;" />
+ <Color name="Digits" foreground="#BBAD34" exampleText="&lt;digits&gt;0123456789&lt;/digits&gt;" />
+ <Color name="ScriptTag" foreground="#3F8FD6" exampleText="&lt;script&gt;alert('Hello World!');&lt;/script&gt;" />
+ <Color name="JavaScriptTag" foreground="#3F8FD6" exampleText="&lt;script lang=&quot;JavaScript&quot;&gt;alert('Hello World!');&lt;/script&gt;" />
+ <Color name="JScriptTag" foreground="#3F8FD6" exampleText="&lt;script lang=&quot;JScript&quot;&gt;Response.Write(&quot;Hello World!&quot;);&lt;/script&gt;" />
+ <Color name="VBScriptTag" foreground="#3F8FD6" exampleText="&lt;script lang=&quot;VBScript&quot;&gt;Response.Write(&quot;Hello World!&quot;)&lt;/script&gt;" />
+ <Color name="UnknownScriptTag" foreground="#3F8FD6" exampleText="&lt;script la&gt;alert('Hello World!');&lt;/script&gt;" />
+ <Color name="HtmlTag" foreground="#3F8FD6" exampleText="&lt;html&gt;Hello World!/html&gt;" />
+ <Color name="Tags" foreground="#3F8FD6" exampleText="&lt;html&gt;Hello World! &lt;test /&gt;&lt;/html&gt;" />
+ <Color name="Attributes" foreground="#3F8FD6" exampleText="&lt;html background='green'&gt;Hello World! &lt;test /&gt;&lt;/html&gt;" />
+ <Color name="Slash" foreground="Gainsboro" exampleText="&lt;html background='green'&gt;Hello World! &lt;test /&gt;&lt;/html&gt;" />
+ <Color name="Assignment" foreground="Gainsboro" exampleText="&lt;html background='green'&gt;Hello World! &lt;test /&gt;&lt;/html&gt;" />
+ <Color name="String" foreground="#E48383" exampleText="&lt;html background='green'&gt;Hello World! &lt;test /&gt;&lt;/html&gt;" />
+ <Color name="EntityReference" foreground="#3F8FD6" exampleText="&lt;html background='green'&gt;Hello&amp;nbsp;World! &lt;test /&gt;&lt;/html&gt;" />
+ <Color name="Entities" foreground="#3F8FD6" exampleText="&lt;html background='green'&gt;Hello&amp;nbsp;World! &lt;test /&gt;&lt;/html&gt;" />
+ <Color name="UnknownAttribute" foreground="#DCDCDC" exampleText="&lt;html foo='bar' background='green'&gt;Hello&amp;nbsp;World! &lt;test /&gt;&lt;/html&gt;" />
+ <RuleSet ignoreCase="true">
+ <Span color="Comment" multiline="true">
+ <Begin>&lt;!--</Begin>
+ <End>--&gt;</End>
+ </Span>
+ <Span ruleSet="JavaScriptSet" multiline="true">
+ <Begin color="JavaScriptTag">&lt;script\ lang="JavaScript"&gt;</Begin>
+ <End color="JavaScriptTag">&lt;/script&gt;</End>
+ </Span>
+ <Span ruleSet="JavaScriptSet" multiline="true">
+ <Begin color="JavaScriptTag">&lt;script\s.*?text/javascript.*?&gt;</Begin>
+ <End color="JavaScriptTag">&lt;/script&gt;</End>
+ </Span>
+ <Span multiline="true">
+ <Begin color="JScriptTag">&lt;script\ lang="JScript"&gt;</Begin>
+ <End color="JScriptTag">&lt;/script&gt;</End>
+ </Span>
+ <Span multiline="true">
+ <Begin color="VBScriptTag">&lt;script\ lang="VBScript"&gt;</Begin>
+ <End color="VBScriptTag">&lt;/script&gt;</End>
+ </Span>
+ <Span ruleSet="JavaScriptSet" multiline="true">
+ <Begin color="ScriptTag">&lt;script&gt;</Begin>
+ <End color="ScriptTag">&lt;/script&gt;</End>
+ </Span>
+ <Span multiline="true">
+ <Begin color="UnknownScriptTag">&lt;script[^\w\d_]</Begin>
+ <End color="UnknownScriptTag">&lt;/script&gt;</End>
+ </Span>
+ <Span color="HtmlTag" ruleSet="HtmlTagSet" multiline="true">
+ <Begin>&lt;</Begin>
+ <End>&gt;</End>
+ </Span>
+ <Span color="EntityReference" ruleSet="EntityReferenceSet">
+ <Begin>&amp;</Begin>
+ <End>;</End>
+ </Span>
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="JavaScriptSet">
+ <Import ruleSet="JavaScript/" />
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="EntityReferenceSet" ignoreCase="false">
+ <Keywords color="Entities">
+ <Word>aacute</Word>
+ <Word>agrave</Word>
+ <Word>acirc</Word>
+ <Word>amp</Word>
+ <Word>atilde</Word>
+ <Word>aring</Word>
+ <Word>auml</Word>
+ <Word>aelig</Word>
+ <Word>ccedil</Word>
+ <Word>copy</Word>
+ <Word>eacute</Word>
+ <Word>egrave</Word>
+ <Word>ecirc</Word>
+ <Word>euml</Word>
+ <Word>iacute</Word>
+ <Word>igrave</Word>
+ <Word>icirc</Word>
+ <Word>iuml</Word>
+ <Word>eth</Word>
+ <Word>gt</Word>
+ <Word>lt</Word>
+ <Word>nbsp</Word>
+ <Word>ntilde</Word>
+ <Word>oacute</Word>
+ <Word>ograve</Word>
+ <Word>ocirc</Word>
+ <Word>otilde</Word>
+ <Word>ouml</Word>
+ <Word>oslash</Word>
+ <Word>quot</Word>
+ <Word>reg</Word>
+ <Word>szlig</Word>
+ <Word>uacute</Word>
+ <Word>ugrave</Word>
+ <Word>ucirc</Word>
+ <Word>uuml</Word>
+ <Word>yacute</Word>
+ <Word>thorn</Word>
+ <Word>trade</Word>
+ <Word>yuml</Word>
+ </Keywords>
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="HtmlTagSet" ignoreCase="false">
+ <Keywords color="Slash">
+ <Word>/</Word>
+ </Keywords>
+ <Keywords color="Assignment">
+ <Word>=</Word>
+ </Keywords>
+ <Keywords color="Tags">
+ <Word>!DOCTYPE</Word>
+ <Word>A</Word>
+ <Word>ABBR</Word>
+ <Word>ACRONYM</Word>
+ <Word>ADDRESS</Word>
+ <Word>APPLET</Word>
+ <Word>AREA</Word>
+ <Word>B</Word>
+ <Word>BASE</Word>
+ <Word>BASEFONT</Word>
+ <Word>BGSOUND</Word>
+ <Word>BDO</Word>
+ <Word>BIG</Word>
+ <Word>BLINK</Word>
+ <Word>BLOCKQUOTE</Word>
+ <Word>BODY</Word>
+ <Word>BR</Word>
+ <Word>BUTTON</Word>
+ <Word>CAPTION</Word>
+ <Word>CENTER</Word>
+ <Word>CITE</Word>
+ <Word>CODE</Word>
+ <Word>COL</Word>
+ <Word>COLGROUP</Word>
+ <Word>COMMENT</Word>
+ <Word>DD</Word>
+ <Word>DEL</Word>
+ <Word>DFN</Word>
+ <Word>DIR</Word>
+ <Word>DIV</Word>
+ <Word>DL</Word>
+ <Word>DT</Word>
+ <Word>EM</Word>
+ <Word>EMBED</Word>
+ <Word>FIELDSET</Word>
+ <Word>FONT</Word>
+ <Word>FORM</Word>
+ <Word>FRAME</Word>
+ <Word>FRAMESET</Word>
+ <Word>H</Word>
+ <Word>H1</Word>
+ <Word>H2</Word>
+ <Word>H3</Word>
+ <Word>H4</Word>
+ <Word>H5</Word>
+ <Word>H6</Word>
+ <Word>HEAD</Word>
+ <Word>HR</Word>
+ <Word>HTA:APPLICATION</Word>
+ <Word>HTML</Word>
+ <Word>I</Word>
+ <Word>IFRAME</Word>
+ <Word>IMG</Word>
+ <Word>INPUT</Word>
+ <Word>INS</Word>
+ <Word>ISINDEX</Word>
+ <Word>KBD</Word>
+ <Word>LABEL</Word>
+ <Word>LEGEnd</Word>
+ <Word>LI</Word>
+ <Word>LINK</Word>
+ <Word>LISTING</Word>
+ <Word>MAP</Word>
+ <Word>MARQUEE</Word>
+ <Word>MENU</Word>
+ <Word>META</Word>
+ <Word>MULTICOL</Word>
+ <Word>NEXTID</Word>
+ <Word>NOBR</Word>
+ <Word>NOFRAMES</Word>
+ <Word>NOSCRIPT</Word>
+ <Word>OBJECT</Word>
+ <Word>OL</Word>
+ <Word>OPTGROUP</Word>
+ <Word>OPTION</Word>
+ <Word>P</Word>
+ <Word>PARAM</Word>
+ <Word>PLAINTEXT</Word>
+ <Word>PRE</Word>
+ <Word>Q</Word>
+ <Word>S</Word>
+ <Word>SAMP</Word>
+ <Word>SCRIPT</Word>
+ <Word>SELECT</Word>
+ <Word>SERVER</Word>
+ <Word>SMALL</Word>
+ <Word>SOUND</Word>
+ <Word>SPACER</Word>
+ <Word>Span</Word>
+ <Word>STRONG</Word>
+ <Word>STYLE</Word>
+ <Word>SUB</Word>
+ <Word>SUP</Word>
+ <Word>TABLE</Word>
+ <Word>TBODY</Word>
+ <Word>TD</Word>
+ <Word>TEXTAREA</Word>
+ <Word>TEXTFLOW</Word>
+ <Word>TFOOT</Word>
+ <Word>TH</Word>
+ <Word>THEAD</Word>
+ <Word>TITLE</Word>
+ <Word>TR</Word>
+ <Word>TT</Word>
+ <Word>U</Word>
+ <Word>VAR</Word>
+ <Word>WBR</Word>
+ <Word>XMP</Word>
+ </Keywords>
+ <Keywords color="Attributes">
+ <Word>abbr</Word>
+ <Word>accept-charset</Word>
+ <Word>accept</Word>
+ <Word>accesskey</Word>
+ <Word>action</Word>
+ <Word>align</Word>
+ <Word>alink</Word>
+ <Word>alt</Word>
+ <Word>applicationname</Word>
+ <Word>archive</Word>
+ <Word>axis</Word>
+ <Word>background</Word>
+ <Word>behavior</Word>
+ <Word>bgcolor</Word>
+ <Word>bgproperties</Word>
+ <Word>border</Word>
+ <Word>bordercolor</Word>
+ <Word>bordercolordark</Word>
+ <Word>bordercolorligh</Word>
+ <Word>borderstyle</Word>
+ <Word>caption</Word>
+ <Word>cellpadding</Word>
+ <Word>cellspacing</Word>
+ <Word>char</Word>
+ <Word>charoff</Word>
+ <Word>charset</Word>
+ <Word>checked</Word>
+ <Word>cite</Word>
+ <Word>class</Word>
+ <Word>classid</Word>
+ <Word>clear</Word>
+ <Word>code</Word>
+ <Word>codetype</Word>
+ <Word>color</Word>
+ <Word>cols</Word>
+ <Word>colspan</Word>
+ <Word>compact</Word>
+ <Word>content</Word>
+ <Word>coords</Word>
+ <Word>data</Word>
+ <Word>datetime</Word>
+ <Word>declare</Word>
+ <Word>defer</Word>
+ <Word>dir</Word>
+ <Word>direction</Word>
+ <Word>disabled</Word>
+ <Word>dynsrc</Word>
+ <Word>enctype</Word>
+ <Word>face</Word>
+ <Word>for</Word>
+ <Word>frame</Word>
+ <Word>frameborder</Word>
+ <Word>framespacing</Word>
+ <Word>gutter</Word>
+ <Word>headers</Word>
+ <Word>height</Word>
+ <Word>href</Word>
+ <Word>hreflang</Word>
+ <Word>hspace</Word>
+ <Word>http-equiv</Word>
+ <Word>icon</Word>
+ <Word>id</Word>
+ <Word>ismap</Word>
+ <Word>label</Word>
+ <Word>language</Word>
+ <Word>leftmargin</Word>
+ <Word>link</Word>
+ <Word>longdesc</Word>
+ <Word>loop</Word>
+ <Word>lowsrc</Word>
+ <Word>marginheight</Word>
+ <Word>marginwidth</Word>
+ <Word>maximizebutton</Word>
+ <Word>maxlength</Word>
+ <Word>media</Word>
+ <Word>method</Word>
+ <Word>methods</Word>
+ <Word>minimizebutton</Word>
+ <Word>multiple</Word>
+ <Word>name</Word>
+ <Word>nohref</Word>
+ <Word>noresize</Word>
+ <Word>noshade</Word>
+ <Word>nowrap</Word>
+ <Word>object</Word>
+ <Word>onabort</Word>
+ <Word>onblur</Word>
+ <Word>onchange</Word>
+ <Word>onclick</Word>
+ <Word>ondblclick</Word>
+ <Word>onerror</Word>
+ <Word>onfocus</Word>
+ <Word>onkeydown</Word>
+ <Word>onkeypress</Word>
+ <Word>onkeyup</Word>
+ <Word>onload</Word>
+ <Word>onmousedown</Word>
+ <Word>onmousemove</Word>
+ <Word>onmouseout</Word>
+ <Word>onmouseover</Word>
+ <Word>onmouseup</Word>
+ <Word>onreset</Word>
+ <Word>onselect</Word>
+ <Word>onsubmit</Word>
+ <Word>onunload</Word>
+ <Word>profile</Word>
+ <Word>prompt</Word>
+ <Word>readonly</Word>
+ <Word>rel</Word>
+ <Word>rev</Word>
+ <Word>rows</Word>
+ <Word>rowspan</Word>
+ <Word>rules</Word>
+ <Word>runat</Word>
+ <Word>scheme</Word>
+ <Word>scope</Word>
+ <Word>scrollamount</Word>
+ <Word>scrolldelay</Word>
+ <Word>scrolling</Word>
+ <Word>selected</Word>
+ <Word>shape</Word>
+ <Word>showintaskbar</Word>
+ <Word>singleinstance</Word>
+ <Word>size</Word>
+ <Word>span</Word>
+ <Word>src</Word>
+ <Word>standby</Word>
+ <Word>start</Word>
+ <Word>style</Word>
+ <Word>summary</Word>
+ <Word>sysmenu</Word>
+ <Word>tabindex</Word>
+ <Word>target</Word>
+ <Word>text</Word>
+ <Word>title</Word>
+ <Word>topmargin</Word>
+ <Word>type</Word>
+ <Word>urn</Word>
+ <Word>usemap</Word>
+ <Word>valign</Word>
+ <Word>value</Word>
+ <Word>valuetype</Word>
+ <Word>version</Word>
+ <Word>vlink</Word>
+ <Word>vrml</Word>
+ <Word>vspace</Word>
+ <Word>width</Word>
+ <Word>windowstate</Word>
+ <Word>wrap</Word>
+ </Keywords>
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ </Span>
+ <Span color="String">
+ <Begin>'</Begin>
+ <End>'</End>
+ </Span>
+ <Rule color="UnknownAttribute">[\d\w_]+(?=(\s*=))</Rule>
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Java-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Java-Mode.xshd
new file mode 100644
index 000000000..dd5053e91
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Java-Mode.xshd
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<!-- syntaxdefinition for Java 2001 by Jonathan Pierce & Mike Krueger -->
+<!-- converted to AvalonEdit format by Siegfried Pammer in 2010 -->
+<SyntaxDefinition name="Java" extensions=".java" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="MethodName" foreground="DarkBlue" />
+ <Color name="Digits" foreground="DarkBlue" fontStyle="italic" />
+ <Color name="String" foreground="Magenta" />
+ <Color name="Character" foreground="Magenta" />
+ <Color name="Comment" foreground="SlateGray" />
+ <Color name="Punctuation" foreground="DarkGreen" />
+ <Color name="AccessKeywords" foreground="Black" fontWeight="bold" />
+ <Color name="OperatorKeywords" foreground="DarkCyan" fontWeight="bold" />
+ <Color name="SelectionStatements" foreground="Blue" fontWeight="bold" />
+ <Color name="IterationStatements" foreground="Blue" fontWeight="bold" />
+ <Color name="ExceptionHandlingStatements" foreground="Teal" fontWeight="bold" />
+ <Color name="ValueTypes" foreground="Red" fontWeight="bold" />
+ <Color name="ReferenceTypes" foreground="Red" />
+ <Color name="Void" foreground="Red" />
+ <Color name="JumpStatements" foreground="Navy" />
+ <Color name="Modifiers" foreground="Brown" />
+ <Color name="AccessModifiers" foreground="Blue" fontWeight="bold" />
+ <Color name="Package" foreground="Green" fontWeight="bold" />
+ <Color name="Literals" foreground="Black" fontWeight="bold" />
+ <Color name="CommentTags" foreground="Red" fontWeight="bold" fontStyle="italic" />
+ <Color name="JavaDocTags" foreground="DarkGray" fontWeight="bold" fontStyle="italic" />
+ <RuleSet ignoreCase="false">
+ <Rule color="Punctuation">
+ [?,.()\[\]{}+\-/%*&lt;&gt;^!|]+
+ </Rule>
+ <Keywords color="AccessKeywords">
+ <Word>this</Word>
+ <Word>super</Word>
+ </Keywords>
+ <Keywords color="OperatorKeywords">
+ <Word>new</Word>
+ <Word>instanceof</Word>
+ <Word>true</Word>
+ <Word>false</Word>
+ </Keywords>
+ <Keywords color="SelectionStatements">
+ <Word>else</Word>
+ <Word>if</Word>
+ <Word>switch</Word>
+ <Word>case</Word>
+ </Keywords>
+ <Keywords color="IterationStatements">
+ <Word>do</Word>
+ <Word>for</Word>
+ <Word>while</Word>
+ </Keywords>
+ <Keywords color="JumpStatements">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>default</Word>
+ <Word>goto</Word>
+ <Word>return</Word>
+ </Keywords>
+ <Keywords color="ExceptionHandlingStatements">
+ <Word>try</Word>
+ <Word>throw</Word>
+ <Word>catch</Word>
+ <Word>finally</Word>
+ </Keywords>
+ <Keywords color="ValueTypes">
+ <Word>boolean</Word>
+ <Word>double</Word>
+ <Word>int</Word>
+ <Word>short</Word>
+ <Word>long</Word>
+ <Word>float</Word>
+ <Word>byte</Word>
+ <Word>char</Word>
+ </Keywords>
+ <Keywords color="ReferenceTypes">
+ <Word>class</Word>
+ <Word>interface</Word>
+ <Word>object</Word>
+ </Keywords>
+ <Keywords color="Void">
+ <Word>void</Word>
+ </Keywords>
+ <Keywords color="Modifiers">
+ <Word>abstract</Word>
+ <Word>const</Word>
+ <Word>static</Word>
+ <Word>final</Word>
+ <Word>native</Word>
+ <Word>extends</Word>
+ <Word>implements</Word>
+ <Word>volatile</Word>
+ <Word>transient</Word>
+ <Word>throws</Word>
+ <Word>strictfp</Word>
+ <Word>synchronized</Word>
+ </Keywords>
+ <Keywords color="AccessModifiers">
+ <Word>public</Word>
+ <Word>protected</Word>
+ <Word>private</Word>
+ </Keywords>
+ <Keywords color="Package">
+ <Word>package</Word>
+ <Word>import</Word>
+ </Keywords>
+ <Keywords color="Literals">
+ <Word>null</Word>
+ </Keywords>
+ <Span color="Comment" ruleSet="TestSet">
+ <Begin>//</Begin>
+ </Span>
+ <Span color="Comment" ruleSet="TestSet" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Span color="Character">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Rule color="MethodName">[\d\w_]+(?=(\s*\())</Rule>
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+ <RuleSet name="TestSet" ignoreCase="true">
+ <Keywords color="CommentTags">
+ <Word>TODO</Word>
+ </Keywords>
+ <Keywords color="JavaDocTags">
+ <Word>@author</Word>
+ <Word>@version</Word>
+ <Word>@param</Word>
+ <Word>@return</Word>
+ <Word>@exception</Word>
+ <Word>@throws</Word>
+ <Word>@see</Word>
+ <Word>@since</Word>
+ <Word>@serial</Word>
+ <Word>@serialField</Word>
+ <Word>@serialData</Word>
+ <Word>@deprecated</Word>
+ </Keywords>
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/JavaScript-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/JavaScript-Mode.xshd
new file mode 100644
index 000000000..97775dce5
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/JavaScript-Mode.xshd
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<!-- syntaxdefinition for JavaScript 2.0 by Svante Lidman -->
+<!-- converted to AvalonEdit format by Siegfried Pammer in 2010 -->
+<SyntaxDefinition name="JavaScript" extensions=".js" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Digits" foreground="DarkBlue" />
+ <Color name="Comment" foreground="Green" />
+ <Color name="String" foreground="Sienna" />
+ <Color name="Character" foreground="Sienna" />
+ <Color name="Regex" foreground="Sienna" />
+ <Color name="JavaScriptKeyWords" foreground="Blue" />
+ <Color name="JavaScriptIntrinsics" foreground="Blue" />
+ <Color name="JavaScriptLiterals" foreground="Blue" />
+ <Color name="JavaScriptGlobalFunctions" foreground="Blue" />
+ <RuleSet ignoreCase="false">
+ <Keywords color="JavaScriptKeyWords">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>delete</Word>
+ <Word>else</Word>
+ <Word>for</Word>
+ <Word>function</Word>
+ <Word>if</Word>
+ <Word>in</Word>
+ <Word>new</Word>
+ <Word>return</Word>
+ <Word>this</Word>
+ <Word>typeof</Word>
+ <Word>var</Word>
+ <Word>void</Word>
+ <Word>while</Word>
+ <Word>with</Word>
+ <Word>abstract</Word>
+ <Word>boolean</Word>
+ <Word>byte</Word>
+ <Word>case</Word>
+ <Word>catch</Word>
+ <Word>char</Word>
+ <Word>class</Word>
+ <Word>const</Word>
+ <Word>debugger</Word>
+ <Word>default</Word>
+ <Word>do</Word>
+ <Word>double</Word>
+ <Word>enum</Word>
+ <Word>export</Word>
+ <Word>extends</Word>
+ <Word>final</Word>
+ <Word>finally</Word>
+ <Word>float</Word>
+ <Word>goto</Word>
+ <Word>implements</Word>
+ <Word>import</Word>
+ <Word>instanceof</Word>
+ <Word>int</Word>
+ <Word>interface</Word>
+ <Word>long</Word>
+ <Word>native</Word>
+ <Word>package</Word>
+ <Word>private</Word>
+ <Word>protected</Word>
+ <Word>public</Word>
+ <Word>short</Word>
+ <Word>static</Word>
+ <Word>super</Word>
+ <Word>switch</Word>
+ <Word>synchronized</Word>
+ <Word>throw</Word>
+ <Word>throws</Word>
+ <Word>transient</Word>
+ <Word>try</Word>
+ <Word>volatile</Word>
+ </Keywords>
+ <Keywords color="JavaScriptIntrinsics">
+ <Word>Array</Word>
+ <Word>Boolean</Word>
+ <Word>Date</Word>
+ <Word>Function</Word>
+ <Word>Global</Word>
+ <Word>Math</Word>
+ <Word>Number</Word>
+ <Word>Object</Word>
+ <Word>RegExp</Word>
+ <Word>String</Word>
+ </Keywords>
+ <Keywords color="JavaScriptLiterals">
+ <Word>false</Word>
+ <Word>null</Word>
+ <Word>true</Word>
+ <Word>NaN</Word>
+ <Word>Infinity</Word>
+ </Keywords>
+ <Keywords color="JavaScriptGlobalFunctions">
+ <Word>eval</Word>
+ <Word>parseInt</Word>
+ <Word>parseFloat</Word>
+ <Word>escape</Word>
+ <Word>unescape</Word>
+ <Word>isNaN</Word>
+ <Word>isFinite</Word>
+ </Keywords>
+ <Span color="Comment">
+ <Begin>//</Begin>
+ </Span>
+ <Span color="Comment" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+ <!--<Rule color="Regex">/.*/</Rule>-->
+ <Span color="Regex">
+ <Begin>/</Begin>
+ <End>/</End>
+ <RuleSet>
+ <Span begin="\\/" end="." />
+ </RuleSet>
+ </Span>
+ <Span color="String" multiline="true">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Span color="Character">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <Span begin="\\" end="." />
+ </RuleSet>
+ </Span>
+ <Rule color="Digits">\b0[xX][0-9a-fA-F]+|(\b\d+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?</Rule>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/MarkDown-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/MarkDown-Mode.xshd
new file mode 100644
index 000000000..ead5045ab
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/MarkDown-Mode.xshd
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<SyntaxDefinition name="MarkDown" extensions=".md" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Heading" foreground="Maroon" exampleText="# Title #" />
+ <Color name="Emphasis" fontStyle="italic" exampleText="*this* is important!" />
+ <Color name="StrongEmphasis" fontWeight="bold" exampleText="**this** is more important!" />
+ <Color name="Code" exampleText="this is `int.GetHashCode()`" />
+ <Color name="BlockQuote" foreground="DarkBlue" exampleText="&gt; This is a\r\n&gt; quote." />
+ <Color name="Link" foreground="Blue" exampleText="[text](http://example.com)" />
+ <Color name="Image" foreground="Green" exampleText="[text][http://example.com/test.png]" />
+ <Color name="LineBreak" background="LightGray" exampleText="end of line \r\n2nd line " />
+
+ <RuleSet ignoreCase="true">
+ <Rule color="Heading">
+ ^\#.*
+ </Rule>
+ <Rule color="StrongEmphasis">
+ \*\*.*\*\*
+ </Rule>
+ <Rule color="StrongEmphasis">
+ __.*__
+ </Rule>
+ <Rule color="Emphasis">
+ \*(?![ ]).*\*
+ </Rule>
+ <Rule color="Emphasis">
+ _.*_
+ </Rule>
+ <Rule color="Code">
+ `.*`
+ </Rule>
+ <Span color="Code" ruleSet="C#/" multiline="true">
+ <Begin>^\t</Begin>
+ <End>^(?!\t)</End>
+ </Span>
+ <Span color="Code" ruleSet="C#/" multiline="true">
+ <Begin>^[ ]{4}</Begin>
+ <End>^(?![ ]{4})</End>
+ </Span>
+ <Span color="BlockQuote" multiline="true">
+ <Begin>^&gt;</Begin>
+ <End>^(?!&gt;)</End>
+ </Span>
+ <Rule color="Image">
+ \!\[.*\]\[.*\]
+ </Rule>
+ <Rule color="Link">
+ \[.*\]\(.*\)
+ </Rule>
+ <Rule color="Link">
+ \[.*\]\[.*\]
+ </Rule>
+ <Rule color="LineBreak">
+ [ ]{2}$
+ </Rule>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV1.xsd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV1.xsd
new file mode 100644
index 000000000..82bce451d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV1.xsd
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
+ <xsd:annotation>
+ <xsd:documentation>
+ This schema defines the syntax for mode definitions in SharpDevelop.
+ The schema can be simplified quite a bit but it does the job as is.
+
+
+ If you are using this file as a reference it is probably easiest to scroll to
+ the botton to find the definition of the root element called SyntaxDefinition and
+ then unwind the different type definitions and refernces.
+
+ Note on coloring:
+ Many tags define how some symbol should be colored. If a specific symbol
+ can not be matched onto either a Span definition, Keyword, or a Digit/Number it
+ will be rendered in the current default color. Which is the default color of the
+ current span or the default color of the mode as a whole if no span has been entered.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <!-- Defines the default rendering of the mode -->
+ <xsd:complexType name="EnvironmentEntry">
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:complexType>
+
+ <xsd:complexType name="CustomEnvironmentEntry">
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:complexType>
+
+ <!-- The environment tag defines the coloring of various attributes in SharpDevelop -->
+ <xsd:complexType name="Environment">
+
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element name="Default" type="EnvironmentEntry" minOccurs="0" maxOccurs="1" />
+ <xsd:element name="Selection" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="VRuler" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="InvalidLines" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="CaretMarker" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="CaretLine" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+
+ <xsd:element name="LineNumbers" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+
+ <xsd:element name="FoldLine" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="FoldMarker" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="SelectedFoldLine" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+
+ <xsd:element name="EOLMarkers" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="SpaceMarkers" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="TabMarkers" type="EnvironmentEntry" minOccurs="0" maxOccurs="1"/>
+
+ <xsd:element name="Custom" type="CustomEnvironmentEntry" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:complexType>
+
+ <xsd:complexType name="Properties">
+ <xsd:sequence>
+ <xsd:element name="Property" type="Property" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="Property">
+ <!-- The actual KeyWord, typically reserved words or symbols in a programming language -->
+ <xsd:attribute name="name" type="xsd:string" />
+ <xsd:attribute name="value" type="xsd:string" />
+ </xsd:complexType>
+
+ <!-- The Digits tag defines the color for rendering Digits-->
+ <xsd:complexType name="Digits">
+ <xsd:attribute name="name" type="xsd:string" />
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:complexType>
+
+ <!-- Defines the delimiting characters of the syntax, e.g., the characters that, "break up" a line
+ into separate symbols, typically key words. It is not necessary, or desirable to include the
+ characters that denot the start or end of a span. Space and Tab are implicitly defined as delimeters
+ and they don't need to be includeded explicitly (this will probably be changed at some future time).-->
+ <xsd:complexType name="Delimiters">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <!-- The beginning symbol of a Span -->
+ <xsd:complexType name="Begin">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="singleword" type="xsd:boolean" />
+ <xsd:attribute name="startofline" type="xsd:boolean" />
+ <!-- The default rendering style for the Begin symbol. If not specified
+ the defaul rendering style for the span will be used. -->
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <!-- The end symbol of a Span -->
+ <xsd:complexType name="End">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="singleword" type="xsd:boolean" />
+ <!-- The default rendering style for the End symbol. If not specified
+ the defaul rendering style for the span will be used. -->
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="Span">
+ <xsd:sequence>
+ <!-- Defines the symbol that indicates the beginning of the span. -->
+ <xsd:element name="Begin" type="Begin" />
+ <!-- Defines the symbol that indicates the end of the span. May be omitted for
+ one-line spans. -->
+ <xsd:element name="End" minOccurs="0" type="End" />
+ </xsd:sequence>
+ <!-- The name of the span definition -->
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ <!-- Defines the rule set that is applicable in the Span. May be omitted. -->
+ <xsd:attribute name="rule" type="xsd:string" />
+ <!-- Defines wether the Span should terminate automatically at the end of line. Typical examples
+ include one-line comments such as // in C++ or REM in Windows .Bat files. -->
+ <xsd:attribute name="stopateol" type="xsd:boolean" />
+
+ <!-- OBSOLUTE: Defines whether C-style escape sequences using \ are applicable or not in the span. -->
+ <xsd:attribute name="noescapesequences" type="xsd:boolean" />
+
+ <!-- defines the escape character -->
+ <xsd:attribute name="escapecharacter" type="xsd:string" />
+
+ <!-- The default rendering style for the span -->
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:complexType>
+
+ <xsd:complexType name="MarkPrevious">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <!-- Svante Lidman, looking in the code it is a bit unclear what the intent is here... -->
+ <xsd:attribute name="markmarker" type="xsd:boolean" />
+ <!-- The rendering style to be used -->
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <!-- Allows you to define the coloring of the symbol that follows a specified symbol -->
+ <xsd:complexType name="MarkFollowing">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <!-- Svante Lidman, looking in the code it is a bit unclear what the intent is here... -->
+ <xsd:attribute name="markmarker" type="xsd:boolean" />
+ <!-- The rendering style to be used -->
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="Key">
+ <!-- The actual KeyWord, typically reserved words or symbols in a programming language -->
+ <xsd:attribute name="word" type="xsd:string" />
+ </xsd:complexType>
+
+
+ <!-- A grouping of keywords that sholuld be colored the same way -->
+ <xsd:complexType name="KeyWords">
+ <xsd:sequence>
+ <!-- allow groups with 0 keywords: this simplifies the syntax highlighting editor -->
+ <!-- A KeyWord -->
+ <xsd:element name="Key" type="Key" minOccurs="0" maxOccurs="unbounded">
+ </xsd:element>
+ </xsd:sequence>
+ <!-- The name of the KeyWord group -->
+ <xsd:attribute name="name" type="xsd:string" />
+ <!-- The rendering style of the KeyWord group -->
+ <xsd:attribute name="bold" type="xsd:boolean" />
+ <xsd:attribute name="italic" type="xsd:boolean" />
+ <xsd:attribute name="color" type="xsd:string" />
+ <xsd:attribute name="bgcolor" type="xsd:string" />
+ </xsd:complexType>
+
+ <xsd:complexType name="RuleSet">
+ <xsd:sequence>
+ <!-- Defines the delimiting characters of the syntax, e.g., the characters that, "break up" a line
+ into separate symbols, typically key words. It is not necessary, or desirable to include the
+ characters that denot the start or end of a span. Space and Tab are implicitly defined as delimeters
+ and they don't need to be includeded explicitly (this will probably be changed at some future time).-->
+ <xsd:element name="Delimiters" type="Delimiters" minOccurs="0" maxOccurs="1">
+ </xsd:element>
+ <!-- A Span tag defines a scope, or what can be seen as a separate parsing context where a different set of
+ highlighting rules are applicable compared to the text where the span is found.
+ Examples of spans include:
+ - A string in a language as C
+ - A <script> tag in Html
+ - The internals of a tag in XML (between < and >).
+ A span can have a rule set associated with it that defines the highlighting rules that are applicable
+ in the span. -->
+ <xsd:element name="Span" type="Span" minOccurs="0" maxOccurs="unbounded">
+ </xsd:element>
+ <!-- The MarkPrevious tag allows you to define the coloring of the item that preceeds a specific
+ symbol. An example of where this comes in handy is when coloring the contents of an XML-tag,
+ in particular the attributes and attribute names. The following definition:
+ <MarkPrevious bold="false" italic="false" color="Red">=</MarkPrevious>
+ will make teh highlighter color words that are followed by an = to be colored in Red.
+ You can see this in this file if you view it with the default XML-mode in SharpDevelop. -->
+ <xsd:element name="MarkPrevious" type="MarkPrevious" minOccurs="0" maxOccurs="unbounded">
+ </xsd:element>
+ <!-- The MarkFollowing tag works similarly as the MarkPrevious tag but relates to the coloring
+ of the symbol that follows the specified symbol. -->
+ <xsd:element name="MarkFollowing" type="MarkFollowing" minOccurs="0" maxOccurs="unbounded">
+ </xsd:element>
+ <!-- Defines a group of keywords that should be colored the same way -->
+ <xsd:element name="KeyWords" type="KeyWords" minOccurs="0" maxOccurs="unbounded">
+ </xsd:element>
+ </xsd:sequence>
+ <!-- The name of the RuleSet. Used when you refer to the RuleSet in the rule attribute of a Span tag.
+ Each mode file should have a rule definition without a defined name. This denotes the default rule
+ set for the mode. -->
+ <xsd:attribute name="name" type="xsd:string" />
+ <!-- Allows you to use another mode, defined in another file as a RuleSet. For an example see the
+ use of the JavaScript mode from the HTML-mode. -->
+ <xsd:attribute name="reference" type="xsd:string" />
+ <!-- Defines whether case is significant for matching keywords in the mode. -->
+ <xsd:attribute name="ignorecase" type="xsd:boolean" />
+ <!-- OBSOLETE: noescapesequences -->
+ <xsd:attribute name="noescapesequences" type="xsd:boolean" />
+ <!-- defines the escape character -->
+ <xsd:attribute name="escapecharacter" type="xsd:string" />
+ </xsd:complexType>
+
+ <!-- The RuleSets tag is just a grouping of the set of RuleSets for a mode. -->
+ <xsd:complexType name="RuleSets">
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <!-- Any number of RuleSet tag can be defined in a mode -->
+ <xsd:element name="RuleSet" type="RuleSet" minOccurs="1" maxOccurs="unbounded">
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <!-- SyntaxDefinition is the root-element in a mode definition file -->
+ <xsd:element name="SyntaxDefinition">
+ <xsd:complexType>
+ <xsd:sequence>
+ <!-- The Environment tag defines colors, for various standard elements in the SharpDevelop GUI, if
+ not given the default values are used. -->
+ <xsd:element name="Environment" type="Environment" minOccurs="0" maxOccurs="1"/>
+
+ <!-- The Properties section defines properties which are bound to the highlighting -->
+ <xsd:element name="Properties" type="Properties" minOccurs="0" maxOccurs="1" />
+
+ <!-- The Digits tag defines the color for rendering Digits-->
+ <xsd:element name="Digits" type="Digits" minOccurs="0" maxOccurs="1"/>
+
+ <!-- The RuleSets tag defines the rule sets that are used in the mode. Note that all modes are defined in
+ a flat structture even if they are used recursively. For an example of a mode that uses
+ multiple rule sets see the XML-mode. There is a top level rule-set and and another rule-set
+ that handles highligting within a tag, i.e., between < and >. -->
+ <xsd:element name="RuleSets" type="RuleSets" />
+ </xsd:sequence>
+ <!-- The name of the mode. This is used when you, in the defintion of a RuleSet refers to another
+ mode. I.e., one that is defined in an external file. For an example of this see the HTML-Mode that
+ uses the JavaScript-mode this way. -->
+ <xsd:attribute name="name" type="xsd:string" />
+ <!-- The file extensions that the mode is applicable for. Extensions must be written with lower case and
+ should include the ., as in .txt. If several extensions are applicable they should be separeated with | -->
+ <xsd:attribute name="extensions" type="xsd:string" />
+ <!-- Name of a syntax mode where rulesets, spans, keywords and other settings are imported from -->
+ <xsd:attribute name="extends" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+
+</xsd:schema>
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV2.xsd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV2.xsd
new file mode 100644
index 000000000..047ef38a1
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/ModeV2.xsd
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsd:schema version="2" elementFormDefault="qualified" targetNamespace="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <!-- Font Weight -->
+ <xsd:simpleType name="FontWeight">
+ <xsd:union>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:integer">
+ <xsd:minInclusive value="1"/>
+ <xsd:maxInclusive value="999"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="bold"/>
+ <xsd:enumeration value="normal"/>
+ <xsd:enumeration value="regular"/>
+ <!-- should we support other font weight names? -->
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:union>
+ </xsd:simpleType>
+
+ <!-- Font Style -->
+ <xsd:simpleType name="FontStyle">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="italic"/>
+ <xsd:enumeration value="normal"/>
+ <xsd:enumeration value="oblique"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- Color -->
+ <xsd:attributeGroup name="ColorAttributes">
+ <xsd:attribute name="foreground" type="xsd:string" use="optional" />
+ <xsd:attribute name="background" type="xsd:string" use="optional" />
+ <xsd:attribute name="fontWeight" type="FontWeight" use="optional" />
+ <xsd:attribute name="fontStyle" type="FontStyle" use="optional" />
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:attributeGroup>
+
+ <xsd:attributeGroup name="ColorReferenceAttributes">
+ <xsd:attributeGroup ref="ColorAttributes" />
+ <xsd:attribute name="color" type="xsd:string" use="optional" />
+ </xsd:attributeGroup>
+
+ <xsd:element name="Color">
+ <xsd:complexType>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ <xsd:attribute name="exampleText" type="xsd:string" use="optional" />
+ <xsd:attributeGroup ref="ColorAttributes"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="Property">
+ <xsd:complexType>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ <xsd:attribute name="value" type="xsd:string" use="required" />
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:complexType>
+ </xsd:element>
+
+ <!-- Regular expression -->
+ <xsd:simpleType name="regex">
+ <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="regexIgnorePatternWhitespace">
+ <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+
+ <!-- Keywords -->
+ <xsd:element name="Keywords">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="Word" minOccurs="1" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attributeGroup ref="ColorReferenceAttributes"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="Word">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <!-- Spans -->
+ <xsd:element name="Span">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="Begin" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="End" minOccurs="0" maxOccurs="1"/>
+ <xsd:element ref="RuleSet" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+
+ <xsd:attributeGroup ref="ColorReferenceAttributes"/>
+ <xsd:attribute name="multiline" type="xsd:boolean" use="optional"/>
+ <xsd:attribute name="ruleSet" type="xsd:string" use="optional"/>
+ <xsd:attribute name="begin" type="regex" use="optional"/>
+ <xsd:attribute name="end" type="regex" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:complexType name="SpanBeginEnd">
+ <xsd:simpleContent>
+ <xsd:extension base="regexIgnorePatternWhitespace">
+ <xsd:attributeGroup ref="ColorReferenceAttributes"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+ <xsd:element name="Begin" type="SpanBeginEnd"/>
+ <xsd:element name="End" type="SpanBeginEnd"/>
+
+ <!-- Imports -->
+ <xsd:element name="Import">
+ <xsd:complexType>
+ <xsd:attribute name="ruleSet" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <!-- Rules -->
+ <xsd:element name="Rule">
+ <xsd:complexType>
+ <xsd:simpleContent>
+ <xsd:extension base="regexIgnorePatternWhitespace">
+ <xsd:attributeGroup ref="ColorReferenceAttributes"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <!-- Rule set -->
+ <xsd:element name="RuleSet">
+ <xsd:complexType>
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="Keywords"/>
+ <xsd:element ref="Span"/>
+ <xsd:element ref="Import"/>
+ <xsd:element ref="Rule"/>
+ <xsd:any namespace="##other" processContents="lax" />
+ </xsd:choice>
+ <xsd:attribute name="name" type="xsd:string" use="optional" />
+ <xsd:attribute name="ignoreCase" type="xsd:boolean" use="optional" />
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:complexType>
+ </xsd:element>
+
+ <!-- Main syntax definition -->
+ <xsd:element name="SyntaxDefinition">
+ <xsd:complexType>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element ref="Property"/>
+ <xsd:element ref="Color"/>
+ <xsd:element ref="RuleSet"/>
+ <xsd:any namespace="##other" processContents="lax" />
+ </xsd:choice>
+ <xsd:attribute name="name" type="xsd:string" use="optional" />
+ <xsd:attribute name="extensions" type="xsd:string" use="optional" />
+ <xsd:anyAttribute namespace="##other" processContents="lax" />
+ </xsd:complexType>
+ </xsd:element>
+</xsd:schema> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PHP-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PHP-Mode.xshd
new file mode 100644
index 000000000..f09ff27d9
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PHP-Mode.xshd
@@ -0,0 +1,158 @@
+<?xml version="1.0"?>
+<!-- syntaxdefinition for PHP 2001 by Chad Smith & Mike Krueger -->
+<!-- converted to AvalonEdit format by Siegfried Pammer in 2010 -->
+<SyntaxDefinition name ="PHP" extensions = ".php" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="Green" exampleText="// comment" />
+ <Color name="String" foreground="Blue" exampleText="$text = &quot;Hello, World!&quot;" />
+ <Color name="NumberLiteral" foreground="DarkBlue" exampleText="3.1415" />
+ <Color name="Punctuation" foreground="DarkGreen" exampleText="a(b + c)" />
+ <Color name="FunctionCall" foreground="MidnightBlue" fontWeight="bold" exampleText="abs(num);"/>
+ <Color name="AccessKeywords" foreground="Black" fontWeight="bold" exampleText="global $database;" />
+ <Color name="OperatorKeywords" foreground="DarkCyan" fontWeight="bold" exampleText="if (x === false and y === false) { }" />
+ <Color name="SelectionStatements" foreground="Blue" fontWeight="bold" exampleText="if (true) { } else { }" />
+ <Color name="IterationStatements" foreground="Blue" fontWeight="bold" exampleText="while (true) { }" />
+ <Color name="JumpStatements" foreground="Navy" fontWeight="bold" exampleText="if (x == 5) continue;" />
+ <Color name="ControlStatements" foreground="Teal" fontWeight="bold" exampleText="include(&quot;test.php&quot;);" />
+ <Color name="ValueTypes" foreground="Red" fontWeight="bold" exampleText="int test = 5;" />
+ <Color name="OtherTypes" foreground="Red" exampleText="object test = null;" />
+ <Color name="AccessModifiers" foreground="Blue" fontWeight="bold" exampleText="public function test() {}" />
+ <RuleSet>
+ <Span color="Comment">
+ <Begin>\#</Begin>
+ </Span>
+
+ <Span color="Comment">
+ <Begin>//</Begin>
+ </Span>
+
+ <Span color="Comment" multiline="true">
+ <Begin>/\*</Begin>
+ <End>\*/</End>
+ </Span>
+
+ <!-- Digits -->
+ <Rule color="NumberLiteral">
+ \b0[xX][0-9a-fA-F]+ # hex number
+ |
+ \b0[0-9]+ # octal number
+ |
+ ( \b\d+(\.[0-9]+)? #number with optional floating point
+ | \.[0-9]+ #or just starting with floating point
+ )
+ ([eE][+-]?[0-9]+)? # optional exponent
+ </Rule>
+
+ <Rule color="Punctuation">
+ [?,.:;()\[\]{}+\-/%*&lt;&gt;&amp;^!|~@]+
+ </Rule>
+
+ <!-- Mark previous rule-->
+ <Rule color="FunctionCall">
+ \b
+ [\d\w_]+ # an identifier
+ (?=\s*\() # followed by (
+ </Rule>
+
+ <Span color="String" multiline="true">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <Span color="String" multiline="true">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <!-- heredoc syntax -->
+ <Span color="String" multiline="true">
+ <Begin>&lt;&lt;&lt;\"?[\d\w_]+\"?$</Begin>
+ <End>^[\d\w_]+;</End>
+ </Span>
+
+ <!-- nowdoc syntax -->
+ <Span color="String" multiline="true">
+ <Begin>&lt;&lt;&lt;\'[\d\w_]+\'$</Begin>
+ <End>^[\d\w_]+;</End>
+ </Span>
+
+ <Keywords color="AccessKeywords">
+ <Word>global</Word>
+ <Word>my</Word>
+ <Word>var</Word>
+ </Keywords>
+
+ <Keywords color="OperatorKeywords">
+ <Word>and</Word>
+ <Word>or</Word>
+ <Word>new</Word>
+ <Word>clone</Word>
+ <Word>instanceof</Word>
+ <Word>xor</Word>
+ <Word>true</Word>
+ <Word>false</Word>
+ </Keywords>
+
+ <Keywords color="SelectionStatements">
+ <Word>else</Word>
+ <Word>else</Word>
+ <Word>switch</Word>
+ <Word>case</Word>
+ <Word>endif</Word>
+ <Word>elseif</Word>
+ </Keywords>
+
+ <Keywords color="IterationStatements">
+ <Word>do</Word>
+ <Word>for</Word>
+ <Word>foreach</Word>
+ <Word>while</Word>
+ <Word>endwhile</Word>
+ <Word>exit</Word>
+ </Keywords>
+
+ <Keywords color="JumpStatements">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>default</Word>
+ <Word>goto</Word>
+ <Word>return</Word>
+ </Keywords>
+
+ <Keywords color="ControlStatements">
+ <Word>require</Word>
+ <Word>include</Word>
+ <Word>require</Word>
+ <Word>include</Word>
+ <Word>function</Word>
+ </Keywords>
+
+ <Keywords color="ValueTypes">
+ <Word>int</Word>
+ <Word>integer</Word>
+ <Word>real</Word>
+ <Word>double</Word>
+ <Word>float</Word>
+ <Word>string</Word>
+ <Word>array</Word>
+ <Word>object</Word>
+ </Keywords>
+
+ <Keywords color="OtherTypes">
+ <Word>class</Word>
+ <Word>void</Word>
+ </Keywords>
+
+ <Keywords color="AccessModifiers">
+ <Word>public</Word>
+ <Word>private</Word>
+ </Keywords>
+ </RuleSet>
+</SyntaxDefinition>
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Patch-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Patch-Mode.xshd
new file mode 100644
index 000000000..c8e1c3cfd
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Patch-Mode.xshd
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<SyntaxDefinition name="Patch" extensions=".patch;.diff" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="AddedText" foreground="Navy" exampleText="+added" />
+ <Color name="RemovedText" foreground="#FF339966" exampleText="-removed" />
+ <Color name="UnchangedText" exampleText=" unchanged" />
+ <Color name="Position" foreground="Navy" exampleText="@@ -186,6 +186,12 @@" />
+ <Color name="Header" foreground="DarkRed" exampleText="--- (oldversion)&#10;+++ (newversion)" />
+ <Color name="FileName" foreground="Green" fontWeight="bold" exampleText="Index: path/to/file" />
+ <RuleSet>
+ <Span color="FileName">
+ <Begin>Index:\s</Begin>
+ </Span>
+ <Span color="Header">
+ <Begin>==</Begin>
+ </Span>
+ <Span color="Header">
+ <Begin>---</Begin>
+ </Span>
+ <Span color="Header">
+ <Begin>\+\+\+</Begin>
+ </Span>
+ <Span foreground="Purple">
+ <Begin>@@</Begin>
+ </Span>
+ <Span color="RemovedText">
+ <Begin>-</Begin>
+ </Span>
+ <Span color="AddedText">
+ <Begin>\+</Begin>
+ </Span>
+ <Span color="UnchangedText">
+ <Begin>\s</Begin>
+ </Span>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PowerShell.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PowerShell.xshd
new file mode 100644
index 000000000..ee6fec4c8
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/PowerShell.xshd
@@ -0,0 +1,146 @@
+<?xml version="1.0"?>
+<SyntaxDefinition name="PowerShell" extensions=".ps1;.psm1;.psd1" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="Green" exampleText="// comment" />
+ <Color name="String" foreground="Blue" exampleText="string text = &quot;Hello, World!&quot;"/>
+ <Color name="Char" foreground="Magenta" exampleText="char linefeed = '\n';"/>
+ <Color name="Punctuation" exampleText="a(b.c);" />
+ <Color name="NumberLiteral" foreground="DarkBlue" exampleText="3.1415f"/>
+ <Color name="Keywords" fontWeight="bold" foreground="Blue" exampleText="if (a)"/>
+ <Color name="Variable" foreground="Maroon" exampleText="$param = 1" />
+ <Color name="ExceptionKeywords" fontWeight="bold" foreground="Teal" />
+ <Color name="GotoKeywords" foreground="Navy" />
+ <Color name="ReferenceTypes" foreground="Red" />
+ <Color name="Command" fontWeight="bold" foreground="MidnightBlue" />
+ <Color name="Operators" foreground="#FF8515EA" exampleText="-eq"/>
+
+ <RuleSet ignoreCase="true">
+ <Span color="Comment">
+ <Begin>\#</Begin>
+ </Span>
+
+ <Span color="Comment" multiline="true">
+ <Begin>&lt;\#</Begin>
+ <End>\#&gt;</End>
+ </Span>
+
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <Span color="Char">
+ <Begin>'</Begin>
+ <End>'</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin="\\" end="."/>
+ </RuleSet>
+ </Span>
+
+ <Span color="String" multiline="true">
+ <Begin color="String">@"</Begin>
+ <End>"@</End>
+ <RuleSet>
+ <!-- span for escape sequences -->
+ <Span begin='""' end=""/>
+ </RuleSet>
+ </Span>
+
+ <Keywords color="Keywords">
+ <Word>while</Word>
+ <Word>param</Word>
+ <Word>end</Word>
+ <Word>define</Word>
+ <Word>else</Word>
+ <Word>from</Word>
+ <Word>foreach</Word>
+ <Word>var</Word>
+ <Word>dynamicparam</Word>
+ <Word>filter</Word>
+ <Word>dp</Word>
+ <Word>until</Word>
+ <Word>for</Word>
+ <Word>exit</Word>
+ <Word>switch</Word>
+ <Word>process</Word>
+ <Word>begin</Word>
+ <Word>elseif</Word>
+ <Word>if</Word>
+ <Word>in</Word>
+ <Word>data</Word>
+ <Word>class</Word>
+ <Word>using</Word>
+ <Word>function</Word>
+ </Keywords>
+
+ <Keywords color="ExceptionKeywords">
+ <Word>catch</Word>
+ <Word>finally</Word>
+ <Word>throw</Word>
+ <Word>trap</Word>
+ <Word>try</Word>
+ </Keywords>
+
+ <Keywords color="GotoKeywords">
+ <Word>break</Word>
+ <Word>continue</Word>
+ <Word>return</Word>
+ </Keywords>
+
+ <Keywords color="ReferenceTypes">
+ <Word>class</Word>
+ </Keywords>
+
+ <Keywords color="Operators">
+ <Word>-not</Word>
+ <Word>-band</Word>
+ <Word>-bor</Word>
+ <Word>-replace</Word>
+ <Word>-ireplace</Word>
+ <Word>-creplace</Word>
+ <Word>-and</Word>
+ <Word>-or</Word>
+ <Word>-is</Word>
+ <Word>-isnot</Word>
+ <Word>-as</Word>
+ <Word>-lt</Word>
+ <Word>-le</Word>
+ <Word>-gt</Word>
+ <Word>-ge</Word>
+ <Word>-eq</Word>
+ <Word>-ne</Word>
+ <Word>-contains</Word>
+ <Word>-notcontains</Word>
+ <Word>-like</Word>
+ <Word>-notlike</Word>
+ <Word>-match</Word>
+ <Word>-notmatch</Word>
+ </Keywords>
+
+ <Rule color="Variable">
+ \$[\d\w_]+
+ </Rule>
+
+ <Rule color="Command">
+ [\w]+-[\w]+
+ </Rule>
+
+ <!-- Digits -->
+ <Rule color="NumberLiteral">
+ \b0[xX][0-9a-fA-F]+ # hex number
+ |
+ ( \b\d+(\.[0-9]+)? #number with optional floating point
+ | \.[0-9]+ #or just starting with floating point
+ )
+ ([eE][+-]?[0-9]+)? # optional exponent
+ </Rule>
+
+ <Rule color="Punctuation">
+ [?,.;()\[\]{}+\-/%*&lt;&gt;^+~!|&amp;]+
+ </Rule>
+ </RuleSet>
+</SyntaxDefinition>
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Resources.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Resources.cs
new file mode 100644
index 000000000..3ac838ce3
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Resources.cs
@@ -0,0 +1,48 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.IO;
+
+namespace Tango.Scripting.Editors.Highlighting
+{
+ static class Resources
+ {
+ static readonly string Prefix = typeof(Resources).FullName + ".";
+
+ public static Stream OpenStream(string name)
+ {
+ Stream s = typeof(Resources).Assembly.GetManifestResourceStream(Prefix + name);
+ if (s == null)
+ throw new FileNotFoundException("The resource file '" + name + "' was not found.");
+ return s;
+ }
+
+ internal static void RegisterBuiltInHighlightings(HighlightingManager.DefaultHighlightingManager hlm)
+ {
+ hlm.RegisterHighlighting("XmlDoc", null, "XmlDoc.xshd");
+ hlm.RegisterHighlighting("C#", new[] { ".cs" }, "CSharp-Mode.xshd");
+
+ hlm.RegisterHighlighting("JavaScript", new[] { ".js" }, "JavaScript-Mode.xshd");
+ hlm.RegisterHighlighting("HTML", new[] { ".htm", ".html" }, "HTML-Mode.xshd");
+ hlm.RegisterHighlighting("ASP/XHTML", new[] { ".asp", ".aspx", ".asax", ".asmx", ".ascx", ".master" }, "ASPX.xshd");
+
+ hlm.RegisterHighlighting("Boo", new[] { ".boo" }, "Boo.xshd");
+ hlm.RegisterHighlighting("Coco", new[] { ".atg" }, "Coco-Mode.xshd");
+ hlm.RegisterHighlighting("CSS", new[] { ".css" }, "CSS-Mode.xshd");
+ hlm.RegisterHighlighting("C++", new[] { ".c", ".h", ".cc", ".cpp" , ".hpp" }, "CPP-Mode.xshd");
+ hlm.RegisterHighlighting("Java", new[] { ".java" }, "Java-Mode.xshd");
+ hlm.RegisterHighlighting("Patch", new[] { ".patch", ".diff" }, "Patch-Mode.xshd");
+ hlm.RegisterHighlighting("PowerShell", new[] { ".ps1", ".psm1", ".psd1" }, "PowerShell.xshd");
+ hlm.RegisterHighlighting("PHP", new[] { ".php" }, "PHP-Mode.xshd");
+ hlm.RegisterHighlighting("TeX", new[] { ".tex" }, "Tex-Mode.xshd");
+ hlm.RegisterHighlighting("VBNET", new[] { ".vb" }, "VBNET-Mode.xshd");
+ hlm.RegisterHighlighting("XML", (".xml;.xsl;.xslt;.xsd;.manifest;.config;.addin;" +
+ ".xshd;.wxs;.wxi;.wxl;.proj;.csproj;.vbproj;.ilproj;" +
+ ".booproj;.build;.xfrm;.targets;.xaml;.xpt;" +
+ ".xft;.map;.wsdl;.disco;.ps1xml;.nuspec").Split(';'),
+ "XML-Mode.xshd");
+ hlm.RegisterHighlighting("MarkDown", new[] { ".md" }, "MarkDown-Mode.xshd");
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Tex-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Tex-Mode.xshd
new file mode 100644
index 000000000..91083b0ab
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/Tex-Mode.xshd
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+<!-- syntaxdefinition for TeX document 2001 by Mike Krueger (gleaned from Jedit) -->
+
+<SyntaxDefinition name = "TeX" extensions = ".tex">
+
+ <Digits name = "Digits" bold = "false" italic = "false" color = "Black"/>
+
+ <RuleSets>
+ <RuleSet ignorecase = "false">
+ <Delimiters>&amp;~!@%^*()-+=|\#/{}[]:;"'&lt;&gt; , .?</Delimiters>
+
+ <Span name = "LineComment" bold = "false" italic = "true" color = "SlateGray" stopateol = "true">
+ <Begin>%</Begin>
+ </Span>
+
+ <Span name = "MathMode" rule = "MathMode" bold = "false" italic = "false" color = "Black" stopateol = "false">
+ <Begin>$$</Begin>
+ <End>$$</End>
+ </Span>
+ <Span name = "LatexMathMode" rule = "MathMode" bold = "false" italic = "false" color = "Black" stopateol = "false">
+ <Begin>\[</Begin>
+ <End>\]</End>
+ </Span>
+
+ <!-- \... commands -->
+ <MarkFollowing markmarker ="true" bold = "true" italic = "false" color = "MidnightBlue">\</MarkFollowing>
+
+ <!-- some commands must be handled specially -->
+ <KeyWords name = "Keyword1" bold = "false" italic = "false" color = "Blue">
+ <Key word = "\$" />
+ <Key word = "\\" />
+ <Key word = "\%" />
+ </KeyWords>
+
+ <KeyWords name = "KeyWords2" bold="true" italic="false" color="Green">
+ <Key word = ")" />
+ <Key word = ")" />
+ <Key word = "{" />
+ <Key word = "}" />
+ <Key word = "[" />
+ <Key word = "]" />
+ <Key word = "=" />
+ <Key word = "!" />
+ <Key word = "+" />
+ <Key word = "-" />
+ <Key word = "/" />
+ <Key word = "*" />
+ <Key word = "&gt;" />
+ <Key word = "&lt;" />
+ <Key word = "&amp;" />
+ <Key word = "|" />
+ <Key word = "^" />
+ <Key word = "~" />
+ <Key word = "." />
+ <Key word = "," />
+ <Key word = ";" />
+ <Key word = "?" />
+ <Key word = ":" />
+ <Key word = "'" />
+ <!-- <Key word = """ />-->
+ <Key word = "`" />
+ </KeyWords>
+ </RuleSet>
+
+ <RuleSet name = "MathMode" ignorecase = "false">
+ <Delimiters>&amp;~!@%^*()-+=|\#/{}[]:;"'&lt;&gt; , .?</Delimiters>
+
+
+ <Span name = "LineComment" bold = "false" italic = "true" color = "SlateGray" stopateol = "true">
+ <Begin>%</Begin>
+ </Span>
+
+ <!-- \... commands -->
+ <MarkFollowing markmarker ="true" bold = "true" italic = "false" color = "MidnightBlue">\</MarkFollowing>
+
+ <KeyWords name = "KeyWords2" bold="true" italic="false" color="Green">
+ <Key word = ")" />
+ <Key word = ")" />
+ <Key word = "{" />
+ <Key word = "}" />
+ <Key word = "[" />
+ <Key word = "]" />
+ <Key word = "=" />
+ <Key word = "!" />
+ <Key word = "+" />
+ <Key word = "-" />
+ <Key word = "/" />
+ <Key word = "*" />
+ <Key word = "&gt;" />
+ <Key word = "&lt;" />
+ <Key word = "&amp;" />
+ <Key word = "|" />
+ <Key word = "^" />
+ <Key word = "~" />
+ <Key word = "." />
+ <Key word = "," />
+ <Key word = ";" />
+ <Key word = "?" />
+ <Key word = ":" />
+ <Key word = "'" />
+ <!-- <Key word = """ />-->
+ <Key word = "`" />
+ </KeyWords>
+ </RuleSet>
+ </RuleSets>
+
+</SyntaxDefinition>
+
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/VBNET-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/VBNET-Mode.xshd
new file mode 100644
index 000000000..b22555a29
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/VBNET-Mode.xshd
@@ -0,0 +1,256 @@
+<?xml version="1.0"?>
+
+<!-- Syntaxdefinition for VB.NET, v0.1 Rev 1 by Christian Holm -->
+<!-- Updated 2005 by Daniel Grunwald for VB.NET 2.0 -->
+<!-- Converted to AvalonEdit format by Daniel Grunwald in 2010 -->
+<!-- Updated 2010 by Siegfried Oleg Pammer for VB.NET 9 and 10 -->
+<!-- Updated 2011 by Siegfried Oleg Pammer for VB 11 CTP -->
+<SyntaxDefinition name="VBNET" extensions=".vb" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="Comment" foreground="Green" exampleText="' comment" />
+ <Color name="String" exampleText="text = &quot;Hello, World!&quot;" />
+
+ <Color name="DateLiteral" foreground="Blue" exampleText="endOfWorld = #2012-12-21#" />
+ <Color name="Preprocessor" foreground="Maroon" exampleText="#Region &quot;Title&quot;" />
+ <Color name="DataTypes" foreground="#FF6F002F" exampleText="Dim b As Boolean = True" />
+ <Color name="Operators" foreground="#FF8515EA" exampleText="If a OrElse b Then"/>
+ <Color name="Constants" foreground="Blue" exampleText="b = False" />
+ <Color name="Keywords" foreground="Blue" exampleText="If a OrElse b Then" />
+ <Color name="FunctionKeywords" foreground="Blue" exampleText="CInt(a)" />
+ <Color name="ContextKeywords" foreground="Blue" exampleText="Declare Unicode Sub SomeMethod" />
+
+ <Property name="DocCommentMarker" value="'''" />
+
+ <RuleSet ignoreCase="true">
+ <Span color="String">
+ <Begin>"</Begin>
+ <End>"</End>
+ <RuleSet>
+ <Span begin="&quot;&quot;" end="" />
+ </RuleSet>
+ </Span>
+ <Span color="Preprocessor" ruleSet="PreprocessorSet">
+ <Begin>(?&lt;=(^\s*))\#</Begin>
+ </Span>
+ <Span color="DateLiteral">
+ <Begin>(?&lt;!(^\s*))\#</Begin>
+ <End>\#</End>
+ </Span>
+ <Span color="Comment" ruleSet="XmlDoc/DocCommentSet">
+ <Begin color="XmlDoc/DocComment">'''</Begin>
+ </Span>
+ <Span color="Comment">
+ <Begin>'</Begin>
+ </Span>
+ <Span color="Comment">
+ <Begin>\bREM\b</Begin>
+ </Span>
+ <Keywords color="DataTypes">
+ <Word>Boolean</Word>
+ <Word>Byte</Word>
+ <Word>Char</Word>
+ <Word>Date</Word>
+ <Word>Decimal</Word>
+ <Word>Double</Word>
+ <Word>Integer</Word>
+ <Word>Long</Word>
+ <Word>Object</Word>
+ <Word>SByte</Word>
+ <Word>Short</Word>
+ <Word>Single</Word>
+ <Word>String</Word>
+ <Word>UInteger</Word>
+ <Word>ULong</Word>
+ <Word>UShort</Word>
+ <Word>Variant</Word>
+ </Keywords>
+ <Keywords color="Operators">
+ <Word>AddressOf</Word>
+ <Word>And</Word>
+ <Word>AndAlso</Word>
+ <Word>Await</Word>
+ <Word>Is</Word>
+ <Word>IsNot</Word>
+ <Word>Like</Word>
+ <Word>Mod</Word>
+ <Word>New</Word>
+ <Word>Not</Word>
+ <Word>Or</Word>
+ <Word>OrElse</Word>
+ <Word>Xor</Word>
+ </Keywords>
+ <Keywords color="Constants">
+ <Word>False</Word>
+ <Word>Me</Word>
+ <Word>MyBase</Word>
+ <Word>MyClass</Word>
+ <Word>Nothing</Word>
+ <Word>True</Word>
+ </Keywords>
+ <Keywords color="FunctionKeywords">
+ <Word>CBool</Word>
+ <Word>CByte</Word>
+ <Word>CChar</Word>
+ <Word>CDate</Word>
+ <Word>CDbl</Word>
+ <Word>CDec</Word>
+ <Word>CInt</Word>
+ <Word>CLng</Word>
+ <Word>CObj</Word>
+ <Word>CSByte</Word>
+ <Word>CShort</Word>
+ <Word>CSng</Word>
+ <Word>CStr</Word>
+ <Word>CType</Word>
+ <Word>CUInt</Word>
+ <Word>CULng</Word>
+ <Word>CUShort</Word>
+ <Word>DirectCast</Word>
+ <Word>GetType</Word>
+ <Word>GetXmlNamespace</Word>
+ <Word>IIf</Word>
+ <Word>TryCast</Word>
+ <Word>TypeOf</Word>
+ </Keywords>
+ <Keywords color="Keywords">
+ <Word>AddHandler</Word>
+ <Word>Alias</Word>
+ <Word>As</Word>
+ <Word>ByRef</Word>
+ <Word>ByVal</Word>
+ <Word>Call</Word>
+ <Word>Case</Word>
+ <Word>Catch</Word>
+ <Word>Class</Word>
+ <Word>Const</Word>
+ <Word>Continue</Word>
+ <Word>Declare</Word>
+ <Word>Default</Word>
+ <Word>Delegate</Word>
+ <Word>Dim</Word>
+ <Word>Do</Word>
+ <Word>Each</Word>
+ <Word>Else</Word>
+ <Word>ElseIf</Word>
+ <Word>End</Word>
+ <Word>EndIf</Word>
+ <Word>Enum</Word>
+ <Word>Erase</Word>
+ <Word>Error</Word>
+ <Word>Event</Word>
+ <Word>Exit</Word>
+ <Word>Finally</Word>
+ <Word>For</Word>
+ <Word>Friend</Word>
+ <Word>Function</Word>
+ <Word>Get</Word>
+ <Word>Global</Word>
+ <Word>GoSub</Word>
+ <Word>GoTo</Word>
+ <Word>Handles</Word>
+ <Word>If</Word>
+ <Word>Implements</Word>
+ <Word>Imports</Word>
+ <Word>In</Word>
+ <Word>Inherits</Word>
+ <Word>Interface</Word>
+ <Word>Let</Word>
+ <Word>Lib</Word>
+ <Word>Loop</Word>
+ <Word>Module</Word>
+ <Word>MustInherit</Word>
+ <Word>MustOverride</Word>
+ <Word>Namespace</Word>
+ <Word>Narrowing</Word>
+ <Word>New</Word>
+ <Word>Next</Word>
+ <Word>NotInheritable</Word>
+ <Word>NotOverridable</Word>
+ <Word>Of</Word>
+ <Word>On</Word>
+ <Word>Operator</Word>
+ <Word>Option</Word>
+ <Word>Optional</Word>
+ <Word>Overloads</Word>
+ <Word>Overridable</Word>
+ <Word>Overrides</Word>
+ <Word>ParamArray</Word>
+ <Word>Partial</Word>
+ <Word>Private</Word>
+ <Word>Property</Word>
+ <Word>Protected</Word>
+ <Word>Public</Word>
+ <Word>RaiseEvent</Word>
+ <Word>ReadOnly</Word>
+ <Word>ReDim</Word>
+ <Word>RemoveHandler</Word>
+ <Word>Resume</Word>
+ <Word>Return</Word>
+ <Word>Select</Word>
+ <Word>Set</Word>
+ <Word>Shadows</Word>
+ <Word>Shared</Word>
+ <Word>Static</Word>
+ <Word>Step</Word>
+ <Word>Stop</Word>
+ <Word>Structure</Word>
+ <Word>Sub</Word>
+ <Word>SyncLock</Word>
+ <Word>Then</Word>
+ <Word>Throw</Word>
+ <Word>To</Word>
+ <Word>Try</Word>
+ <Word>Using</Word>
+ <Word>Wend</Word>
+ <Word>When</Word>
+ <Word>While</Word>
+ <Word>Widening</Word>
+ <Word>With</Word>
+ <Word>WithEvents</Word>
+ <Word>WriteOnly</Word>
+ </Keywords>
+ <Keywords color="ContextKeywords">
+ <Word>Aggregate</Word>
+ <Word>Ansi</Word>
+ <Word>Ascending</Word>
+ <Word>Async</Word>
+ <Word>Auto</Word>
+ <Word>Binary</Word>
+ <Word>By</Word>
+ <Word>Compare</Word>
+ <Word>Custom</Word>
+ <Word>Descending</Word>
+ <Word>Distinct</Word>
+ <Word>Equals</Word>
+ <Word>Explicit</Word>
+ <Word>From</Word>
+ <Word>Group</Word>
+ <Word>Infer</Word>
+ <Word>Into</Word>
+ <Word>Iterator</Word>
+ <Word>Join</Word>
+ <Word>Key</Word>
+ <Word>Off</Word>
+ <Word>Preserve</Word>
+ <Word>Skip</Word>
+ <Word>Strict</Word>
+ <Word>Take</Word>
+ <Word>Text</Word>
+ <Word>Unicode</Word>
+ <Word>Until</Word>
+ <Word>Where</Word>
+ <Word>Yield</Word>
+ </Keywords>
+ </RuleSet>
+ <RuleSet name="PreprocessorSet" ignoreCase="true">
+ <Keywords fontWeight="bold">
+ <Word>Const</Word>
+ <Word>Else</Word>
+ <Word>ElseIf</Word>
+ <Word>End</Word>
+ <Word>ExternalChecksum</Word>
+ <Word>ExternalSource</Word>
+ <Word>If</Word>
+ <Word>Region</Word>
+ </Keywords>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XML-Mode.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XML-Mode.xshd
new file mode 100644
index 000000000..8f0bdef76
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XML-Mode.xshd
@@ -0,0 +1,63 @@
+<SyntaxDefinition name="XML" extensions=".xml;.xsl;.xslt;.xsd;.manifest;.config;.addin;.xshd;.wxs;.wxi;.wxl;.proj;.csproj;.vbproj;.ilproj;.booproj;.build;.xfrm;.targets;.xaml;.xpt;.xft;.map;.wsdl;.disco;.ps1xml;.nuspec" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color foreground="Green" name="Comment" exampleText="&lt;!-- comment --&gt;" />
+ <Color foreground="Blue" name="CData" exampleText="&lt;![CDATA[data]]&gt;" />
+ <Color foreground="Blue" name="DocType" exampleText="&lt;!DOCTYPE rootElement&gt;" />
+ <Color foreground="Blue" name="XmlDeclaration" exampleText='&lt;?xml version="1.0"?&gt;' />
+ <Color foreground="DarkMagenta" name="XmlTag" exampleText='&lt;tag attribute="value" /&gt;' />
+ <Color foreground="Red" name="AttributeName" exampleText='&lt;tag attribute="value" /&gt;' />
+ <Color foreground="Blue" name="AttributeValue" exampleText='&lt;tag attribute="value" /&gt;' />
+ <Color foreground="Teal" name="Entity" exampleText="index.aspx?a=1&amp;amp;b=2" />
+ <Color foreground="Olive" name="BrokenEntity" exampleText="index.aspx?a=1&amp;b=2" />
+
+ <RuleSet>
+ <Span color="Comment" multiline="true">
+ <Begin>&lt;!--</Begin>
+ <End>--&gt;</End>
+ </Span>
+ <Span color="CData" multiline="true">
+ <Begin>&lt;!\[CDATA\[</Begin>
+ <End>]]&gt;</End>
+ </Span>
+ <Span color="DocType" multiline="true">
+ <Begin>&lt;!DOCTYPE</Begin>
+ <End>&gt;</End>
+ </Span>
+ <Span color="XmlDeclaration" multiline="true">
+ <Begin>&lt;\?</Begin>
+ <End>\?&gt;</End>
+ </Span>
+ <Span color="XmlTag" multiline="true">
+ <Begin>&lt;</Begin>
+ <End>&gt;</End>
+ <RuleSet>
+ <!-- Treat the position before '<' as end, as that's not a valid character
+ in attribute names and indicates the user forgot a closing quote. -->
+ <Span color="AttributeValue" multiline="true" ruleSet="EntitySet">
+ <Begin>"</Begin>
+ <End>"|(?=&lt;)</End>
+ </Span>
+ <Span color="AttributeValue" multiline="true" ruleSet="EntitySet">
+ <Begin>'</Begin>
+ <End>'|(?=&lt;)</End>
+ </Span>
+ <Rule color="AttributeName">[\d\w_\-\.]+(?=(\s*=))</Rule>
+ <Rule color="AttributeValue">=</Rule>
+ </RuleSet>
+ </Span>
+ <Import ruleSet="EntitySet"/>
+ </RuleSet>
+
+ <RuleSet name="EntitySet">
+ <Rule color="Entity">
+ &amp;
+ [\w\d\#]+
+ ;
+ </Rule>
+
+ <Rule color="BrokenEntity">
+ &amp;
+ [\w\d\#]*
+ #missing ;
+ </Rule>
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XmlDoc.xshd b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XmlDoc.xshd
new file mode 100644
index 000000000..e4303de6a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Resources/XmlDoc.xshd
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<SyntaxDefinition name="XmlDoc" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
+ <Color name="XmlString" foreground="Silver" fontWeight="bold" exampleText="${DocCommentMarker} &lt;exception cref=&quot;System.Exception&quot; /&gt;" />
+ <Color name="DocComment" foreground="Gray" exampleText="${DocCommentMarker} &lt;exception cref=&quot;System.Exception&quot; /&gt;" />
+ <Color name="XmlPunctuation" fontWeight="bold" exampleText="${DocCommentMarker} &lt;exception cref=&quot;System.Exception&quot; /&gt;" />
+ <Color name="KnownDocTags" fontWeight="bold" exampleText="${DocCommentMarker} &lt;exception cref=&quot;System.Exception&quot; /&gt;" />
+
+ <RuleSet name="DocCommentSet">
+ <Span color="DocComment">
+ <Begin>&lt;</Begin>
+ <End>&gt;</End>
+ <RuleSet>
+ <Span color="XmlString">
+ <Begin>"</Begin>
+ <End>"</End>
+ </Span>
+ <Keywords color="XmlPunctuation">
+ <Word>/</Word>
+ <Word>|</Word>
+ <Word>=</Word>
+ </Keywords>
+ <Keywords color="KnownDocTags">
+ <Word>c</Word>
+ <Word>code</Word>
+ <Word>example</Word>
+ <Word>exception</Word>
+ <Word>list</Word>
+ <Word>para</Word>
+ <Word>param</Word>
+ <Word>paramref</Word>
+ <Word>permission</Word>
+ <Word>remarks</Word>
+ <Word>returns</Word>
+ <Word>see</Word>
+ <Word>seealso</Word>
+ <Word>summary</Word>
+ <Word>value</Word>
+
+ <Word>type</Word>
+ <Word>name</Word>
+ <Word>cref</Word>
+ <Word>item</Word>
+ <Word>term</Word>
+ <Word>description</Word>
+ <Word>listheader</Word>
+ <Word>typeparam</Word>
+ <Word>typeparamref</Word>
+ </Keywords>
+ </RuleSet>
+ </Span>
+ </RuleSet>
+
+ <!-- root ruleset = DocCommentSet -->
+ <RuleSet>
+ <Import ruleSet="DocCommentSet" />
+ </RuleSet>
+</SyntaxDefinition> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/HighlightingLoader.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/HighlightingLoader.cs
new file mode 100644
index 000000000..f1d0d8554
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/HighlightingLoader.cs
@@ -0,0 +1,99 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Xml;
+using System.Xml.Schema;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// Static class with helper methods to load XSHD highlighting files.
+ /// </summary>
+ public static class HighlightingLoader
+ {
+ #region XSHD loading
+ /// <summary>
+ /// Lodas a syntax definition from the xml reader.
+ /// </summary>
+ public static XshdSyntaxDefinition LoadXshd(XmlReader reader)
+ {
+ return LoadXshd(reader, false);
+ }
+
+ internal static XshdSyntaxDefinition LoadXshd(XmlReader reader, bool skipValidation)
+ {
+ if (reader == null)
+ throw new ArgumentNullException("reader");
+ try {
+ reader.MoveToContent();
+ if (reader.NamespaceURI == V2Loader.Namespace) {
+ return V2Loader.LoadDefinition(reader, skipValidation);
+ } else {
+ return V1Loader.LoadDefinition(reader, skipValidation);
+ }
+ } catch (XmlSchemaException ex) {
+ throw WrapException(ex, ex.LineNumber, ex.LinePosition);
+ } catch (XmlException ex) {
+ throw WrapException(ex, ex.LineNumber, ex.LinePosition);
+ }
+ }
+
+ static Exception WrapException(Exception ex, int lineNumber, int linePosition)
+ {
+ return new HighlightingDefinitionInvalidException(FormatExceptionMessage(ex.Message, lineNumber, linePosition), ex);
+ }
+
+ internal static string FormatExceptionMessage(string message, int lineNumber, int linePosition)
+ {
+ if (lineNumber <= 0)
+ return message;
+ else
+ return "Error at position (line " + lineNumber + ", column " + linePosition + "):\n" + message;
+ }
+
+ internal static XmlReader GetValidatingReader(XmlReader input, bool ignoreWhitespace, XmlSchemaSet schemaSet)
+ {
+ XmlReaderSettings settings = new XmlReaderSettings();
+ settings.CloseInput = true;
+ settings.IgnoreComments = true;
+ settings.IgnoreWhitespace = ignoreWhitespace;
+ if (schemaSet != null) {
+ settings.Schemas = schemaSet;
+ settings.ValidationType = ValidationType.Schema;
+ }
+ return XmlReader.Create(input, settings);
+ }
+
+ internal static XmlSchemaSet LoadSchemaSet(XmlReader schemaInput)
+ {
+ XmlSchemaSet schemaSet = new XmlSchemaSet();
+ schemaSet.Add(null, schemaInput);
+ schemaSet.ValidationEventHandler += delegate(object sender, ValidationEventArgs args) {
+ throw new HighlightingDefinitionInvalidException(args.Message);
+ };
+ return schemaSet;
+ }
+ #endregion
+
+ #region Load Highlighting from XSHD
+ /// <summary>
+ /// Creates a highlighting definition from the XSHD file.
+ /// </summary>
+ public static IHighlightingDefinition Load(XshdSyntaxDefinition syntaxDefinition, IHighlightingDefinitionReferenceResolver resolver)
+ {
+ if (syntaxDefinition == null)
+ throw new ArgumentNullException("syntaxDefinition");
+ return new XmlHighlightingDefinition(syntaxDefinition, resolver);
+ }
+
+ /// <summary>
+ /// Creates a highlighting definition from the XSHD file.
+ /// </summary>
+ public static IHighlightingDefinition Load(XmlReader reader, IHighlightingDefinitionReferenceResolver resolver)
+ {
+ return Load(LoadXshd(reader), resolver);
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/IXshdVisitor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/IXshdVisitor.cs
new file mode 100644
index 000000000..f328a32d3
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/IXshdVisitor.cs
@@ -0,0 +1,32 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// A visitor over the XSHD element tree.
+ /// </summary>
+ public interface IXshdVisitor
+ {
+ /// <summary/>
+ object VisitRuleSet(XshdRuleSet ruleSet);
+
+ /// <summary/>
+ object VisitColor(XshdColor color);
+
+ /// <summary/>
+ object VisitKeywords(XshdKeywords keywords);
+
+ /// <summary/>
+ object VisitSpan(XshdSpan span);
+
+ /// <summary/>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", Justification = "A VB programmer implementing a visitor?")]
+ object VisitImport(XshdImport import);
+
+ /// <summary/>
+ object VisitRule(XshdRule rule);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/SaveXshdVisitor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/SaveXshdVisitor.cs
new file mode 100644
index 000000000..e158954f1
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/SaveXshdVisitor.cs
@@ -0,0 +1,182 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Xml;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// Xshd visitor implementation that saves an .xshd file as XML.
+ /// </summary>
+ public sealed class SaveXshdVisitor : IXshdVisitor
+ {
+ /// <summary>
+ /// XML namespace for XSHD.
+ /// </summary>
+ public const string Namespace = V2Loader.Namespace;
+
+ XmlWriter writer;
+
+ /// <summary>
+ /// Creates a new SaveXshdVisitor instance.
+ /// </summary>
+ public SaveXshdVisitor(XmlWriter writer)
+ {
+ if (writer == null)
+ throw new ArgumentNullException("writer");
+ this.writer = writer;
+ }
+
+ /// <summary>
+ /// Writes the specified syntax definition.
+ /// </summary>
+ public void WriteDefinition(XshdSyntaxDefinition definition)
+ {
+ if (definition == null)
+ throw new ArgumentNullException("definition");
+ writer.WriteStartElement("SyntaxDefinition", Namespace);
+ if (definition.Name != null)
+ writer.WriteAttributeString("name", definition.Name);
+ if (definition.Extensions != null)
+ writer.WriteAttributeString("extensions", string.Join(";", definition.Extensions.ToArray()));
+
+ definition.AcceptElements(this);
+
+ writer.WriteEndElement();
+ }
+
+ object IXshdVisitor.VisitRuleSet(XshdRuleSet ruleSet)
+ {
+ writer.WriteStartElement("RuleSet", Namespace);
+
+ if (ruleSet.Name != null)
+ writer.WriteAttributeString("name", ruleSet.Name);
+ WriteBoolAttribute("ignoreCase", ruleSet.IgnoreCase);
+
+ ruleSet.AcceptElements(this);
+
+ writer.WriteEndElement();
+ return null;
+ }
+
+ void WriteBoolAttribute(string attributeName, bool? value)
+ {
+ if (value != null) {
+ writer.WriteAttributeString(attributeName, value.Value ? "true" : "false");
+ }
+ }
+
+ void WriteRuleSetReference(XshdReference<XshdRuleSet> ruleSetReference)
+ {
+ if (ruleSetReference.ReferencedElement != null) {
+ if (ruleSetReference.ReferencedDefinition != null)
+ writer.WriteAttributeString("ruleSet", ruleSetReference.ReferencedDefinition + "/" + ruleSetReference.ReferencedElement);
+ else
+ writer.WriteAttributeString("ruleSet", ruleSetReference.ReferencedElement);
+ }
+ }
+
+ void WriteColorReference(XshdReference<XshdColor> color)
+ {
+ if (color.InlineElement != null) {
+ WriteColorAttributes(color.InlineElement);
+ } else if (color.ReferencedElement != null) {
+ if (color.ReferencedDefinition != null)
+ writer.WriteAttributeString("color", color.ReferencedDefinition + "/" + color.ReferencedElement);
+ else
+ writer.WriteAttributeString("color", color.ReferencedElement);
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "The file format requires lowercase, and all possible values are English-only")]
+ void WriteColorAttributes(XshdColor color)
+ {
+ if (color.Foreground != null)
+ writer.WriteAttributeString("foreground", color.Foreground.ToString());
+ if (color.Background != null)
+ writer.WriteAttributeString("background", color.Background.ToString());
+ if (color.FontWeight != null)
+ writer.WriteAttributeString("fontWeight", V2Loader.FontWeightConverter.ConvertToInvariantString(color.FontWeight.Value).ToLowerInvariant());
+ if (color.FontStyle != null)
+ writer.WriteAttributeString("fontStyle", V2Loader.FontStyleConverter.ConvertToInvariantString(color.FontStyle.Value).ToLowerInvariant());
+ }
+
+ object IXshdVisitor.VisitColor(XshdColor color)
+ {
+ writer.WriteStartElement("Color", Namespace);
+ if (color.Name != null)
+ writer.WriteAttributeString("name", color.Name);
+ WriteColorAttributes(color);
+ if (color.ExampleText != null)
+ writer.WriteAttributeString("exampleText", color.ExampleText);
+ writer.WriteEndElement();
+ return null;
+ }
+
+ object IXshdVisitor.VisitKeywords(XshdKeywords keywords)
+ {
+ writer.WriteStartElement("Keywords", Namespace);
+ WriteColorReference(keywords.ColorReference);
+ foreach (string word in keywords.Words) {
+ writer.WriteElementString("Word", Namespace, word);
+ }
+ writer.WriteEndElement();
+ return null;
+ }
+
+ object IXshdVisitor.VisitSpan(XshdSpan span)
+ {
+ writer.WriteStartElement("Span", Namespace);
+ WriteColorReference(span.SpanColorReference);
+ if (span.BeginRegexType == XshdRegexType.Default && span.BeginRegex != null)
+ writer.WriteAttributeString("begin", span.BeginRegex);
+ if (span.EndRegexType == XshdRegexType.Default && span.EndRegex != null)
+ writer.WriteAttributeString("end", span.EndRegex);
+ WriteRuleSetReference(span.RuleSetReference);
+ if (span.Multiline)
+ writer.WriteAttributeString("multiline", "true");
+
+ if (span.BeginRegexType == XshdRegexType.IgnorePatternWhitespace)
+ WriteBeginEndElement("Begin", span.BeginRegex, span.BeginColorReference);
+ if (span.EndRegexType == XshdRegexType.IgnorePatternWhitespace)
+ WriteBeginEndElement("End", span.EndRegex, span.EndColorReference);
+
+ if (span.RuleSetReference.InlineElement != null)
+ span.RuleSetReference.InlineElement.AcceptVisitor(this);
+
+ writer.WriteEndElement();
+ return null;
+ }
+
+ void WriteBeginEndElement(string elementName, string regex, XshdReference<XshdColor> colorReference)
+ {
+ if (regex != null) {
+ writer.WriteStartElement(elementName, Namespace);
+ WriteColorReference(colorReference);
+ writer.WriteString(regex);
+ writer.WriteEndElement();
+ }
+ }
+
+ object IXshdVisitor.VisitImport(XshdImport import)
+ {
+ writer.WriteStartElement("Import", Namespace);
+ WriteRuleSetReference(import.RuleSetReference);
+ writer.WriteEndElement();
+ return null;
+ }
+
+ object IXshdVisitor.VisitRule(XshdRule rule)
+ {
+ writer.WriteStartElement("Rule", Namespace);
+ WriteColorReference(rule.ColorReference);
+
+ writer.WriteString(rule.Regex);
+
+ writer.WriteEndElement();
+ return null;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V1Loader.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V1Loader.cs
new file mode 100644
index 000000000..f3caa7eda
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V1Loader.cs
@@ -0,0 +1,325 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Media;
+using System.Xml;
+using System.Xml.Schema;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// Loads .xshd files, version 1.0.
+ /// </summary>
+ sealed class V1Loader
+ {
+ static XmlSchemaSet schemaSet;
+
+ static XmlSchemaSet SchemaSet {
+ get {
+ if (schemaSet == null) {
+ schemaSet = HighlightingLoader.LoadSchemaSet(new XmlTextReader(
+ Resources.OpenStream("ModeV1.xsd")));
+ }
+ return schemaSet;
+ }
+ }
+
+ public static XshdSyntaxDefinition LoadDefinition(XmlReader reader, bool skipValidation)
+ {
+ reader = HighlightingLoader.GetValidatingReader(reader, false, skipValidation ? null : SchemaSet);
+ XmlDocument document = new XmlDocument();
+ document.Load(reader);
+ V1Loader loader = new V1Loader();
+ return loader.ParseDefinition(document.DocumentElement);
+ }
+
+ XshdSyntaxDefinition ParseDefinition(XmlElement syntaxDefinition)
+ {
+ XshdSyntaxDefinition def = new XshdSyntaxDefinition();
+ def.Name = syntaxDefinition.GetAttributeOrNull("name");
+ if (syntaxDefinition.HasAttribute("extensions")) {
+ def.Extensions.AddRange(syntaxDefinition.GetAttribute("extensions").Split(';', '|'));
+ }
+
+ XshdRuleSet mainRuleSetElement = null;
+ foreach (XmlElement element in syntaxDefinition.GetElementsByTagName("RuleSet")) {
+ XshdRuleSet ruleSet = ImportRuleSet(element);
+ def.Elements.Add(ruleSet);
+ if (ruleSet.Name == null)
+ mainRuleSetElement = ruleSet;
+
+ if (syntaxDefinition["Digits"] != null) {
+ // create digit highlighting rule
+
+ const string optionalExponent = @"([eE][+-]?[0-9]+)?";
+ const string floatingPoint = @"\.[0-9]+";
+ ruleSet.Elements.Add(
+ new XshdRule {
+ ColorReference = GetColorReference(syntaxDefinition["Digits"]),
+ RegexType = XshdRegexType.IgnorePatternWhitespace,
+ Regex = @"\b0[xX][0-9a-fA-F]+"
+ + @"|"
+ + @"(\b\d+(" + floatingPoint + ")?"
+ + @"|" + floatingPoint + ")"
+ + optionalExponent
+ });
+ }
+ }
+
+ if (syntaxDefinition.HasAttribute("extends") && mainRuleSetElement != null) {
+ // convert 'extends="HTML"' to '<Import ruleSet="HTML/" />' in main rule set.
+ mainRuleSetElement.Elements.Add(
+ new XshdImport { RuleSetReference = new XshdReference<XshdRuleSet>(
+ syntaxDefinition.GetAttribute("extends"), string.Empty
+ ) });
+ }
+ return def;
+ }
+
+ static XshdColor GetColorFromElement(XmlElement element)
+ {
+ if (!element.HasAttribute("bold") && !element.HasAttribute("italic") && !element.HasAttribute("color") && !element.HasAttribute("bgcolor"))
+ return null;
+ XshdColor color = new XshdColor();
+ if (element.HasAttribute("bold"))
+ color.FontWeight = XmlConvert.ToBoolean(element.GetAttribute("bold")) ? FontWeights.Bold : FontWeights.Normal;
+ if (element.HasAttribute("italic"))
+ color.FontStyle = XmlConvert.ToBoolean(element.GetAttribute("italic")) ? FontStyles.Italic : FontStyles.Normal;
+ if (element.HasAttribute("color"))
+ color.Foreground = ParseColor(element.GetAttribute("color"));
+ if (element.HasAttribute("bgcolor"))
+ color.Background = ParseColor(element.GetAttribute("bgcolor"));
+ return color;
+ }
+
+ static XshdReference<XshdColor> GetColorReference(XmlElement element)
+ {
+ XshdColor color = GetColorFromElement(element);
+ if (color != null)
+ return new XshdReference<XshdColor>(color);
+ else
+ return new XshdReference<XshdColor>();
+ }
+
+ static HighlightingBrush ParseColor(string c)
+ {
+ if (c.StartsWith("#", StringComparison.Ordinal)) {
+ int a = 255;
+ int offset = 0;
+ if (c.Length > 7) {
+ offset = 2;
+ a = Int32.Parse(c.Substring(1,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ }
+
+ int r = Int32.Parse(c.Substring(1 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ int g = Int32.Parse(c.Substring(3 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ int b = Int32.Parse(c.Substring(5 + offset,2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ return new SimpleHighlightingBrush(Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b));
+ } else if (c.StartsWith("SystemColors.", StringComparison.Ordinal)) {
+ return V2Loader.GetSystemColorBrush(null, c);
+ } else {
+ return new SimpleHighlightingBrush((Color)V2Loader.ColorConverter.ConvertFromInvariantString(c));
+ }
+ }
+
+ char ruleSetEscapeCharacter;
+
+ XshdRuleSet ImportRuleSet(XmlElement element)
+ {
+ XshdRuleSet ruleSet = new XshdRuleSet();
+ ruleSet.Name = element.GetAttributeOrNull("name");
+
+ if (element.HasAttribute("escapecharacter")) {
+ ruleSetEscapeCharacter = element.GetAttribute("escapecharacter")[0];
+ } else {
+ ruleSetEscapeCharacter = '\0';
+ }
+
+ if (element.HasAttribute("reference")) {
+ ruleSet.Elements.Add(
+ new XshdImport { RuleSetReference = new XshdReference<XshdRuleSet>(
+ element.GetAttribute("reference"), string.Empty
+ ) });
+ }
+ ruleSet.IgnoreCase = element.GetBoolAttribute("ignorecase");
+
+ foreach (XmlElement el in element.GetElementsByTagName("KeyWords")) {
+ XshdKeywords keywords = new XshdKeywords();
+ keywords.ColorReference = GetColorReference(el);
+ // we have to handle old syntax highlighting definitions that contain
+ // empty keywords or empty keyword groups
+ foreach (XmlElement node in el.GetElementsByTagName("Key")) {
+ string word = node.GetAttribute("word");
+ if (!string.IsNullOrEmpty(word))
+ keywords.Words.Add(word);
+ }
+ if (keywords.Words.Count > 0) {
+ ruleSet.Elements.Add(keywords);
+ }
+ }
+
+ foreach (XmlElement el in element.GetElementsByTagName("Span")) {
+ ruleSet.Elements.Add(ImportSpan(el));
+ }
+
+ foreach (XmlElement el in element.GetElementsByTagName("MarkPrevious")) {
+ ruleSet.Elements.Add(ImportMarkPrevNext(el, false));
+ }
+ foreach (XmlElement el in element.GetElementsByTagName("MarkFollowing")) {
+ ruleSet.Elements.Add(ImportMarkPrevNext(el, true));
+ }
+
+ return ruleSet;
+ }
+
+ static XshdRule ImportMarkPrevNext(XmlElement el, bool markFollowing)
+ {
+ bool markMarker = el.GetBoolAttribute("markmarker") ?? false;
+ string what = Regex.Escape(el.InnerText);
+ const string identifier = @"[\d\w_]+";
+ const string whitespace = @"\s*";
+
+ string regex;
+ if (markFollowing) {
+ if (markMarker) {
+ regex = what + whitespace + identifier;
+ } else {
+ regex = "(?<=(" + what + whitespace + "))" + identifier;
+ }
+ } else {
+ if (markMarker) {
+ regex = identifier + whitespace + what;
+ } else {
+ regex = identifier + "(?=(" + whitespace + what + "))";
+ }
+ }
+ return new XshdRule {
+ ColorReference = GetColorReference(el),
+ Regex = regex,
+ RegexType = XshdRegexType.IgnorePatternWhitespace
+ };
+ }
+
+ XshdSpan ImportSpan(XmlElement element)
+ {
+ XshdSpan span = new XshdSpan();
+ if (element.HasAttribute("rule")) {
+ span.RuleSetReference = new XshdReference<XshdRuleSet>(null, element.GetAttribute("rule"));
+ }
+ char escapeCharacter = ruleSetEscapeCharacter;
+ if (element.HasAttribute("escapecharacter")) {
+ escapeCharacter = element.GetAttribute("escapecharacter")[0];
+ }
+ span.Multiline = !(element.GetBoolAttribute("stopateol") ?? false);
+
+ span.SpanColorReference = GetColorReference(element);
+
+ span.BeginRegexType = XshdRegexType.IgnorePatternWhitespace;
+ span.BeginRegex = ImportRegex(element["Begin"].InnerText,
+ element["Begin"].GetBoolAttribute("singleword") ?? false,
+ element["Begin"].GetBoolAttribute("startofline"));
+ span.BeginColorReference = GetColorReference(element["Begin"]);
+
+ string endElementText = string.Empty;
+ if (element["End"] != null) {
+ span.EndRegexType = XshdRegexType.IgnorePatternWhitespace;
+ endElementText = element["End"].InnerText;
+ span.EndRegex = ImportRegex(endElementText,
+ element["End"].GetBoolAttribute("singleword") ?? false,
+ null);
+ span.EndColorReference = GetColorReference(element["End"]);
+ }
+
+ if (escapeCharacter != '\0') {
+ XshdRuleSet ruleSet = new XshdRuleSet();
+ if (endElementText.Length == 1 && endElementText[0] == escapeCharacter) {
+ // ""-style escape
+ ruleSet.Elements.Add(new XshdSpan {
+ BeginRegex = Regex.Escape(endElementText + endElementText),
+ EndRegex = ""
+ });
+ } else {
+ // \"-style escape
+ ruleSet.Elements.Add(new XshdSpan {
+ BeginRegex = Regex.Escape(escapeCharacter.ToString()),
+ EndRegex = "."
+ });
+ }
+ if (span.RuleSetReference.ReferencedElement != null) {
+ ruleSet.Elements.Add(new XshdImport { RuleSetReference = span.RuleSetReference });
+ }
+ span.RuleSetReference = new XshdReference<XshdRuleSet>(ruleSet);
+ }
+ return span;
+ }
+
+ static string ImportRegex(string expr, bool singleWord, bool? startOfLine)
+ {
+ StringBuilder b = new StringBuilder();
+ if (startOfLine != null) {
+ if (startOfLine.Value) {
+ b.Append(@"(?<=(^\s*))");
+ } else {
+ b.Append(@"(?<!(^\s*))");
+ }
+ } else {
+ if (singleWord)
+ b.Append(@"\b");
+ }
+ for (int i = 0; i < expr.Length; i++) {
+ char c = expr[i];
+ if (c == '@') {
+ ++i;
+ if (i == expr.Length)
+ throw new HighlightingDefinitionInvalidException("Unexpected end of @ sequence, use @@ to look for a single @.");
+ switch (expr[i]) {
+ case 'C': // match whitespace or punctuation
+ b.Append(@"[^\w\d_]");
+ break;
+ case '!': // negative lookahead
+ {
+ StringBuilder whatmatch = new StringBuilder();
+ ++i;
+ while (i < expr.Length && expr[i] != '@') {
+ whatmatch.Append(expr[i++]);
+ }
+ b.Append("(?!(");
+ b.Append(Regex.Escape(whatmatch.ToString()));
+ b.Append("))");
+ }
+ break;
+ case '-': // negative lookbehind
+ {
+ StringBuilder whatmatch = new StringBuilder();
+ ++i;
+ while (i < expr.Length && expr[i] != '@') {
+ whatmatch.Append(expr[i++]);
+ }
+ b.Append("(?<!(");
+ b.Append(Regex.Escape(whatmatch.ToString()));
+ b.Append("))");
+ }
+ break;
+ case '@':
+ b.Append("@");
+ break;
+ default:
+ throw new HighlightingDefinitionInvalidException("Unknown character in @ sequence.");
+ }
+ } else {
+ b.Append(Regex.Escape(c.ToString()));
+ }
+ }
+ if (singleWord)
+ b.Append(@"\b");
+ return b.ToString();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V2Loader.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V2Loader.cs
new file mode 100644
index 000000000..ab2dbfc93
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/V2Loader.cs
@@ -0,0 +1,334 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Media;
+using System.Xml;
+using System.Xml.Schema;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// Loads .xshd files, version 2.0.
+ /// Version 2.0 files are recognized by the namespace.
+ /// </summary>
+ static class V2Loader
+ {
+ public const string Namespace = "http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008";
+
+ static XmlSchemaSet schemaSet;
+
+ static XmlSchemaSet SchemaSet {
+ get {
+ if (schemaSet == null) {
+ schemaSet = HighlightingLoader.LoadSchemaSet(new XmlTextReader(
+ Resources.OpenStream("ModeV2.xsd")));
+ }
+ return schemaSet;
+ }
+ }
+
+ public static XshdSyntaxDefinition LoadDefinition(XmlReader reader, bool skipValidation)
+ {
+ reader = HighlightingLoader.GetValidatingReader(reader, true, skipValidation ? null : SchemaSet);
+ reader.Read();
+ return ParseDefinition(reader);
+ }
+
+ static XshdSyntaxDefinition ParseDefinition(XmlReader reader)
+ {
+ Debug.Assert(reader.LocalName == "SyntaxDefinition");
+ XshdSyntaxDefinition def = new XshdSyntaxDefinition();
+ def.Name = reader.GetAttribute("name");
+ string extensions = reader.GetAttribute("extensions");
+ if (extensions != null)
+ def.Extensions.AddRange(extensions.Split(';'));
+ ParseElements(def.Elements, reader);
+ Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
+ Debug.Assert(reader.LocalName == "SyntaxDefinition");
+ return def;
+ }
+
+ static void ParseElements(ICollection<XshdElement> c, XmlReader reader)
+ {
+ if (reader.IsEmptyElement)
+ return;
+ while (reader.Read() && reader.NodeType != XmlNodeType.EndElement) {
+ Debug.Assert(reader.NodeType == XmlNodeType.Element);
+ if (reader.NamespaceURI != Namespace) {
+ if (!reader.IsEmptyElement)
+ reader.Skip();
+ continue;
+ }
+ switch (reader.Name) {
+ case "RuleSet":
+ c.Add(ParseRuleSet(reader));
+ break;
+ case "Property":
+ c.Add(ParseProperty(reader));
+ break;
+ case "Color":
+ c.Add(ParseNamedColor(reader));
+ break;
+ case "Keywords":
+ c.Add(ParseKeywords(reader));
+ break;
+ case "Span":
+ c.Add(ParseSpan(reader));
+ break;
+ case "Import":
+ c.Add(ParseImport(reader));
+ break;
+ case "Rule":
+ c.Add(ParseRule(reader));
+ break;
+ default:
+ throw new NotSupportedException("Unknown element " + reader.Name);
+ }
+ }
+ }
+
+ static XshdElement ParseProperty(XmlReader reader)
+ {
+ XshdProperty property = new XshdProperty();
+ SetPosition(property, reader);
+ property.Name = reader.GetAttribute("name");
+ property.Value = reader.GetAttribute("value");
+ return property;
+ }
+
+ static XshdRuleSet ParseRuleSet(XmlReader reader)
+ {
+ XshdRuleSet ruleSet = new XshdRuleSet();
+ SetPosition(ruleSet, reader);
+ ruleSet.Name = reader.GetAttribute("name");
+ ruleSet.IgnoreCase = reader.GetBoolAttribute("ignoreCase");
+
+ CheckElementName(reader, ruleSet.Name);
+ ParseElements(ruleSet.Elements, reader);
+ return ruleSet;
+ }
+
+ static XshdRule ParseRule(XmlReader reader)
+ {
+ XshdRule rule = new XshdRule();
+ SetPosition(rule, reader);
+ rule.ColorReference = ParseColorReference(reader);
+ if (!reader.IsEmptyElement) {
+ reader.Read();
+ if (reader.NodeType == XmlNodeType.Text) {
+ rule.Regex = reader.ReadContentAsString();
+ rule.RegexType = XshdRegexType.IgnorePatternWhitespace;
+ }
+ }
+ return rule;
+ }
+
+ static XshdKeywords ParseKeywords(XmlReader reader)
+ {
+ XshdKeywords keywords = new XshdKeywords();
+ SetPosition(keywords, reader);
+ keywords.ColorReference = ParseColorReference(reader);
+ reader.Read();
+ while (reader.NodeType != XmlNodeType.EndElement) {
+ Debug.Assert(reader.NodeType == XmlNodeType.Element);
+ keywords.Words.Add(reader.ReadElementString());
+ }
+ return keywords;
+ }
+
+ static XshdImport ParseImport(XmlReader reader)
+ {
+ XshdImport import = new XshdImport();
+ SetPosition(import, reader);
+ import.RuleSetReference = ParseRuleSetReference(reader);
+ if (!reader.IsEmptyElement)
+ reader.Skip();
+ return import;
+ }
+
+ static XshdSpan ParseSpan(XmlReader reader)
+ {
+ XshdSpan span = new XshdSpan();
+ SetPosition(span, reader);
+ span.BeginRegex = reader.GetAttribute("begin");
+ span.EndRegex = reader.GetAttribute("end");
+ span.Multiline = reader.GetBoolAttribute("multiline") ?? false;
+ span.SpanColorReference = ParseColorReference(reader);
+ span.RuleSetReference = ParseRuleSetReference(reader);
+ if (!reader.IsEmptyElement) {
+ reader.Read();
+ while (reader.NodeType != XmlNodeType.EndElement) {
+ Debug.Assert(reader.NodeType == XmlNodeType.Element);
+ switch (reader.Name) {
+ case "Begin":
+ if (span.BeginRegex != null)
+ throw Error(reader, "Duplicate Begin regex");
+ span.BeginColorReference = ParseColorReference(reader);
+ span.BeginRegex = reader.ReadElementString();
+ span.BeginRegexType = XshdRegexType.IgnorePatternWhitespace;
+ break;
+ case "End":
+ if (span.EndRegex != null)
+ throw Error(reader, "Duplicate End regex");
+ span.EndColorReference = ParseColorReference(reader);
+ span.EndRegex = reader.ReadElementString();
+ span.EndRegexType = XshdRegexType.IgnorePatternWhitespace;
+ break;
+ case "RuleSet":
+ if (span.RuleSetReference.ReferencedElement != null)
+ throw Error(reader, "Cannot specify both inline RuleSet and RuleSet reference");
+ span.RuleSetReference = new XshdReference<XshdRuleSet>(ParseRuleSet(reader));
+ reader.Read();
+ break;
+ default:
+ throw new NotSupportedException("Unknown element " + reader.Name);
+ }
+ }
+ }
+ return span;
+ }
+
+ static Exception Error(XmlReader reader, string message)
+ {
+ return Error(reader as IXmlLineInfo, message);
+ }
+
+ static Exception Error(IXmlLineInfo lineInfo, string message)
+ {
+ if (lineInfo != null)
+ return new HighlightingDefinitionInvalidException(HighlightingLoader.FormatExceptionMessage(message, lineInfo.LineNumber, lineInfo.LinePosition));
+ else
+ return new HighlightingDefinitionInvalidException(message);
+ }
+
+ /// <summary>
+ /// Sets the element's position to the XmlReader's position.
+ /// </summary>
+ static void SetPosition(XshdElement element, XmlReader reader)
+ {
+ IXmlLineInfo lineInfo = reader as IXmlLineInfo;
+ if (lineInfo != null) {
+ element.LineNumber = lineInfo.LineNumber;
+ element.ColumnNumber = lineInfo.LinePosition;
+ }
+ }
+
+ static XshdReference<XshdRuleSet> ParseRuleSetReference(XmlReader reader)
+ {
+ string ruleSet = reader.GetAttribute("ruleSet");
+ if (ruleSet != null) {
+ // '/' is valid in highlighting definition names, so we need the last occurence
+ int pos = ruleSet.LastIndexOf('/');
+ if (pos >= 0) {
+ return new XshdReference<XshdRuleSet>(ruleSet.Substring(0, pos), ruleSet.Substring(pos + 1));
+ } else {
+ return new XshdReference<XshdRuleSet>(null, ruleSet);
+ }
+ } else {
+ return new XshdReference<XshdRuleSet>();
+ }
+ }
+
+ static void CheckElementName(XmlReader reader, string name)
+ {
+ if (name != null) {
+ if (name.Length == 0)
+ throw Error(reader, "The empty string is not a valid name.");
+ if (name.IndexOf('/') >= 0)
+ throw Error(reader, "Element names must not contain a slash.");
+ }
+ }
+
+ #region ParseColor
+ static XshdColor ParseNamedColor(XmlReader reader)
+ {
+ XshdColor color = ParseColorAttributes(reader);
+ // check removed: invisible named colors may be useful now that apps can read highlighting data
+ //if (color.Foreground == null && color.FontWeight == null && color.FontStyle == null)
+ // throw Error(reader, "A named color must have at least one element.");
+ color.Name = reader.GetAttribute("name");
+ CheckElementName(reader, color.Name);
+ color.ExampleText = reader.GetAttribute("exampleText");
+ return color;
+ }
+
+ static XshdReference<XshdColor> ParseColorReference(XmlReader reader)
+ {
+ string color = reader.GetAttribute("color");
+ if (color != null) {
+ int pos = color.LastIndexOf('/');
+ if (pos >= 0) {
+ return new XshdReference<XshdColor>(color.Substring(0, pos), color.Substring(pos + 1));
+ } else {
+ return new XshdReference<XshdColor>(null, color);
+ }
+ } else {
+ return new XshdReference<XshdColor>(ParseColorAttributes(reader));
+ }
+ }
+
+ static XshdColor ParseColorAttributes(XmlReader reader)
+ {
+ XshdColor color = new XshdColor();
+ SetPosition(color, reader);
+ IXmlLineInfo position = reader as IXmlLineInfo;
+ color.Foreground = ParseColor(position, reader.GetAttribute("foreground"));
+ color.Background = ParseColor(position, reader.GetAttribute("background"));
+ color.FontWeight = ParseFontWeight(reader.GetAttribute("fontWeight"));
+ color.FontStyle = ParseFontStyle(reader.GetAttribute("fontStyle"));
+ return color;
+ }
+
+ internal readonly static ColorConverter ColorConverter = new ColorConverter();
+ internal readonly static FontWeightConverter FontWeightConverter = new FontWeightConverter();
+ internal readonly static FontStyleConverter FontStyleConverter = new FontStyleConverter();
+
+ static HighlightingBrush ParseColor(IXmlLineInfo lineInfo, string color)
+ {
+ if (string.IsNullOrEmpty(color))
+ return null;
+ if (color.StartsWith("SystemColors.", StringComparison.Ordinal))
+ return GetSystemColorBrush(lineInfo, color);
+ else
+ return FixedColorHighlightingBrush((Color?)ColorConverter.ConvertFromInvariantString(color));
+ }
+
+ internal static SystemColorHighlightingBrush GetSystemColorBrush(IXmlLineInfo lineInfo, string name)
+ {
+ Debug.Assert(name.StartsWith("SystemColors.", StringComparison.Ordinal));
+ string shortName = name.Substring(13);
+ var property = typeof(SystemColors).GetProperty(shortName + "Brush");
+ if (property == null)
+ throw Error(lineInfo, "Cannot find '" + name + "'.");
+ return new SystemColorHighlightingBrush(property);
+ }
+
+ static HighlightingBrush FixedColorHighlightingBrush(Color? color)
+ {
+ if (color == null)
+ return null;
+ return new SimpleHighlightingBrush(color.Value);
+ }
+
+ static FontWeight? ParseFontWeight(string fontWeight)
+ {
+ if (string.IsNullOrEmpty(fontWeight))
+ return null;
+ return (FontWeight?)FontWeightConverter.ConvertFromInvariantString(fontWeight);
+ }
+
+ static FontStyle? ParseFontStyle(string fontStyle)
+ {
+ if (string.IsNullOrEmpty(fontStyle))
+ return null;
+ return (FontStyle?)FontStyleConverter.ConvertFromInvariantString(fontStyle);
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XmlHighlightingDefinition.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XmlHighlightingDefinition.cs
new file mode 100644
index 000000000..3f52e5d87
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XmlHighlightingDefinition.cs
@@ -0,0 +1,406 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Text.RegularExpressions;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ [Serializable]
+ sealed class XmlHighlightingDefinition : IHighlightingDefinition2
+ {
+ public string Name { get; private set; }
+
+ public XmlHighlightingDefinition(XshdSyntaxDefinition xshd, IHighlightingDefinitionReferenceResolver resolver)
+ {
+ this.Name = xshd.Name;
+ // Create HighlightingRuleSet instances
+ var rnev = new RegisterNamedElementsVisitor(this);
+ xshd.AcceptElements(rnev);
+ // Assign MainRuleSet so that references can be resolved
+ foreach (XshdElement element in xshd.Elements) {
+ XshdRuleSet xrs = element as XshdRuleSet;
+ if (xrs != null && xrs.Name == null) {
+ if (MainRuleSet != null)
+ throw Error(element, "Duplicate main RuleSet. There must be only one nameless RuleSet!");
+ else
+ MainRuleSet = rnev.ruleSets[xrs];
+ }
+ }
+ if (MainRuleSet == null)
+ throw new HighlightingDefinitionInvalidException("Could not find main RuleSet.");
+ // Translate elements within the rulesets (resolving references and processing imports)
+ xshd.AcceptElements(new TranslateElementVisitor(this, rnev.ruleSets, resolver));
+
+ foreach (var p in xshd.Elements.OfType<XshdProperty>())
+ propDict.Add(p.Name, p.Value);
+ }
+
+ #region RegisterNamedElements
+ sealed class RegisterNamedElementsVisitor : IXshdVisitor
+ {
+ XmlHighlightingDefinition def;
+ internal readonly Dictionary<XshdRuleSet, HighlightingRuleSet> ruleSets
+ = new Dictionary<XshdRuleSet, HighlightingRuleSet>();
+
+ public RegisterNamedElementsVisitor(XmlHighlightingDefinition def)
+ {
+ this.def = def;
+ }
+
+ public object VisitRuleSet(XshdRuleSet ruleSet)
+ {
+ HighlightingRuleSet hrs = new HighlightingRuleSet();
+ ruleSets.Add(ruleSet, hrs);
+ if (ruleSet.Name != null) {
+ if (ruleSet.Name.Length == 0)
+ throw Error(ruleSet, "Name must not be the empty string");
+ if (def.ruleSetDict.ContainsKey(ruleSet.Name))
+ throw Error(ruleSet, "Duplicate rule set name '" + ruleSet.Name + "'.");
+
+ def.ruleSetDict.Add(ruleSet.Name, hrs);
+ }
+ ruleSet.AcceptElements(this);
+ return null;
+ }
+
+ public object VisitColor(XshdColor color)
+ {
+ if (color.Name != null) {
+ if (color.Name.Length == 0)
+ throw Error(color, "Name must not be the empty string");
+ if (def.colorDict.ContainsKey(color.Name))
+ throw Error(color, "Duplicate color name '" + color.Name + "'.");
+
+ def.colorDict.Add(color.Name, new HighlightingColor());
+ }
+ return null;
+ }
+
+ public object VisitKeywords(XshdKeywords keywords)
+ {
+ return keywords.ColorReference.AcceptVisitor(this);
+ }
+
+ public object VisitSpan(XshdSpan span)
+ {
+ span.BeginColorReference.AcceptVisitor(this);
+ span.SpanColorReference.AcceptVisitor(this);
+ span.EndColorReference.AcceptVisitor(this);
+ return span.RuleSetReference.AcceptVisitor(this);
+ }
+
+ public object VisitImport(XshdImport import)
+ {
+ return import.RuleSetReference.AcceptVisitor(this);
+ }
+
+ public object VisitRule(XshdRule rule)
+ {
+ return rule.ColorReference.AcceptVisitor(this);
+ }
+ }
+ #endregion
+
+ #region TranslateElements
+ sealed class TranslateElementVisitor : IXshdVisitor
+ {
+ readonly XmlHighlightingDefinition def;
+ readonly Dictionary<XshdRuleSet, HighlightingRuleSet> ruleSetDict;
+ readonly Dictionary<HighlightingRuleSet, XshdRuleSet> reverseRuleSetDict;
+ readonly IHighlightingDefinitionReferenceResolver resolver;
+ HashSet<XshdRuleSet> processingStartedRuleSets = new HashSet<XshdRuleSet>();
+ HashSet<XshdRuleSet> processedRuleSets = new HashSet<XshdRuleSet>();
+ bool ignoreCase;
+
+ public TranslateElementVisitor(XmlHighlightingDefinition def, Dictionary<XshdRuleSet, HighlightingRuleSet> ruleSetDict, IHighlightingDefinitionReferenceResolver resolver)
+ {
+ Debug.Assert(def != null);
+ Debug.Assert(ruleSetDict != null);
+ this.def = def;
+ this.ruleSetDict = ruleSetDict;
+ this.resolver = resolver;
+ reverseRuleSetDict = new Dictionary<HighlightingRuleSet, XshdRuleSet>();
+ foreach (var pair in ruleSetDict) {
+ reverseRuleSetDict.Add(pair.Value, pair.Key);
+ }
+ }
+
+ public object VisitRuleSet(XshdRuleSet ruleSet)
+ {
+ HighlightingRuleSet rs = ruleSetDict[ruleSet];
+ if (processedRuleSets.Contains(ruleSet))
+ return rs;
+ if (!processingStartedRuleSets.Add(ruleSet))
+ throw Error(ruleSet, "RuleSet cannot be processed because it contains cyclic <Import>");
+
+ bool oldIgnoreCase = ignoreCase;
+ if (ruleSet.IgnoreCase != null)
+ ignoreCase = ruleSet.IgnoreCase.Value;
+
+ rs.Name = ruleSet.Name;
+
+ foreach (XshdElement element in ruleSet.Elements) {
+ object o = element.AcceptVisitor(this);
+ HighlightingRuleSet elementRuleSet = o as HighlightingRuleSet;
+ if (elementRuleSet != null) {
+ Merge(rs, elementRuleSet);
+ } else {
+ HighlightingSpan span = o as HighlightingSpan;
+ if (span != null) {
+ rs.Spans.Add(span);
+ } else {
+ HighlightingRule elementRule = o as HighlightingRule;
+ if (elementRule != null) {
+ rs.Rules.Add(elementRule);
+ }
+ }
+ }
+ }
+
+ ignoreCase = oldIgnoreCase;
+ processedRuleSets.Add(ruleSet);
+
+ return rs;
+ }
+
+ static void Merge(HighlightingRuleSet target, HighlightingRuleSet source)
+ {
+ target.Rules.AddRange(source.Rules);
+ target.Spans.AddRange(source.Spans);
+ }
+
+ public object VisitColor(XshdColor color)
+ {
+ HighlightingColor c;
+ if (color.Name != null)
+ c = def.colorDict[color.Name];
+ else if (color.Foreground == null && color.FontStyle == null && color.FontWeight == null)
+ return null;
+ else
+ c = new HighlightingColor();
+
+ c.Name = color.Name;
+ c.Foreground = color.Foreground;
+ c.Background = color.Background;
+ c.FontStyle = color.FontStyle;
+ c.FontWeight = color.FontWeight;
+ return c;
+ }
+
+ public object VisitKeywords(XshdKeywords keywords)
+ {
+ if (keywords.Words.Count == 0)
+ return Error(keywords, "Keyword group must not be empty.");
+ foreach (string keyword in keywords.Words) {
+ if (string.IsNullOrEmpty(keyword))
+ throw Error(keywords, "Cannot use empty string as keyword");
+ }
+ StringBuilder keyWordRegex = new StringBuilder();
+ // We can use "\b" only where the keyword starts/ends with a letter or digit, otherwise we don't
+ // highlight correctly. (example: ILAsm-Mode.xshd with ".maxstack" keyword)
+ if (keywords.Words.All(IsSimpleWord)) {
+ keyWordRegex.Append(@"\b(?>");
+ // (?> = atomic group
+ // atomic groups increase matching performance, but we
+ // must ensure that the keywords are sorted correctly.
+ // "\b(?>in|int)\b" does not match "int" because the atomic group captures "in".
+ // To solve this, we are sorting the keywords by descending length.
+ int i = 0;
+ foreach (string keyword in keywords.Words.OrderByDescending(w=>w.Length)) {
+ if (i++ > 0)
+ keyWordRegex.Append('|');
+ keyWordRegex.Append(Regex.Escape(keyword));
+ }
+ keyWordRegex.Append(@")\b");
+ } else {
+ keyWordRegex.Append('(');
+ int i = 0;
+ foreach (string keyword in keywords.Words) {
+ if (i++ > 0)
+ keyWordRegex.Append('|');
+ if (char.IsLetterOrDigit(keyword[0]))
+ keyWordRegex.Append(@"\b");
+ keyWordRegex.Append(Regex.Escape(keyword));
+ if (char.IsLetterOrDigit(keyword[keyword.Length - 1]))
+ keyWordRegex.Append(@"\b");
+ }
+ keyWordRegex.Append(')');
+ }
+ return new HighlightingRule {
+ Color = GetColor(keywords, keywords.ColorReference),
+ Regex = CreateRegex(keywords, keyWordRegex.ToString(), XshdRegexType.Default)
+ };
+ }
+
+ static bool IsSimpleWord(string word)
+ {
+ return char.IsLetterOrDigit(word[0]) && char.IsLetterOrDigit(word, word.Length - 1);
+ }
+
+ Regex CreateRegex(XshdElement position, string regex, XshdRegexType regexType)
+ {
+ if (regex == null)
+ throw Error(position, "Regex missing");
+ RegexOptions options = RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture;
+ if (regexType == XshdRegexType.IgnorePatternWhitespace)
+ options |= RegexOptions.IgnorePatternWhitespace;
+ if (ignoreCase)
+ options |= RegexOptions.IgnoreCase;
+ try {
+ return new Regex(regex, options);
+ } catch (ArgumentException ex) {
+ throw Error(position, ex.Message);
+ }
+ }
+
+ HighlightingColor GetColor(XshdElement position, XshdReference<XshdColor> colorReference)
+ {
+ if (colorReference.InlineElement != null) {
+ return (HighlightingColor)colorReference.InlineElement.AcceptVisitor(this);
+ } else if (colorReference.ReferencedElement != null) {
+ IHighlightingDefinition definition = GetDefinition(position, colorReference.ReferencedDefinition);
+ HighlightingColor color = definition.GetNamedColor(colorReference.ReferencedElement);
+ if (color == null)
+ throw Error(position, "Could not find color named '" + colorReference.ReferencedElement + "'.");
+ return color;
+ } else {
+ return null;
+ }
+ }
+
+ IHighlightingDefinition GetDefinition(XshdElement position, string definitionName)
+ {
+ if (definitionName == null)
+ return def;
+ if (resolver == null)
+ throw Error(position, "Resolving references to other syntax definitions is not possible because the IHighlightingDefinitionReferenceResolver is null.");
+ IHighlightingDefinition d = resolver.GetDefinition(definitionName);
+ if (d == null)
+ throw Error(position, "Could not find definition with name '" + definitionName + "'.");
+ return d;
+ }
+
+ HighlightingRuleSet GetRuleSet(XshdElement position, XshdReference<XshdRuleSet> ruleSetReference)
+ {
+ if (ruleSetReference.InlineElement != null) {
+ return (HighlightingRuleSet)ruleSetReference.InlineElement.AcceptVisitor(this);
+ } else if (ruleSetReference.ReferencedElement != null) {
+ IHighlightingDefinition definition = GetDefinition(position, ruleSetReference.ReferencedDefinition);
+ HighlightingRuleSet ruleSet = definition.GetNamedRuleSet(ruleSetReference.ReferencedElement);
+ if (ruleSet == null)
+ throw Error(position, "Could not find rule set named '" + ruleSetReference.ReferencedElement + "'.");
+ return ruleSet;
+ } else {
+ return null;
+ }
+ }
+
+ public object VisitSpan(XshdSpan span)
+ {
+ string endRegex = span.EndRegex;
+ if (string.IsNullOrEmpty(span.BeginRegex) && string.IsNullOrEmpty(span.EndRegex))
+ throw Error(span, "Span has no start/end regex.");
+ if (!span.Multiline) {
+ if (endRegex == null)
+ endRegex = "$";
+ else if (span.EndRegexType == XshdRegexType.IgnorePatternWhitespace)
+ endRegex = "($|" + endRegex + "\n)";
+ else
+ endRegex = "($|" + endRegex + ")";
+ }
+ HighlightingColor wholeSpanColor = GetColor(span, span.SpanColorReference);
+ return new HighlightingSpan {
+ StartExpression = CreateRegex(span, span.BeginRegex, span.BeginRegexType),
+ EndExpression = CreateRegex(span, endRegex, span.EndRegexType),
+ RuleSet = GetRuleSet(span, span.RuleSetReference),
+ StartColor = GetColor(span, span.BeginColorReference),
+ SpanColor = wholeSpanColor,
+ EndColor = GetColor(span, span.EndColorReference),
+ SpanColorIncludesStart = true,
+ SpanColorIncludesEnd = true
+ };
+ }
+
+ public object VisitImport(XshdImport import)
+ {
+ HighlightingRuleSet hrs = GetRuleSet(import, import.RuleSetReference);
+ XshdRuleSet inputRuleSet;
+ if (reverseRuleSetDict.TryGetValue(hrs, out inputRuleSet)) {
+ // ensure the ruleset is processed before importing its members
+ if (VisitRuleSet(inputRuleSet) != hrs)
+ Debug.Fail("this shouldn't happen");
+ }
+ return hrs;
+ }
+
+ public object VisitRule(XshdRule rule)
+ {
+ return new HighlightingRule {
+ Color = GetColor(rule, rule.ColorReference),
+ Regex = CreateRegex(rule, rule.Regex, rule.RegexType)
+ };
+ }
+ }
+ #endregion
+
+ static Exception Error(XshdElement element, string message)
+ {
+ if (element.LineNumber > 0)
+ return new HighlightingDefinitionInvalidException(
+ "Error at line " + element.LineNumber + ":\n" + message);
+ else
+ return new HighlightingDefinitionInvalidException(message);
+ }
+
+ Dictionary<string, HighlightingRuleSet> ruleSetDict = new Dictionary<string, HighlightingRuleSet>();
+ Dictionary<string, HighlightingColor> colorDict = new Dictionary<string, HighlightingColor>();
+ [OptionalField]
+ Dictionary<string, string> propDict = new Dictionary<string, string>();
+
+ public HighlightingRuleSet MainRuleSet { get; private set; }
+
+ public HighlightingRuleSet GetNamedRuleSet(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ return MainRuleSet;
+ HighlightingRuleSet r;
+ if (ruleSetDict.TryGetValue(name, out r))
+ return r;
+ else
+ return null;
+ }
+
+ public HighlightingColor GetNamedColor(string name)
+ {
+ HighlightingColor c;
+ if (colorDict.TryGetValue(name, out c))
+ return c;
+ else
+ return null;
+ }
+
+ public IEnumerable<HighlightingColor> NamedHighlightingColors {
+ get {
+ return colorDict.Values;
+ }
+ }
+
+ public override string ToString()
+ {
+ return this.Name;
+ }
+
+ public IDictionary<string, string> Properties {
+ get {
+ return propDict;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdColor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdColor.cs
new file mode 100644
index 000000000..bbc5abb98
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdColor.cs
@@ -0,0 +1,97 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Runtime.Serialization;
+using System.Security.Permissions;
+using System.Windows;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// A color in an Xshd file.
+ /// </summary>
+ [Serializable]
+ public class XshdColor : XshdElement, ISerializable
+ {
+ /// <summary>
+ /// Gets/sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets/sets the foreground brush.
+ /// </summary>
+ public HighlightingBrush Foreground { get; set; }
+
+ /// <summary>
+ /// Gets/sets the background brush.
+ /// </summary>
+ public HighlightingBrush Background { get; set; }
+
+ /// <summary>
+ /// Gets/sets the font weight.
+ /// </summary>
+ public FontWeight? FontWeight { get; set; }
+
+ /// <summary>
+ /// Gets/sets the font style.
+ /// </summary>
+ public FontStyle? FontStyle { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the example text that demonstrates where the color is used.
+ /// </summary>
+ public string ExampleText { get; set; }
+
+ /// <summary>
+ /// Creates a new XshdColor instance.
+ /// </summary>
+ public XshdColor()
+ {
+ }
+
+ /// <summary>
+ /// Deserializes an XshdColor.
+ /// </summary>
+ protected XshdColor(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException("info");
+ this.Name = info.GetString("Name");
+ this.Foreground = (HighlightingBrush)info.GetValue("Foreground", typeof(HighlightingBrush));
+ this.Background = (HighlightingBrush)info.GetValue("Background", typeof(HighlightingBrush));
+ if (info.GetBoolean("HasWeight"))
+ this.FontWeight = System.Windows.FontWeight.FromOpenTypeWeight(info.GetInt32("Weight"));
+ if (info.GetBoolean("HasStyle"))
+ this.FontStyle = (FontStyle?)new FontStyleConverter().ConvertFromInvariantString(info.GetString("Style"));
+ this.ExampleText = info.GetString("ExampleText");
+ }
+
+ /// <summary>
+ /// Serializes this XshdColor instance.
+ /// </summary>
+ [System.Security.SecurityCritical]
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException("info");
+ info.AddValue("Name", this.Name);
+ info.AddValue("Foreground", this.Foreground);
+ info.AddValue("Background", this.Background);
+ info.AddValue("HasWeight", this.FontWeight.HasValue);
+ if (this.FontWeight.HasValue)
+ info.AddValue("Weight", this.FontWeight.Value.ToOpenTypeWeight());
+ info.AddValue("HasStyle", this.FontStyle.HasValue);
+ if (this.FontStyle.HasValue)
+ info.AddValue("Style", this.FontStyle.Value.ToString());
+ info.AddValue("ExampleText", this.ExampleText);
+ }
+
+ /// <inheritdoc/>
+ public override object AcceptVisitor(IXshdVisitor visitor)
+ {
+ return visitor.VisitColor(this);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdElement.cs
new file mode 100644
index 000000000..d7634e63e
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdElement.cs
@@ -0,0 +1,29 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// An element in a XSHD rule set.
+ /// </summary>
+ [Serializable]
+ public abstract class XshdElement
+ {
+ /// <summary>
+ /// Gets the line number in the .xshd file.
+ /// </summary>
+ public int LineNumber { get; set; }
+
+ /// <summary>
+ /// Gets the column number in the .xshd file.
+ /// </summary>
+ public int ColumnNumber { get; set; }
+
+ /// <summary>
+ /// Applies the visitor to this element.
+ /// </summary>
+ public abstract object AcceptVisitor(IXshdVisitor visitor);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdImport.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdImport.cs
new file mode 100644
index 000000000..68ecdd2d0
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdImport.cs
@@ -0,0 +1,25 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// &lt;Import&gt; element.
+ /// </summary>
+ [Serializable]
+ public class XshdImport : XshdElement
+ {
+ /// <summary>
+ /// Gets/sets the referenced rule set.
+ /// </summary>
+ public XshdReference<XshdRuleSet> RuleSetReference { get; set; }
+
+ /// <inheritdoc/>
+ public override object AcceptVisitor(IXshdVisitor visitor)
+ {
+ return visitor.VisitImport(this);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdKeywords.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdKeywords.cs
new file mode 100644
index 000000000..245f34ae5
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdKeywords.cs
@@ -0,0 +1,36 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// A list of keywords.
+ /// </summary>
+ [Serializable]
+ public class XshdKeywords : XshdElement
+ {
+ /// <summary>
+ /// The color.
+ /// </summary>
+ public XshdReference<XshdColor> ColorReference { get; set; }
+
+ readonly NullSafeCollection<string> words = new NullSafeCollection<string>();
+
+ /// <summary>
+ /// Gets the list of key words.
+ /// </summary>
+ public IList<string> Words {
+ get { return words; }
+ }
+
+ /// <inheritdoc/>
+ public override object AcceptVisitor(IXshdVisitor visitor)
+ {
+ return visitor.VisitKeywords(this);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdProperty.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdProperty.cs
new file mode 100644
index 000000000..3ca65f4b4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdProperty.cs
@@ -0,0 +1,38 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// A property in an Xshd file.
+ /// </summary>
+ [Serializable]
+ public class XshdProperty : XshdElement
+ {
+ /// <summary>
+ /// Gets/sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets/sets the value.
+ /// </summary>
+ public string Value { get; set; }
+
+ /// <summary>
+ /// Creates a new XshdColor instance.
+ /// </summary>
+ public XshdProperty()
+ {
+ }
+
+ /// <inheritdoc/>
+ public override object AcceptVisitor(IXshdVisitor visitor)
+ {
+ return null;
+// return visitor.VisitProperty(this);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdReference.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdReference.cs
new file mode 100644
index 000000000..363cd881c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdReference.cs
@@ -0,0 +1,127 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// A reference to an xshd color, or an inline xshd color.
+ /// </summary>
+ [Serializable]
+ public struct XshdReference<T> : IEquatable<XshdReference<T>> where T : XshdElement
+ {
+ string referencedDefinition;
+ string referencedElement;
+ T inlineElement;
+
+ /// <summary>
+ /// Gets the reference.
+ /// </summary>
+ public string ReferencedDefinition {
+ get { return referencedDefinition; }
+ }
+
+ /// <summary>
+ /// Gets the reference.
+ /// </summary>
+ public string ReferencedElement {
+ get { return referencedElement; }
+ }
+
+ /// <summary>
+ /// Gets the inline element.
+ /// </summary>
+ public T InlineElement {
+ get { return inlineElement; }
+ }
+
+ /// <summary>
+ /// Creates a new XshdReference instance.
+ /// </summary>
+ public XshdReference(string referencedDefinition, string referencedElement)
+ {
+ if (referencedElement == null)
+ throw new ArgumentNullException("referencedElement");
+ this.referencedDefinition = referencedDefinition;
+ this.referencedElement = referencedElement;
+ this.inlineElement = null;
+ }
+
+ /// <summary>
+ /// Creates a new XshdReference instance.
+ /// </summary>
+ public XshdReference(T inlineElement)
+ {
+ if (inlineElement == null)
+ throw new ArgumentNullException("inlineElement");
+ this.referencedDefinition = null;
+ this.referencedElement = null;
+ this.inlineElement = inlineElement;
+ }
+
+ /// <summary>
+ /// Applies the visitor to the inline element, if there is any.
+ /// </summary>
+ public object AcceptVisitor(IXshdVisitor visitor)
+ {
+ if (inlineElement != null)
+ return inlineElement.AcceptVisitor(visitor);
+ else
+ return null;
+ }
+
+ #region Equals and GetHashCode implementation
+ // The code in this region is useful if you want to use this structure in collections.
+ // If you don't need it, you can just remove the region and the ": IEquatable<XshdColorReference>" declaration.
+
+ /// <inheritdoc/>
+ public override bool Equals(object obj)
+ {
+ if (obj is XshdReference<T>)
+ return Equals((XshdReference<T>)obj); // use Equals method below
+ else
+ return false;
+ }
+
+ /// <summary>
+ /// Equality operator.
+ /// </summary>
+ public bool Equals(XshdReference<T> other)
+ {
+ // add comparisions for all members here
+ return this.referencedDefinition == other.referencedDefinition
+ && this.referencedElement == other.referencedElement
+ && this.inlineElement == other.inlineElement;
+ }
+
+ /// <inheritdoc/>
+ public override int GetHashCode()
+ {
+ // combine the hash codes of all members here (e.g. with XOR operator ^)
+ return GetHashCode(referencedDefinition) ^ GetHashCode(referencedElement) ^ GetHashCode(inlineElement);
+ }
+
+ static int GetHashCode(object o)
+ {
+ return o != null ? o.GetHashCode() : 0;
+ }
+
+ /// <summary>
+ /// Equality operator.
+ /// </summary>
+ public static bool operator ==(XshdReference<T> left, XshdReference<T> right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Inequality operator.
+ /// </summary>
+ public static bool operator !=(XshdReference<T> left, XshdReference<T> right)
+ {
+ return !left.Equals(right);
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRule.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRule.cs
new file mode 100644
index 000000000..abca25f69
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRule.cs
@@ -0,0 +1,35 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// &lt;Rule&gt; element.
+ /// </summary>
+ [Serializable]
+ public class XshdRule : XshdElement
+ {
+ /// <summary>
+ /// Gets/sets the rule regex.
+ /// </summary>
+ public string Regex { get; set; }
+
+ /// <summary>
+ /// Gets/sets the rule regex type.
+ /// </summary>
+ public XshdRegexType RegexType { get; set; }
+
+ /// <summary>
+ /// Gets/sets the color reference.
+ /// </summary>
+ public XshdReference<XshdColor> ColorReference { get; set; }
+
+ /// <inheritdoc/>
+ public override object AcceptVisitor(IXshdVisitor visitor)
+ {
+ return visitor.VisitRule(this);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRuleSet.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRuleSet.cs
new file mode 100644
index 000000000..2ffa16143
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdRuleSet.cs
@@ -0,0 +1,51 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// A rule set in a XSHD file.
+ /// </summary>
+ [Serializable]
+ public class XshdRuleSet : XshdElement
+ {
+ /// <summary>
+ /// Gets/Sets the name of the rule set.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets/sets whether the case is ignored in expressions inside this rule set.
+ /// </summary>
+ public bool? IgnoreCase { get; set; }
+
+ readonly NullSafeCollection<XshdElement> elements = new NullSafeCollection<XshdElement>();
+
+ /// <summary>
+ /// Gets the collection of elements.
+ /// </summary>
+ public IList<XshdElement> Elements {
+ get { return elements; }
+ }
+
+ /// <summary>
+ /// Applies the visitor to all elements.
+ /// </summary>
+ public void AcceptElements(IXshdVisitor visitor)
+ {
+ foreach (XshdElement element in Elements) {
+ element.AcceptVisitor(visitor);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override object AcceptVisitor(IXshdVisitor visitor)
+ {
+ return visitor.VisitRuleSet(this);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSpan.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSpan.cs
new file mode 100644
index 000000000..6d32abad4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSpan.cs
@@ -0,0 +1,82 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// Specifies the type of the regex.
+ /// </summary>
+ public enum XshdRegexType
+ {
+ /// <summary>
+ /// Normal regex. Used when the regex was specified as attribute.
+ /// </summary>
+ Default,
+ /// <summary>
+ /// Ignore pattern whitespace / allow regex comments. Used when the regex was specified as text element.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly",
+ Justification = "Using the same case as the RegexOption")]
+ IgnorePatternWhitespace
+ }
+
+ /// <summary>
+ /// &lt;Span&gt; element.
+ /// </summary>
+ [Serializable]
+ public class XshdSpan : XshdElement
+ {
+ /// <summary>
+ /// Gets/sets the begin regex.
+ /// </summary>
+ public string BeginRegex { get; set; }
+
+ /// <summary>
+ /// Gets/sets the begin regex type.
+ /// </summary>
+ public XshdRegexType BeginRegexType { get; set; }
+
+ /// <summary>
+ /// Gets/sets the end regex.
+ /// </summary>
+ public string EndRegex { get; set; }
+
+ /// <summary>
+ /// Gets/sets the end regex type.
+ /// </summary>
+ public XshdRegexType EndRegexType { get; set; }
+
+ /// <summary>
+ /// Gets/sets whether the span is multiline.
+ /// </summary>
+ public bool Multiline { get; set; }
+
+ /// <summary>
+ /// Gets/sets the rule set reference.
+ /// </summary>
+ public XshdReference<XshdRuleSet> RuleSetReference { get; set; }
+
+ /// <summary>
+ /// Gets/sets the span color.
+ /// </summary>
+ public XshdReference<XshdColor> SpanColorReference { get; set; }
+
+ /// <summary>
+ /// Gets/sets the span begin color.
+ /// </summary>
+ public XshdReference<XshdColor> BeginColorReference { get; set; }
+
+ /// <summary>
+ /// Gets/sets the span end color.
+ /// </summary>
+ public XshdReference<XshdColor> EndColorReference { get; set; }
+
+ /// <inheritdoc/>
+ public override object AcceptVisitor(IXshdVisitor visitor)
+ {
+ return visitor.VisitSpan(this);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSyntaxDefinition.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSyntaxDefinition.cs
new file mode 100644
index 000000000..347f14d25
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Highlighting/Xshd/XshdSyntaxDefinition.cs
@@ -0,0 +1,50 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Highlighting.Xshd
+{
+ /// <summary>
+ /// A &lt;SyntaxDefinition&gt; element.
+ /// </summary>
+ [Serializable]
+ public class XshdSyntaxDefinition
+ {
+ /// <summary>
+ /// Creates a new XshdSyntaxDefinition object.
+ /// </summary>
+ public XshdSyntaxDefinition()
+ {
+ this.Elements = new NullSafeCollection<XshdElement>();
+ this.Extensions = new NullSafeCollection<string>();
+ }
+
+ /// <summary>
+ /// Gets/sets the definition name
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets the associated extensions.
+ /// </summary>
+ public IList<string> Extensions { get; private set; }
+
+ /// <summary>
+ /// Gets the collection of elements.
+ /// </summary>
+ public IList<XshdElement> Elements { get; private set; }
+
+ /// <summary>
+ /// Applies the visitor to all elements.
+ /// </summary>
+ public void AcceptElements(IXshdVisitor visitor)
+ {
+ foreach (XshdElement element in Elements) {
+ element.AcceptVisitor(visitor);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs
new file mode 100644
index 000000000..36aeb213b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationHelper.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ class CSharpIndentationHelper
+ {
+ public static string IndentCSharpCode(string code)
+ {
+ const string INDENT_STEP = "\t";
+
+ if (string.IsNullOrWhiteSpace(code))
+ {
+ return code;
+ }
+
+ var result = new StringBuilder();
+ var indent = string.Empty;
+ var lineContent = false;
+ var stringDefinition = false;
+
+ for (var i = 0; i < code.Length; i++)
+ {
+ var ch = code[i];
+
+ if (ch == '"' && !stringDefinition)
+ {
+ result.Append(ch);
+ stringDefinition = true;
+ continue;
+ }
+
+ if (ch == '"' && stringDefinition)
+ {
+ result.Append(ch);
+ stringDefinition = false;
+ continue;
+ }
+
+ if (stringDefinition)
+ {
+ result.Append(ch);
+ continue;
+ }
+
+ if (ch == '{' && !stringDefinition)
+ {
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+ result.Append(indent).Append("{");
+
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+ indent += INDENT_STEP;
+ lineContent = false;
+
+ continue;
+ }
+
+ if (ch == '}' && !stringDefinition)
+ {
+ if (indent.Length != 0)
+ {
+ indent = indent.Substring(0, indent.Length - INDENT_STEP.Length);
+ }
+
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+ result.Append(indent).Append("}");
+
+ if (lineContent)
+ {
+ result.AppendLine();
+ }
+
+
+ lineContent = false;
+
+ continue;
+ }
+
+ if (ch == '\r')
+ {
+ continue;
+ }
+
+ if ((ch == ' ' || ch == '\t') && !lineContent)
+ {
+ continue;
+ }
+
+ if (ch == '\n')
+ {
+ lineContent = false;
+ result.AppendLine();
+
+ continue;
+ }
+
+ if (!lineContent)
+ {
+ result.Append(indent);
+ lineContent = true;
+ }
+
+ result.Append(ch);
+ }
+
+ return result.ToString();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs
new file mode 100644
index 000000000..7b47099fe
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/CSharpIndentationStrategy.cs
@@ -0,0 +1,96 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ /// <summary>
+ /// Smart indentation for C#.
+ /// </summary>
+ public class CSharpIndentationStrategy : DefaultIndentationStrategy
+ {
+ /// <summary>
+ /// Creates a new CSharpIndentationStrategy.
+ /// </summary>
+ public CSharpIndentationStrategy()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new CSharpIndentationStrategy and initializes the settings using the text editor options.
+ /// </summary>
+ public CSharpIndentationStrategy(TextEditorOptions options)
+ {
+ this.IndentationString = options.IndentationString;
+ }
+
+ string indentationString = "\t";
+
+ /// <summary>
+ /// Gets/Sets the indentation string.
+ /// </summary>
+ public string IndentationString
+ {
+ get { return indentationString; }
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ throw new ArgumentException("Indentation string must not be null or empty");
+ indentationString = value;
+ }
+ }
+
+ /// <summary>
+ /// Performs indentation using the specified document accessor.
+ /// </summary>
+ /// <param name="document">Object used for accessing the document line-by-line</param>
+ /// <param name="keepEmptyLines">Specifies whether empty lines should be kept</param>
+ public void Indent(IDocumentAccessor document, bool keepEmptyLines)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ IndentationSettings settings = new IndentationSettings();
+ settings.IndentString = this.IndentationString;
+ settings.LeaveEmptyLines = keepEmptyLines;
+
+ IndentationReformatter r = new IndentationReformatter();
+ r.Reformat(document, settings);
+ }
+
+ /// <inheritdoc cref="IIndentationStrategy.IndentLine"/>
+ public override void IndentLine(TextDocument document, DocumentLine line)
+ {
+ bool keepEmptyLines = true;
+
+ if (line.PreviousLine != null && line.NextLine != null)
+ {
+ string previous_text = document.GetText(line.PreviousLine.Offset, line.PreviousLine.Length);
+ string next_text = document.GetText(line.NextLine.Offset, line.NextLine.Length);
+
+ if (previous_text.Contains("{") && next_text.Contains("}") && previous_text.Length == next_text.Length)
+ {
+ keepEmptyLines = false;
+ }
+ }
+
+ int lineNr = line.LineNumber;
+ TextDocumentAccessor acc = new TextDocumentAccessor(document, lineNr, lineNr);
+ Indent(acc, keepEmptyLines);
+
+ string t = acc.Text;
+ if (t.Length == 0)
+ {
+ // use AutoIndentation for new lines in comments / verbatim strings.
+ base.IndentLine(document, line);
+ }
+ }
+
+ /// <inheritdoc cref="IIndentationStrategy.IndentLines"/>
+ public override void IndentLines(TextDocument document, int beginLine, int endLine)
+ {
+ Indent(new TextDocumentAccessor(document, beginLine, endLine), true);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs
new file mode 100644
index 000000000..dbf6b21a9
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/DocumentAccessor.cs
@@ -0,0 +1,107 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ /// <summary>
+ /// Interface used for the indentation class to access the document.
+ /// </summary>
+ public interface IDocumentAccessor
+ {
+ /// <summary>Gets if the current line is read only (because it is not in the
+ /// selected text region)</summary>
+ bool IsReadOnly { get; }
+ /// <summary>Gets the number of the current line.</summary>
+ int LineNumber { get; }
+ /// <summary>Gets/Sets the text of the current line.</summary>
+ string Text { get; set; }
+ /// <summary>Advances to the next line.</summary>
+ bool MoveNext();
+ }
+
+ #region TextDocumentAccessor
+ /// <summary>
+ /// Adapter IDocumentAccessor -> TextDocument
+ /// </summary>
+ public sealed class TextDocumentAccessor : IDocumentAccessor
+ {
+ readonly TextDocument doc;
+ readonly int minLine;
+ readonly int maxLine;
+
+ /// <summary>
+ /// Creates a new TextDocumentAccessor.
+ /// </summary>
+ public TextDocumentAccessor(TextDocument document)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ doc = document;
+ this.minLine = 1;
+ this.maxLine = doc.LineCount;
+ }
+
+ /// <summary>
+ /// Creates a new TextDocumentAccessor that indents only a part of the document.
+ /// </summary>
+ public TextDocumentAccessor(TextDocument document, int minLine, int maxLine)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ doc = document;
+ this.minLine = minLine;
+ this.maxLine = maxLine;
+ }
+
+ int num;
+ string text;
+ DocumentLine line;
+
+ /// <inheritdoc/>
+ public bool IsReadOnly {
+ get {
+ return num < minLine;
+ }
+ }
+
+ /// <inheritdoc/>
+ public int LineNumber {
+ get {
+ return num;
+ }
+ }
+
+ bool lineDirty;
+
+ /// <inheritdoc/>
+ public string Text {
+ get { return text; }
+ set {
+ if (num < minLine) return;
+ text = value;
+ lineDirty = true;
+ }
+ }
+
+ /// <inheritdoc/>
+ public bool MoveNext()
+ {
+ if (lineDirty) {
+ doc.Replace(line, text);
+ lineDirty = false;
+ }
+ ++num;
+ if (num > maxLine) return false;
+ line = doc.GetLineByNumber(num);
+ text = doc.GetText(line);
+ return true;
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs
new file mode 100644
index 000000000..09e20c909
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/CSharp/IndentationReformatter.cs
@@ -0,0 +1,474 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Indentation.CSharp
+{
+ sealed class IndentationSettings
+ {
+ public string IndentString = "\t";
+ /// <summary>Leave empty lines empty.</summary>
+ public bool LeaveEmptyLines = true;
+ }
+
+ sealed class IndentationReformatter
+ {
+ /// <summary>
+ /// An indentation block. Tracks the state of the indentation.
+ /// </summary>
+ struct Block
+ {
+ /// <summary>
+ /// The indentation outside of the block.
+ /// </summary>
+ public string OuterIndent;
+
+ /// <summary>
+ /// The indentation inside the block.
+ /// </summary>
+ public string InnerIndent;
+
+ /// <summary>
+ /// The last word that was seen inside this block.
+ /// Because parenthesis open a sub-block and thus don't change their parent's LastWord,
+ /// this property can be used to identify the type of block statement (if, while, switch)
+ /// at the position of the '{'.
+ /// </summary>
+ public string LastWord;
+
+ /// <summary>
+ /// The type of bracket that opened this block (, [ or {
+ /// </summary>
+ public char Bracket;
+
+ /// <summary>
+ /// Gets whether there's currently a line continuation going on inside this block.
+ /// </summary>
+ public bool Continuation;
+
+ /// <summary>
+ /// Gets whether there's currently a 'one-line-block' going on. 'one-line-blocks' occur
+ /// with if statements that don't use '{}'. They are not represented by a Block instance on
+ /// the stack, but are instead handled similar to line continuations.
+ /// This property is an integer because there might be multiple nested one-line-blocks.
+ /// As soon as there is a finished statement, OneLineBlock is reset to 0.
+ /// </summary>
+ public int OneLineBlock;
+
+ /// <summary>
+ /// The previous value of one-line-block before it was reset.
+ /// Used to restore the indentation of 'else' to the correct level.
+ /// </summary>
+ public int PreviousOneLineBlock;
+
+ public void ResetOneLineBlock()
+ {
+ PreviousOneLineBlock = OneLineBlock;
+ OneLineBlock = 0;
+ }
+
+ /// <summary>
+ /// Gets the line number where this block started.
+ /// </summary>
+ public int StartLine;
+
+ public void Indent(IndentationSettings set)
+ {
+ Indent(set.IndentString);
+ }
+
+ public void Indent(string indentationString)
+ {
+ OuterIndent = InnerIndent;
+ InnerIndent += indentationString;
+ Continuation = false;
+ ResetOneLineBlock();
+ LastWord = "";
+ }
+
+ public override string ToString()
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "[Block StartLine={0}, LastWord='{1}', Continuation={2}, OneLineBlock={3}, PreviousOneLineBlock={4}]",
+ this.StartLine, this.LastWord, this.Continuation, this.OneLineBlock, this.PreviousOneLineBlock);
+ }
+ }
+
+ StringBuilder wordBuilder;
+ Stack<Block> blocks; // blocks contains all blocks outside of the current
+ Block block; // block is the current block
+
+ bool inString;
+ bool inChar;
+ bool verbatim;
+ bool escape;
+
+ bool lineComment;
+ bool blockComment;
+
+ char lastRealChar; // last non-comment char
+
+ public void Reformat(IDocumentAccessor doc, IndentationSettings set)
+ {
+ Init();
+
+ while (doc.MoveNext()) {
+ Step(doc, set);
+ }
+ }
+
+ public void Init()
+ {
+ wordBuilder = new StringBuilder();
+ blocks = new Stack<Block>();
+ block = new Block();
+ block.InnerIndent = "";
+ block.OuterIndent = "";
+ block.Bracket = '{';
+ block.Continuation = false;
+ block.LastWord = "";
+ block.OneLineBlock = 0;
+ block.PreviousOneLineBlock = 0;
+ block.StartLine = 0;
+
+ inString = false;
+ inChar = false;
+ verbatim = false;
+ escape = false;
+
+ lineComment = false;
+ blockComment = false;
+
+ lastRealChar = ' '; // last non-comment char
+ }
+
+ public void Step(IDocumentAccessor doc, IndentationSettings set)
+ {
+ string line = doc.Text;
+ if (set.LeaveEmptyLines && line.Length == 0) return; // leave empty lines empty
+ line = line.TrimStart();
+
+ StringBuilder indent = new StringBuilder();
+ if (line.Length == 0) {
+ // Special treatment for empty lines:
+ if (blockComment || (inString && verbatim))
+ return;
+ indent.Append(block.InnerIndent);
+ indent.Append(Repeat(set.IndentString, block.OneLineBlock));
+ if (block.Continuation)
+ indent.Append(set.IndentString);
+ if (doc.Text != indent.ToString())
+ doc.Text = indent.ToString();
+ return;
+ }
+
+ if (TrimEnd(doc))
+ line = doc.Text.TrimStart();
+
+ Block oldBlock = block;
+ bool startInComment = blockComment;
+ bool startInString = (inString && verbatim);
+
+ #region Parse char by char
+ lineComment = false;
+ inChar = false;
+ escape = false;
+ if (!verbatim) inString = false;
+
+ lastRealChar = '\n';
+
+ char lastchar = ' ';
+ char c = ' ';
+ char nextchar = line[0];
+ for (int i = 0; i < line.Length; i++) {
+ if (lineComment) break; // cancel parsing current line
+
+ lastchar = c;
+ c = nextchar;
+ if (i + 1 < line.Length)
+ nextchar = line[i + 1];
+ else
+ nextchar = '\n';
+
+ if (escape) {
+ escape = false;
+ continue;
+ }
+
+ #region Check for comment/string chars
+ switch (c) {
+ case '/':
+ if (blockComment && lastchar == '*')
+ blockComment = false;
+ if (!inString && !inChar) {
+ if (!blockComment && nextchar == '/')
+ lineComment = true;
+ if (!lineComment && nextchar == '*')
+ blockComment = true;
+ }
+ break;
+ case '#':
+ if (!(inChar || blockComment || inString))
+ lineComment = true;
+ break;
+ case '"':
+ if (!(inChar || lineComment || blockComment)) {
+ inString = !inString;
+ if (!inString && verbatim) {
+ if (nextchar == '"') {
+ escape = true; // skip escaped quote
+ inString = true;
+ } else {
+ verbatim = false;
+ }
+ } else if (inString && lastchar == '@') {
+ verbatim = true;
+ }
+ }
+ break;
+ case '\'':
+ if (!(inString || lineComment || blockComment)) {
+ inChar = !inChar;
+ }
+ break;
+ case '\\':
+ if ((inString && !verbatim) || inChar)
+ escape = true; // skip next character
+ break;
+ }
+ #endregion
+
+ if (lineComment || blockComment || inString || inChar) {
+ if (wordBuilder.Length > 0)
+ block.LastWord = wordBuilder.ToString();
+ wordBuilder.Length = 0;
+ continue;
+ }
+
+ if (!Char.IsWhiteSpace(c) && c != '[' && c != '/') {
+ if (block.Bracket == '{')
+ block.Continuation = true;
+ }
+
+ if (Char.IsLetterOrDigit(c)) {
+ wordBuilder.Append(c);
+ } else {
+ if (wordBuilder.Length > 0)
+ block.LastWord = wordBuilder.ToString();
+ wordBuilder.Length = 0;
+ }
+
+ #region Push/Pop the blocks
+ switch (c) {
+ case '{':
+ block.ResetOneLineBlock();
+ blocks.Push(block);
+ block.StartLine = doc.LineNumber;
+ if (block.LastWord == "switch") {
+ block.Indent(set.IndentString + set.IndentString);
+ /* oldBlock refers to the previous line, not the previous block
+ * The block we want is not available anymore because it was never pushed.
+ * } else if (oldBlock.OneLineBlock) {
+ // Inside a one-line-block is another statement
+ // with a full block: indent the inner full block
+ // by one additional level
+ block.Indent(set, set.IndentString + set.IndentString);
+ block.OuterIndent += set.IndentString;
+ // Indent current line if it starts with the '{' character
+ if (i == 0) {
+ oldBlock.InnerIndent += set.IndentString;
+ }*/
+ } else {
+ block.Indent(set);
+ }
+ block.Bracket = '{';
+ break;
+ case '}':
+ while (block.Bracket != '{') {
+ if (blocks.Count == 0) break;
+ block = blocks.Pop();
+ }
+ if (blocks.Count == 0) break;
+ block = blocks.Pop();
+ block.Continuation = false;
+ block.ResetOneLineBlock();
+ break;
+ case '(':
+ case '[':
+ blocks.Push(block);
+ if (block.StartLine == doc.LineNumber)
+ block.InnerIndent = block.OuterIndent;
+ else
+ block.StartLine = doc.LineNumber;
+ block.Indent(Repeat(set.IndentString, oldBlock.OneLineBlock) +
+ (oldBlock.Continuation ? set.IndentString : "") +
+ (i == line.Length - 1 ? set.IndentString : new String(' ', i + 1)));
+ block.Bracket = c;
+ break;
+ case ')':
+ if (blocks.Count == 0) break;
+ if (block.Bracket == '(') {
+ block = blocks.Pop();
+ if (IsSingleStatementKeyword(block.LastWord))
+ block.Continuation = false;
+ }
+ break;
+ case ']':
+ if (blocks.Count == 0) break;
+ if (block.Bracket == '[')
+ block = blocks.Pop();
+ break;
+ case ';':
+ case ',':
+ block.Continuation = false;
+ block.ResetOneLineBlock();
+ break;
+ case ':':
+ if (block.LastWord == "case"
+ || line.StartsWith("case ", StringComparison.Ordinal)
+ || line.StartsWith(block.LastWord + ":", StringComparison.Ordinal))
+ {
+ block.Continuation = false;
+ block.ResetOneLineBlock();
+ }
+ break;
+ }
+
+ if (!Char.IsWhiteSpace(c)) {
+ // register this char as last char
+ lastRealChar = c;
+ }
+ #endregion
+ }
+ #endregion
+
+ if (wordBuilder.Length > 0)
+ block.LastWord = wordBuilder.ToString();
+ wordBuilder.Length = 0;
+
+ if (startInString) return;
+ if (startInComment && line[0] != '*') return;
+ if (doc.Text.StartsWith("//\t", StringComparison.Ordinal) || doc.Text == "//")
+ return;
+
+ if (line[0] == '}') {
+ indent.Append(oldBlock.OuterIndent);
+ oldBlock.ResetOneLineBlock();
+ oldBlock.Continuation = false;
+ } else {
+ indent.Append(oldBlock.InnerIndent);
+ }
+
+ if (indent.Length > 0 && oldBlock.Bracket == '(' && line[0] == ')') {
+ indent.Remove(indent.Length - 1, 1);
+ } else if (indent.Length > 0 && oldBlock.Bracket == '[' && line[0] == ']') {
+ indent.Remove(indent.Length - 1, 1);
+ }
+
+ if (line[0] == ':') {
+ oldBlock.Continuation = true;
+ } else if (lastRealChar == ':' && indent.Length >= set.IndentString.Length) {
+ if (block.LastWord == "case" || line.StartsWith("case ", StringComparison.Ordinal) || line.StartsWith(block.LastWord + ":", StringComparison.Ordinal))
+ indent.Remove(indent.Length - set.IndentString.Length, set.IndentString.Length);
+ } else if (lastRealChar == ')') {
+ if (IsSingleStatementKeyword(block.LastWord)) {
+ block.OneLineBlock++;
+ }
+ } else if (lastRealChar == 'e' && block.LastWord == "else") {
+ block.OneLineBlock = Math.Max(1, block.PreviousOneLineBlock);
+ block.Continuation = false;
+ oldBlock.OneLineBlock = block.OneLineBlock - 1;
+ }
+
+ if (doc.IsReadOnly) {
+ // We can't change the current line, but we should accept the existing
+ // indentation if possible (=if the current statement is not a multiline
+ // statement).
+ if (!oldBlock.Continuation && oldBlock.OneLineBlock == 0 &&
+ oldBlock.StartLine == block.StartLine &&
+ block.StartLine < doc.LineNumber && lastRealChar != ':')
+ {
+ // use indent StringBuilder to get the indentation of the current line
+ indent.Length = 0;
+ line = doc.Text; // get untrimmed line
+ for (int i = 0; i < line.Length; ++i) {
+ if (!Char.IsWhiteSpace(line[i]))
+ break;
+ indent.Append(line[i]);
+ }
+ // /* */ multiline comments have an extra space - do not count it
+ // for the block's indentation.
+ if (startInComment && indent.Length > 0 && indent[indent.Length - 1] == ' ') {
+ indent.Length -= 1;
+ }
+ block.InnerIndent = indent.ToString();
+ }
+ return;
+ }
+
+ if (line[0] != '{') {
+ if (line[0] != ')' && oldBlock.Continuation && oldBlock.Bracket == '{')
+ indent.Append(set.IndentString);
+ indent.Append(Repeat(set.IndentString, oldBlock.OneLineBlock));
+ }
+
+ // this is only for blockcomment lines starting with *,
+ // all others keep their old indentation
+ if (startInComment)
+ indent.Append(' ');
+
+ if (indent.Length != (doc.Text.Length - line.Length) ||
+ !doc.Text.StartsWith(indent.ToString(), StringComparison.Ordinal) ||
+ Char.IsWhiteSpace(doc.Text[indent.Length]))
+ {
+ doc.Text = indent.ToString() + line;
+ }
+ }
+
+ static string Repeat(string text, int count)
+ {
+ if (count == 0)
+ return string.Empty;
+ if (count == 1)
+ return text;
+ StringBuilder b = new StringBuilder(text.Length * count);
+ for (int i = 0; i < count; i++)
+ b.Append(text);
+ return b.ToString();
+ }
+
+ static bool IsSingleStatementKeyword(string keyword)
+ {
+ switch (keyword) {
+ case "if":
+ case "for":
+ case "while":
+ case "do":
+ case "foreach":
+ case "using":
+ case "lock":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool TrimEnd(IDocumentAccessor doc)
+ {
+ string line = doc.Text;
+ if (!Char.IsWhiteSpace(line[line.Length - 1])) return false;
+
+ // one space after an empty comment is allowed
+ if (line.EndsWith("// ", StringComparison.Ordinal) || line.EndsWith("* ", StringComparison.Ordinal))
+ return false;
+
+ doc.Text = line.TrimEnd();
+ return true;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs
new file mode 100644
index 000000000..b5a1f0c2c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/DefaultIndentationStrategy.cs
@@ -0,0 +1,40 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Indentation
+{
+ /// <summary>
+ /// Handles indentation by copying the indentation from the previous line.
+ /// Does not support indenting multiple lines.
+ /// </summary>
+ public class DefaultIndentationStrategy : IIndentationStrategy
+ {
+ /// <inheritdoc/>
+ public virtual void IndentLine(TextDocument document, DocumentLine line)
+ {
+ if (document == null)
+ throw new ArgumentNullException("document");
+ if (line == null)
+ throw new ArgumentNullException("line");
+ DocumentLine previousLine = line.PreviousLine;
+ if (previousLine != null) {
+ ISegment indentationSegment = TextUtilities.GetWhitespaceAfter(document, previousLine.Offset);
+ string indentation = document.GetText(indentationSegment);
+ // copy indentation to line
+ indentationSegment = TextUtilities.GetWhitespaceAfter(document, line.Offset);
+ document.Replace(indentationSegment, indentation);
+ }
+ }
+
+ /// <summary>
+ /// Does nothing: indenting multiple lines is useless without a smart indentation strategy.
+ /// </summary>
+ public virtual void IndentLines(TextDocument document, int beginLine, int endLine)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs
new file mode 100644
index 000000000..0697fb642
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Indentation/IIndentationStrategy.cs
@@ -0,0 +1,25 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Document;
+using System;
+
+namespace Tango.Scripting.Editors.Indentation
+{
+ /// <summary>
+ /// Strategy how the text editor handles indentation when new lines are inserted.
+ /// </summary>
+ public interface IIndentationStrategy
+ {
+ /// <summary>
+ /// Sets the indentation for the specified line.
+ /// Usually this is constructed from the indentation of the previous line.
+ /// </summary>
+ void IndentLine(TextDocument document, DocumentLine line);
+
+ /// <summary>
+ /// Reindents a set of lines.
+ /// </summary>
+ void IndentLines(TextDocument document, int beginLine, int endLine);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/AssemblyInfo.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..73d5cf2eb
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/AssemblyInfo.cs
@@ -0,0 +1,43 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+#region Using directives
+
+using System;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Markup;
+
+#endregion
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: System.Runtime.InteropServices.ComVisible(false)]
+[assembly: AssemblyCompany("Tango")]
+[assembly: AssemblyProduct("Tango.Scripting.Editors")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: NeutralResourcesLanguage("en-US")]
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly",
+ Justification = "AssemblyInformationalVersion does not need to be a parsable version")]
+[assembly: AssemblyTitle("Tango.Scripting.Editors")]
+[assembly: AssemblyDescription("WPF-based extensible text editor")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: CLSCompliant(true)]
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/CodeAnalysisDictionary.xml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/CodeAnalysisDictionary.xml
new file mode 100644
index 000000000..71301b91d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Properties/CodeAnalysisDictionary.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<Dictionary>
+ <Words>
+ <Recognized>
+ <!-- add words specific to your application here -->
+ <Word>Uncollapse</Word>
+ <Word>Foldings</Word>
+ <Word>Xshd</Word>
+ <Word>Utils</Word>
+ <Word>Deque</Word>
+ <Word>Colorizer</Word>
+ <Word>Renderer</Word>
+ <Word>Renderers</Word>
+ <Word>Deletable</Word>
+ <!-- required so that FxCop accepts 'yPositionMode' -->
+ <Word>y</Word>
+ </Recognized>
+ <Unrecognized>
+ <!-- Disable Lineup as a single word - LineUp is the spelling used in WPF -->
+ <Word>Lineup</Word>
+ </Unrecognized>
+ <Deprecated>
+ <!-- Use this section to deprecate terms -->
+ <Term PreferredAlternate="Best">Bestest</Term>
+ </Deprecated>
+ </Words>
+ <Acronyms>
+ <CasingExceptions>
+ <!-- Use this section to tell FxCop the correct casing of acronyms. -->
+ <Acronym>WiX</Acronym>
+ </CasingExceptions>
+ </Acronyms>
+</Dictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/BackgroundGeometryBuilder.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/BackgroundGeometryBuilder.cs
new file mode 100644
index 000000000..d3c839b35
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/BackgroundGeometryBuilder.cs
@@ -0,0 +1,343 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls.Primitives;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Helper for creating a PathGeometry.
+ /// </summary>
+ public sealed class BackgroundGeometryBuilder
+ {
+ double cornerRadius;
+
+ /// <summary>
+ /// Gets/sets the radius of the rounded corners.
+ /// </summary>
+ public double CornerRadius {
+ get { return cornerRadius; }
+ set { cornerRadius = value; }
+ }
+
+ /// <summary>
+ /// Gets/Sets whether to align the geometry to whole pixels.
+ /// </summary>
+ public bool AlignToWholePixels { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether to align the geometry to the middle of pixels.
+ /// </summary>
+ public bool AlignToMiddleOfPixels { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether to extend the rectangles to full width at line end.
+ /// </summary>
+ public bool ExtendToFullWidthAtLineEnd { get; set; }
+
+ /// <summary>
+ /// Creates a new BackgroundGeometryBuilder instance.
+ /// </summary>
+ public BackgroundGeometryBuilder()
+ {
+ }
+
+ /// <summary>
+ /// Adds the specified segment to the geometry.
+ /// </summary>
+ public void AddSegment(TextView textView, ISegment segment)
+ {
+ if (textView == null)
+ throw new ArgumentNullException("textView");
+ Size pixelSize = PixelSnapHelpers.GetPixelSize(textView);
+ foreach (Rect r in GetRectsForSegment(textView, segment, ExtendToFullWidthAtLineEnd)) {
+ AddRectangle(pixelSize, r);
+ }
+ }
+
+ /// <summary>
+ /// Adds a rectangle to the geometry.
+ /// </summary>
+ /// <remarks>
+ /// This overload will align the coordinates according to
+ /// <see cref="AlignToWholePixels"/> or <see cref="AlignToMiddleOfPixels"/>.
+ /// Use the <see cref="AddRectangle(double,double,double,double)"/>-overload instead if the coordinates should not be aligned.
+ /// </remarks>
+ public void AddRectangle(TextView textView, Rect rectangle)
+ {
+ AddRectangle(PixelSnapHelpers.GetPixelSize(textView), rectangle);
+ }
+
+ void AddRectangle(Size pixelSize, Rect r)
+ {
+ if (AlignToWholePixels) {
+ AddRectangle(PixelSnapHelpers.Round(r.Left, pixelSize.Width),
+ PixelSnapHelpers.Round(r.Top + 1, pixelSize.Height),
+ PixelSnapHelpers.Round(r.Right, pixelSize.Width),
+ PixelSnapHelpers.Round(r.Bottom + 1, pixelSize.Height));
+ } else if (AlignToMiddleOfPixels) {
+ AddRectangle(PixelSnapHelpers.PixelAlign(r.Left, pixelSize.Width),
+ PixelSnapHelpers.PixelAlign(r.Top + 1, pixelSize.Height),
+ PixelSnapHelpers.PixelAlign(r.Right, pixelSize.Width),
+ PixelSnapHelpers.PixelAlign(r.Bottom + 1, pixelSize.Height));
+ } else {
+ AddRectangle(r.Left, r.Top + 1, r.Right, r.Bottom + 1);
+ }
+ }
+
+ /// <summary>
+ /// Calculates the list of rectangle where the segment in shown.
+ /// This method usually returns one rectangle for each line inside the segment
+ /// (but potentially more, e.g. when bidirectional text is involved).
+ /// </summary>
+ public static IEnumerable<Rect> GetRectsForSegment(TextView textView, ISegment segment, bool extendToFullWidthAtLineEnd = false)
+ {
+ if (textView == null)
+ throw new ArgumentNullException("textView");
+ if (segment == null)
+ throw new ArgumentNullException("segment");
+ return GetRectsForSegmentImpl(textView, segment, extendToFullWidthAtLineEnd);
+ }
+
+ static IEnumerable<Rect> GetRectsForSegmentImpl(TextView textView, ISegment segment, bool extendToFullWidthAtLineEnd)
+ {
+ int segmentStart = segment.Offset;
+ int segmentEnd = segment.Offset + segment.Length;
+
+ segmentStart = segmentStart.CoerceValue(0, textView.Document.TextLength);
+ segmentEnd = segmentEnd.CoerceValue(0, textView.Document.TextLength);
+
+ TextViewPosition start;
+ TextViewPosition end;
+
+ if (segment is SelectionSegment) {
+ SelectionSegment sel = (SelectionSegment)segment;
+ start = new TextViewPosition(textView.Document.GetLocation(sel.StartOffset), sel.StartVisualColumn);
+ end = new TextViewPosition(textView.Document.GetLocation(sel.EndOffset), sel.EndVisualColumn);
+ } else {
+ start = new TextViewPosition(textView.Document.GetLocation(segmentStart), -1);
+ end = new TextViewPosition(textView.Document.GetLocation(segmentEnd), -1);
+ }
+
+ foreach (VisualLine vl in textView.VisualLines) {
+ int vlStartOffset = vl.FirstDocumentLine.Offset;
+ if (vlStartOffset > segmentEnd)
+ break;
+ int vlEndOffset = vl.LastDocumentLine.Offset + vl.LastDocumentLine.Length;
+ if (vlEndOffset < segmentStart)
+ continue;
+
+ int segmentStartVC;
+ if (segmentStart < vlStartOffset)
+ segmentStartVC = 0;
+ else
+ segmentStartVC = vl.ValidateVisualColumn(start, extendToFullWidthAtLineEnd);
+
+ int segmentEndVC;
+ if (segmentEnd > vlEndOffset)
+ segmentEndVC = extendToFullWidthAtLineEnd ? int.MaxValue : vl.VisualLengthWithEndOfLineMarker;
+ else
+ segmentEndVC = vl.ValidateVisualColumn(end, extendToFullWidthAtLineEnd);
+
+ foreach (var rect in ProcessTextLines(textView, vl, segmentStartVC, segmentEndVC))
+ yield return rect;
+ }
+ }
+
+ /// <summary>
+ /// Calculates the rectangles for the visual column segment.
+ /// This returns one rectangle for each line inside the segment.
+ /// </summary>
+ public static IEnumerable<Rect> GetRectsFromVisualSegment(TextView textView, VisualLine line, int startVC, int endVC)
+ {
+ if (textView == null)
+ throw new ArgumentNullException("textView");
+ if (line == null)
+ throw new ArgumentNullException("line");
+ return ProcessTextLines(textView, line, startVC, endVC);
+ }
+
+ static IEnumerable<Rect> ProcessTextLines(TextView textView, VisualLine visualLine, int segmentStartVC, int segmentEndVC)
+ {
+ TextLine lastTextLine = visualLine.TextLines.Last();
+ Vector scrollOffset = textView.ScrollOffset;
+
+ for (int i = 0; i < visualLine.TextLines.Count; i++) {
+ TextLine line = visualLine.TextLines[i];
+ double y = visualLine.GetTextLineVisualYPosition(line, VisualYPosition.LineTop);
+ int visualStartCol = visualLine.GetTextLineVisualStartColumn(line);
+ int visualEndCol = visualStartCol + line.Length;
+ if (line != lastTextLine)
+ visualEndCol -= line.TrailingWhitespaceLength;
+
+ if (segmentEndVC < visualStartCol)
+ break;
+ if (lastTextLine != line && segmentStartVC > visualEndCol)
+ continue;
+ int segmentStartVCInLine = Math.Max(segmentStartVC, visualStartCol);
+ int segmentEndVCInLine = Math.Min(segmentEndVC, visualEndCol);
+ y -= scrollOffset.Y;
+ if (segmentStartVCInLine == segmentEndVCInLine) {
+ // GetTextBounds crashes for length=0, so we'll handle this case with GetDistanceFromCharacterHit
+ // We need to return a rectangle to ensure empty lines are still visible
+ double pos = visualLine.GetTextLineVisualXPosition(line, segmentStartVCInLine);
+ pos -= scrollOffset.X;
+ // The following special cases are necessary to get rid of empty rectangles at the end of a TextLine if "Show Spaces" is active.
+ // If not excluded once, the same rectangle is calculated (and added) twice (since the offset could be mapped to two visual positions; end/start of line), if there is no trailing whitespace.
+ // Skip this TextLine segment, if it is at the end of this line and this line is not the last line of the VisualLine and the selection continues and there is no trailing whitespace.
+ if (segmentEndVCInLine == visualEndCol && i < visualLine.TextLines.Count - 1 && segmentEndVC > segmentEndVCInLine && line.TrailingWhitespaceLength == 0)
+ continue;
+ if (segmentStartVCInLine == visualStartCol && i > 0 && segmentStartVC < segmentStartVCInLine && visualLine.TextLines[i - 1].TrailingWhitespaceLength == 0)
+ continue;
+ yield return new Rect(pos, y, 1, line.Height);
+ } else {
+ Rect lastRect = Rect.Empty;
+ if (segmentStartVCInLine <= visualEndCol) {
+ foreach (TextBounds b in line.GetTextBounds(segmentStartVCInLine, segmentEndVCInLine - segmentStartVCInLine)) {
+ double left = b.Rectangle.Left - scrollOffset.X;
+ double right = b.Rectangle.Right - scrollOffset.X;
+ if (!lastRect.IsEmpty)
+ yield return lastRect;
+ // left>right is possible in RTL languages
+ lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
+ }
+ }
+ if (segmentEndVC >= visualLine.VisualLengthWithEndOfLineMarker) {
+ double left = (segmentStartVC > visualLine.VisualLengthWithEndOfLineMarker ? visualLine.GetTextLineVisualXPosition(lastTextLine, segmentStartVC) : line.Width) - scrollOffset.X;
+ double right = ((segmentEndVC == int.MaxValue || line != lastTextLine) ? Math.Max(((IScrollInfo)textView).ExtentWidth, ((IScrollInfo)textView).ViewportWidth) : visualLine.GetTextLineVisualXPosition(lastTextLine, segmentEndVC)) - scrollOffset.X;
+ Rect extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
+ if (!lastRect.IsEmpty) {
+ if (extendSelection.IntersectsWith(lastRect)) {
+ lastRect.Union(extendSelection);
+ yield return lastRect;
+ } else {
+ yield return lastRect;
+ yield return extendSelection;
+ }
+ } else
+ yield return extendSelection;
+ } else
+ yield return lastRect;
+ }
+ }
+ }
+
+ PathFigureCollection figures = new PathFigureCollection();
+ PathFigure figure;
+ int insertionIndex;
+ double lastTop, lastBottom;
+ double lastLeft, lastRight;
+
+ /// <summary>
+ /// Adds a rectangle to the geometry.
+ /// </summary>
+ /// <remarks>
+ /// This overload assumes that the coordinates are aligned properly
+ /// (see <see cref="AlignToWholePixels"/>, <see cref="AlignToMiddleOfPixels"/>).
+ /// Use the <see cref="AddRectangle(TextView,Rect)"/>-overload instead if the coordinates are not yet aligned.
+ /// </remarks>
+ public void AddRectangle(double left, double top, double right, double bottom)
+ {
+ if (!top.IsClose(lastBottom)) {
+ CloseFigure();
+ }
+ if (figure == null) {
+ figure = new PathFigure();
+ figure.StartPoint = new Point(left, top + cornerRadius);
+ if (Math.Abs(left - right) > cornerRadius) {
+ figure.Segments.Add(MakeArc(left + cornerRadius, top, SweepDirection.Clockwise));
+ figure.Segments.Add(MakeLineSegment(right - cornerRadius, top));
+ figure.Segments.Add(MakeArc(right, top + cornerRadius, SweepDirection.Clockwise));
+ }
+ figure.Segments.Add(MakeLineSegment(right, bottom - cornerRadius));
+ insertionIndex = figure.Segments.Count;
+ //figure.Segments.Add(MakeArc(left, bottom - cornerRadius, SweepDirection.Clockwise));
+ } else {
+ if (!lastRight.IsClose(right)) {
+ double cr = right < lastRight ? -cornerRadius : cornerRadius;
+ SweepDirection dir1 = right < lastRight ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
+ SweepDirection dir2 = right < lastRight ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
+ figure.Segments.Insert(insertionIndex++, MakeArc(lastRight + cr, lastBottom, dir1));
+ figure.Segments.Insert(insertionIndex++, MakeLineSegment(right - cr, top));
+ figure.Segments.Insert(insertionIndex++, MakeArc(right, top + cornerRadius, dir2));
+ }
+ figure.Segments.Insert(insertionIndex++, MakeLineSegment(right, bottom - cornerRadius));
+ figure.Segments.Insert(insertionIndex, MakeLineSegment(lastLeft, lastTop + cornerRadius));
+ if (!lastLeft.IsClose(left)) {
+ double cr = left < lastLeft ? cornerRadius : -cornerRadius;
+ SweepDirection dir1 = left < lastLeft ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
+ SweepDirection dir2 = left < lastLeft ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
+ figure.Segments.Insert(insertionIndex, MakeArc(lastLeft, lastBottom - cornerRadius, dir1));
+ figure.Segments.Insert(insertionIndex, MakeLineSegment(lastLeft - cr, lastBottom));
+ figure.Segments.Insert(insertionIndex, MakeArc(left + cr, lastBottom, dir2));
+ }
+ }
+ this.lastTop = top;
+ this.lastBottom = bottom;
+ this.lastLeft = left;
+ this.lastRight = right;
+ }
+
+ ArcSegment MakeArc(double x, double y, SweepDirection dir)
+ {
+ ArcSegment arc = new ArcSegment(
+ new Point(x, y),
+ new Size(cornerRadius, cornerRadius),
+ 0, false, dir, true);
+ arc.Freeze();
+ return arc;
+ }
+
+ static LineSegment MakeLineSegment(double x, double y)
+ {
+ LineSegment ls = new LineSegment(new Point(x, y), true);
+ ls.Freeze();
+ return ls;
+ }
+
+ /// <summary>
+ /// Closes the current figure.
+ /// </summary>
+ public void CloseFigure()
+ {
+ if (figure != null) {
+ figure.Segments.Insert(insertionIndex, MakeLineSegment(lastLeft, lastTop + cornerRadius));
+ if (Math.Abs(lastLeft - lastRight) > cornerRadius) {
+ figure.Segments.Insert(insertionIndex, MakeArc(lastLeft, lastBottom - cornerRadius, SweepDirection.Clockwise));
+ figure.Segments.Insert(insertionIndex, MakeLineSegment(lastLeft + cornerRadius, lastBottom));
+ figure.Segments.Insert(insertionIndex, MakeArc(lastRight - cornerRadius, lastBottom, SweepDirection.Clockwise));
+ }
+
+ figure.IsClosed = true;
+ figures.Add(figure);
+ figure = null;
+ }
+ }
+
+ /// <summary>
+ /// Creates the geometry.
+ /// Returns null when the geometry is empty!
+ /// </summary>
+ public Geometry CreateGeometry()
+ {
+ CloseFigure();
+ if (figures.Count != 0) {
+ PathGeometry g = new PathGeometry(figures);
+ g.Freeze();
+ return g;
+ } else {
+ return null;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/CollapsedLineSection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/CollapsedLineSection.cs
new file mode 100644
index 000000000..bcbf55798
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/CollapsedLineSection.cs
@@ -0,0 +1,95 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Represents a collapsed line section.
+ /// Use the Uncollapse() method to uncollapse the section.
+ /// </summary>
+ public sealed class CollapsedLineSection
+ {
+ DocumentLine start, end;
+ HeightTree heightTree;
+
+ #if DEBUG
+ internal string ID;
+ static int nextId;
+ #else
+ const string ID = "";
+ #endif
+
+ internal CollapsedLineSection(HeightTree heightTree, DocumentLine start, DocumentLine end)
+ {
+ this.heightTree = heightTree;
+ this.start = start;
+ this.end = end;
+ #if DEBUG
+ unchecked {
+ this.ID = " #" + (nextId++);
+ }
+ #endif
+ }
+
+ /// <summary>
+ /// Gets if the document line is collapsed.
+ /// This property initially is true and turns to false when uncollapsing the section.
+ /// </summary>
+ public bool IsCollapsed {
+ get { return start != null; }
+ }
+
+ /// <summary>
+ /// Gets the start line of the section.
+ /// When the section is uncollapsed or the text containing it is deleted,
+ /// this property returns null.
+ /// </summary>
+ public DocumentLine Start {
+ get { return start; }
+ internal set { start = value; }
+ }
+
+ /// <summary>
+ /// Gets the end line of the section.
+ /// When the section is uncollapsed or the text containing it is deleted,
+ /// this property returns null.
+ /// </summary>
+ public DocumentLine End {
+ get { return end; }
+ internal set { end = value; }
+ }
+
+ /// <summary>
+ /// Uncollapses the section.
+ /// This causes the Start and End properties to be set to null!
+ /// Does nothing if the section is already uncollapsed.
+ /// </summary>
+ public void Uncollapse()
+ {
+ if (start == null)
+ return;
+
+ heightTree.Uncollapse(this);
+ #if DEBUG
+ heightTree.CheckProperties();
+ #endif
+
+ start = null;
+ end = null;
+ }
+
+ /// <summary>
+ /// Gets a string representation of the collapsed section.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.Int32.ToString")]
+ public override string ToString()
+ {
+ return "[CollapsedSection" + ID + " Start=" + (start != null ? start.LineNumber.ToString() : "null")
+ + " End=" + (end != null ? end.LineNumber.ToString() : "null") + "]";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColorizingTransformer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColorizingTransformer.cs
new file mode 100644
index 000000000..97e2c0cea
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColorizingTransformer.cs
@@ -0,0 +1,108 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Base class for <see cref="IVisualLineTransformer"/> that helps
+ /// splitting visual elements so that colors (and other text properties) can be easily assigned
+ /// to individual words/characters.
+ /// </summary>
+ public abstract class ColorizingTransformer : IVisualLineTransformer, ITextViewConnect
+ {
+ /// <summary>
+ /// Gets the list of elements currently being transformed.
+ /// </summary>
+ protected IList<VisualLineElement> CurrentElements { get; private set; }
+
+ /// <summary>
+ /// <see cref="IVisualLineTransformer.Transform"/> implementation.
+ /// Sets <see cref="CurrentElements"/> and calls <see cref="Colorize"/>.
+ /// </summary>
+ public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements)
+ {
+ if (elements == null)
+ throw new ArgumentNullException("elements");
+ if (this.CurrentElements != null)
+ throw new InvalidOperationException("Recursive Transform() call");
+ this.CurrentElements = elements;
+ try {
+ Colorize(context);
+ } finally {
+ this.CurrentElements = null;
+ }
+ }
+
+ /// <summary>
+ /// Performs the colorization.
+ /// </summary>
+ protected abstract void Colorize(ITextRunConstructionContext context);
+
+ /// <summary>
+ /// Changes visual element properties.
+ /// This method accesses <see cref="CurrentElements"/>, so it must be called only during
+ /// a <see cref="Transform"/> call.
+ /// This method splits <see cref="VisualLineElement"/>s as necessary to ensure that the region
+ /// can be colored by setting the <see cref="VisualLineElement.TextRunProperties"/> of whole elements,
+ /// and then calls the <paramref name="action"/> on all elements in the region.
+ /// </summary>
+ /// <param name="visualStartColumn">Start visual column of the region to change</param>
+ /// <param name="visualEndColumn">End visual column of the region to change</param>
+ /// <param name="action">Action that changes an individual <see cref="VisualLineElement"/>.</param>
+ protected void ChangeVisualElements(int visualStartColumn, int visualEndColumn, Action<VisualLineElement> action)
+ {
+ if (action == null)
+ throw new ArgumentNullException("action");
+ for (int i = 0; i < CurrentElements.Count; i++) {
+ VisualLineElement e = CurrentElements[i];
+ if (e.VisualColumn > visualEndColumn)
+ break;
+ if (e.VisualColumn < visualStartColumn &&
+ e.VisualColumn + e.VisualLength > visualStartColumn)
+ {
+ if (e.CanSplit) {
+ e.Split(visualStartColumn, CurrentElements, i--);
+ continue;
+ }
+ }
+ if (e.VisualColumn >= visualStartColumn && e.VisualColumn < visualEndColumn) {
+ if (e.VisualColumn + e.VisualLength > visualEndColumn) {
+ if (e.CanSplit) {
+ e.Split(visualEndColumn, CurrentElements, i--);
+ continue;
+ }
+ } else {
+ action(e);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Called when added to a text view.
+ /// </summary>
+ protected virtual void OnAddToTextView(TextView textView)
+ {
+ }
+
+ /// <summary>
+ /// Called when removed from a text view.
+ /// </summary>
+ protected virtual void OnRemoveFromTextView(TextView textView)
+ {
+ }
+
+ void ITextViewConnect.AddToTextView(TextView textView)
+ {
+ OnAddToTextView(textView);
+ }
+
+ void ITextViewConnect.RemoveFromTextView(TextView textView)
+ {
+ OnRemoveFromTextView(textView);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColumnRulerRenderer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColumnRulerRenderer.cs
new file mode 100644
index 000000000..62a29dcb1
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ColumnRulerRenderer.cs
@@ -0,0 +1,64 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Renders a ruler at a certain column.
+ /// </summary>
+ sealed class ColumnRulerRenderer : IBackgroundRenderer
+ {
+ Pen pen;
+ int column;
+ TextView textView;
+
+ public static readonly Color DefaultForeground = Colors.LightGray;
+
+ public ColumnRulerRenderer(TextView textView)
+ {
+ if (textView == null)
+ throw new ArgumentNullException("textView");
+
+ this.pen = new Pen(new SolidColorBrush(DefaultForeground), 1);
+ this.pen.Freeze();
+ this.textView = textView;
+ this.textView.BackgroundRenderers.Add(this);
+ }
+
+ public KnownLayer Layer {
+ get { return KnownLayer.Background; }
+ }
+
+ public void SetRuler(int column, Pen pen)
+ {
+ if (this.column != column) {
+ this.column = column;
+ textView.InvalidateLayer(this.Layer);
+ }
+ if (this.pen != pen) {
+ this.pen = pen;
+ textView.InvalidateLayer(this.Layer);
+ }
+ }
+
+ public void Draw(TextView textView, System.Windows.Media.DrawingContext drawingContext)
+ {
+ if (column < 1) return;
+ double offset = textView.WideSpaceWidth * column;
+ Size pixelSize = PixelSnapHelpers.GetPixelSize(textView);
+ double markerXPos = PixelSnapHelpers.PixelAlign(offset, pixelSize.Width);
+ markerXPos -= textView.ScrollOffset.X;
+ Point start = new Point(markerXPos, 0);
+ Point end = new Point(markerXPos, Math.Max(textView.DocumentHeight, textView.ActualHeight));
+
+ drawingContext.DrawLine(pen, start, end);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DefaultTextRunTypographyProperties.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DefaultTextRunTypographyProperties.cs
new file mode 100644
index 000000000..2ccf07e3e
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DefaultTextRunTypographyProperties.cs
@@ -0,0 +1,171 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Media.TextFormatting;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Default implementation for TextRunTypographyProperties.
+ /// </summary>
+ public class DefaultTextRunTypographyProperties : TextRunTypographyProperties
+ {
+ /// <inheritdoc/>
+ public override FontVariants Variants {
+ get { return FontVariants.Normal; }
+ }
+
+ /// <inheritdoc/>
+ public override bool StylisticSet1 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet2 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet3 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet4 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet5 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet6 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet7 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet8 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet9 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet10 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet11 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet12 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet13 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet14 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet15 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet16 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet17 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet18 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet19 { get { return false; } }
+ /// <inheritdoc/>
+ public override bool StylisticSet20 { get { return false; } }
+
+ /// <inheritdoc/>
+ public override int StylisticAlternates {
+ get { return 0; }
+ }
+
+ /// <inheritdoc/>
+ public override int StandardSwashes {
+ get { return 0; }
+ }
+
+ /// <inheritdoc/>
+ public override bool StandardLigatures {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override bool SlashedZero {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override FontNumeralStyle NumeralStyle {
+ get { return FontNumeralStyle.Normal; }
+ }
+
+ /// <inheritdoc/>
+ public override FontNumeralAlignment NumeralAlignment {
+ get { return FontNumeralAlignment.Normal; }
+ }
+
+ /// <inheritdoc/>
+ public override bool MathematicalGreek {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override bool Kerning {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override bool HistoricalLigatures {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override bool HistoricalForms {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override FontFraction Fraction {
+ get { return FontFraction.Normal; }
+ }
+
+ /// <inheritdoc/>
+ public override FontEastAsianWidths EastAsianWidths {
+ get { return FontEastAsianWidths.Normal; }
+ }
+
+ /// <inheritdoc/>
+ public override FontEastAsianLanguage EastAsianLanguage {
+ get { return FontEastAsianLanguage.Normal; }
+ }
+
+ /// <inheritdoc/>
+ public override bool EastAsianExpertForms {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override bool DiscretionaryLigatures {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override int ContextualSwashes {
+ get { return 0; }
+ }
+
+ /// <inheritdoc/>
+ public override bool ContextualLigatures {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override bool ContextualAlternates {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override bool CaseSensitiveForms {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override bool CapitalSpacing {
+ get { return false; }
+ }
+
+ /// <inheritdoc/>
+ public override FontCapitals Capitals {
+ get { return FontCapitals.Normal; }
+ }
+
+ /// <inheritdoc/>
+ public override int AnnotationAlternates {
+ get { return 0; }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DocumentColorizingTransformer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DocumentColorizingTransformer.cs
new file mode 100644
index 000000000..d4032d64d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/DocumentColorizingTransformer.cs
@@ -0,0 +1,81 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Base class for <see cref="IVisualLineTransformer"/> that helps
+ /// colorizing the document. Derived classes can work with document lines
+ /// and text offsets and this class takes care of the visual lines and visual columns.
+ /// </summary>
+ public abstract class DocumentColorizingTransformer : ColorizingTransformer
+ {
+ DocumentLine currentDocumentLine;
+ int firstLineStart;
+ int currentDocumentLineStartOffset, currentDocumentLineEndOffset;
+
+ /// <summary>
+ /// Gets the current ITextRunConstructionContext.
+ /// </summary>
+ protected ITextRunConstructionContext CurrentContext { get; private set; }
+
+ /// <inheritdoc/>
+ protected override void Colorize(ITextRunConstructionContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+ this.CurrentContext = context;
+
+ currentDocumentLine = context.VisualLine.FirstDocumentLine;
+ firstLineStart = currentDocumentLineStartOffset = currentDocumentLine.Offset;
+ currentDocumentLineEndOffset = currentDocumentLineStartOffset + currentDocumentLine.Length;
+
+ if (context.VisualLine.FirstDocumentLine == context.VisualLine.LastDocumentLine) {
+ ColorizeLine(currentDocumentLine);
+ } else {
+ ColorizeLine(currentDocumentLine);
+ // ColorizeLine modifies the visual line elements, loop through a copy of the line elements
+ foreach (VisualLineElement e in context.VisualLine.Elements.ToArray()) {
+ int elementOffset = firstLineStart + e.RelativeTextOffset;
+ if (elementOffset >= currentDocumentLineEndOffset) {
+ currentDocumentLine = context.Document.GetLineByOffset(elementOffset);
+ currentDocumentLineStartOffset = currentDocumentLine.Offset;
+ currentDocumentLineEndOffset = currentDocumentLineStartOffset + currentDocumentLine.Length;
+ ColorizeLine(currentDocumentLine);
+ }
+ }
+ }
+ currentDocumentLine = null;
+ this.CurrentContext = null;
+ }
+
+ /// <summary>
+ /// Override this method to colorize an individual document line.
+ /// </summary>
+ protected abstract void ColorizeLine(DocumentLine line);
+
+ /// <summary>
+ /// Changes a part of the current document line.
+ /// </summary>
+ /// <param name="startOffset">Start offset of the region to change</param>
+ /// <param name="endOffset">End offset of the region to change</param>
+ /// <param name="action">Action that changes an individual <see cref="VisualLineElement"/>.</param>
+ protected void ChangeLinePart(int startOffset, int endOffset, Action<VisualLineElement> action)
+ {
+ if (startOffset < currentDocumentLineStartOffset || startOffset > currentDocumentLineEndOffset)
+ throw new ArgumentOutOfRangeException("startOffset", startOffset, "Value must be between " + currentDocumentLineStartOffset + " and " + currentDocumentLineEndOffset);
+ if (endOffset < startOffset || endOffset > currentDocumentLineEndOffset)
+ throw new ArgumentOutOfRangeException("endOffset", endOffset, "Value must be between " + startOffset + " and " + currentDocumentLineEndOffset);
+ VisualLine vl = this.CurrentContext.VisualLine;
+ int visualStart = vl.GetVisualColumn(startOffset - firstLineStart);
+ int visualEnd = vl.GetVisualColumn(endOffset - firstLineStart);
+ if (visualStart < visualEnd) {
+ ChangeVisualElements(visualStart, visualEnd, action);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/FormattedTextElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/FormattedTextElement.cs
new file mode 100644
index 000000000..168dd170d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/FormattedTextElement.cs
@@ -0,0 +1,207 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Formatted text (not normal document text).
+ /// This is used as base class for various VisualLineElements that are displayed using a
+ /// FormattedText, for example newline markers or collapsed folding sections.
+ /// </summary>
+ public class FormattedTextElement : VisualLineElement
+ {
+ internal readonly FormattedText formattedText;
+ internal string text;
+ internal TextLine textLine;
+
+ /// <summary>
+ /// Creates a new FormattedTextElement that displays the specified text
+ /// and occupies the specified length in the document.
+ /// </summary>
+ public FormattedTextElement(string text, int documentLength) : base(1, documentLength)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.text = text;
+ this.BreakBefore = LineBreakCondition.BreakPossible;
+ this.BreakAfter = LineBreakCondition.BreakPossible;
+ }
+
+ /// <summary>
+ /// Creates a new FormattedTextElement that displays the specified text
+ /// and occupies the specified length in the document.
+ /// </summary>
+ public FormattedTextElement(TextLine text, int documentLength) : base(1, documentLength)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.textLine = text;
+ this.BreakBefore = LineBreakCondition.BreakPossible;
+ this.BreakAfter = LineBreakCondition.BreakPossible;
+ }
+
+ /// <summary>
+ /// Creates a new FormattedTextElement that displays the specified text
+ /// and occupies the specified length in the document.
+ /// </summary>
+ public FormattedTextElement(FormattedText text, int documentLength) : base(1, documentLength)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.formattedText = text;
+ this.BreakBefore = LineBreakCondition.BreakPossible;
+ this.BreakAfter = LineBreakCondition.BreakPossible;
+ }
+
+ /// <summary>
+ /// Gets/sets the line break condition before the element.
+ /// The default is 'BreakPossible'.
+ /// </summary>
+ public LineBreakCondition BreakBefore { get; set; }
+
+ /// <summary>
+ /// Gets/sets the line break condition after the element.
+ /// The default is 'BreakPossible'.
+ /// </summary>
+ public LineBreakCondition BreakAfter { get; set; }
+
+ /// <inheritdoc/>
+ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
+ {
+ if (textLine == null) {
+ var formatter = TextFormatterFactory.Create(context.TextView);
+ textLine = PrepareText(formatter, this.text, this.TextRunProperties);
+ this.text = null;
+ }
+ return new FormattedTextRun(this, this.TextRunProperties);
+ }
+
+ /// <summary>
+ /// Constructs a TextLine from a simple text.
+ /// </summary>
+ public static TextLine PrepareText(TextFormatter formatter, string text, TextRunProperties properties)
+ {
+ if (formatter == null)
+ throw new ArgumentNullException("formatter");
+ if (text == null)
+ throw new ArgumentNullException("text");
+ if (properties == null)
+ throw new ArgumentNullException("properties");
+ return formatter.FormatLine(
+ new SimpleTextSource(text, properties),
+ 0,
+ 32000,
+ new VisualLineTextParagraphProperties {
+ defaultTextRunProperties = properties,
+ textWrapping = TextWrapping.NoWrap,
+ tabSize = 40
+ },
+ null);
+ }
+ }
+
+ /// <summary>
+ /// This is the TextRun implementation used by the <see cref="FormattedTextElement"/> class.
+ /// </summary>
+ public class FormattedTextRun : TextEmbeddedObject
+ {
+ readonly FormattedTextElement element;
+ TextRunProperties properties;
+
+ /// <summary>
+ /// Creates a new FormattedTextRun.
+ /// </summary>
+ public FormattedTextRun(FormattedTextElement element, TextRunProperties properties)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+ if (properties == null)
+ throw new ArgumentNullException("properties");
+ this.properties = properties;
+ this.element = element;
+ }
+
+ /// <summary>
+ /// Gets the element for which the FormattedTextRun was created.
+ /// </summary>
+ public FormattedTextElement Element {
+ get { return element; }
+ }
+
+ /// <inheritdoc/>
+ public override LineBreakCondition BreakBefore {
+ get { return element.BreakBefore; }
+ }
+
+ /// <inheritdoc/>
+ public override LineBreakCondition BreakAfter {
+ get { return element.BreakAfter; }
+ }
+
+ /// <inheritdoc/>
+ public override bool HasFixedSize {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override CharacterBufferReference CharacterBufferReference {
+ get { return new CharacterBufferReference(); }
+ }
+
+ /// <inheritdoc/>
+ public override int Length {
+ get { return element.VisualLength; }
+ }
+
+ /// <inheritdoc/>
+ public override TextRunProperties Properties {
+ get { return properties; }
+ }
+
+ /// <inheritdoc/>
+ public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
+ {
+ var formattedText = element.formattedText;
+ if (formattedText != null) {
+ return new TextEmbeddedObjectMetrics(formattedText.WidthIncludingTrailingWhitespace,
+ formattedText.Height,
+ formattedText.Baseline);
+ } else {
+ var text = element.textLine;
+ return new TextEmbeddedObjectMetrics(text.WidthIncludingTrailingWhitespace,
+ text.Height,
+ text.Baseline);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override Rect ComputeBoundingBox(bool rightToLeft, bool sideways)
+ {
+ var formattedText = element.formattedText;
+ if (formattedText != null) {
+ return new Rect(0, 0, formattedText.WidthIncludingTrailingWhitespace, formattedText.Height);
+ } else {
+ var text = element.textLine;
+ return new Rect(0, 0, text.WidthIncludingTrailingWhitespace, text.Height);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override void Draw(DrawingContext drawingContext, Point origin, bool rightToLeft, bool sideways)
+ {
+ if (element.formattedText != null) {
+ origin.Y -= element.formattedText.Baseline;
+ drawingContext.DrawText(element.formattedText, origin);
+ } else {
+ origin.Y -= element.textLine.Baseline;
+ element.textLine.Draw(drawingContext, origin, InvertAxes.None);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/GlobalTextRunProperties.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/GlobalTextRunProperties.cs
new file mode 100644
index 000000000..f0f40c882
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/GlobalTextRunProperties.cs
@@ -0,0 +1,30 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ sealed class GlobalTextRunProperties : TextRunProperties
+ {
+ internal Typeface typeface;
+ internal double fontRenderingEmSize;
+ internal Brush foregroundBrush;
+ private Brush backgroundBrush1;
+ internal System.Globalization.CultureInfo cultureInfo;
+
+ public override Typeface Typeface { get { return typeface; } }
+ public override double FontRenderingEmSize { get { return fontRenderingEmSize; } }
+ public override double FontHintingEmSize { get { return fontRenderingEmSize; } }
+ public override TextDecorationCollection TextDecorations { get { return null; } }
+ public override Brush ForegroundBrush { get { return foregroundBrush; } }
+ public override Brush BackgroundBrush { get { return BackgroundBrush1; } }
+ public override System.Globalization.CultureInfo CultureInfo { get { return cultureInfo; } }
+ public override TextEffectCollection TextEffects { get { return null; } }
+
+ internal Brush BackgroundBrush1 { get => backgroundBrush1; set => backgroundBrush1 = value; }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTree.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTree.cs
new file mode 100644
index 000000000..6751af8fa
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTree.cs
@@ -0,0 +1,1092 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Red-black tree similar to DocumentLineTree, augmented with collapsing and height data.
+ /// </summary>
+ sealed class HeightTree : ILineTracker, IDisposable
+ {
+ // TODO: Optimize this. This tree takes alot of memory.
+ // (56 bytes for HeightTreeNode
+ // We should try to get rid of the dictionary and find height nodes per index. (DONE!)
+ // And we might do much better by compressing lines with the same height into a single node.
+ // That would also improve load times because we would always start with just a single node.
+
+ /* Idea:
+ class NewHeightTreeNode {
+ int totalCount; // =count+left.count+right.count
+ int count; // one node can represent multiple lines
+ double height; // height of each line in this node
+ double totalHeight; // =(collapsedSections!=null?0:height*count) + left.totalHeight + right.totalHeight
+ List<CollapsedSection> collapsedSections; // sections holding this line collapsed
+ // no "nodeCollapsedSections"/"totalCollapsedSections":
+ NewHeightTreeNode left, right, parent;
+ bool color;
+ }
+ totalCollapsedSections: are hard to update and not worth the effort. O(n log n) isn't too bad for
+ collapsing/uncollapsing, especially when compression reduces the n.
+ */
+
+ #region Constructor
+ readonly TextDocument document;
+ HeightTreeNode root;
+ WeakLineTracker weakLineTracker;
+
+ public HeightTree(TextDocument document, double defaultLineHeight)
+ {
+ this.document = document;
+ weakLineTracker = WeakLineTracker.Register(document, this);
+ this.DefaultLineHeight = defaultLineHeight;
+ RebuildDocument();
+ }
+
+ public void Dispose()
+ {
+ if (weakLineTracker != null)
+ weakLineTracker.Deregister();
+ this.root = null;
+ this.weakLineTracker = null;
+ }
+
+ double defaultLineHeight;
+
+ public double DefaultLineHeight {
+ get { return defaultLineHeight; }
+ set {
+ double oldValue = defaultLineHeight;
+ if (oldValue == value)
+ return;
+ defaultLineHeight = value;
+ // update the stored value in all nodes:
+ foreach (var node in AllNodes) {
+ if (node.lineNode.height == oldValue) {
+ node.lineNode.height = value;
+ UpdateAugmentedData(node, UpdateAfterChildrenChangeRecursionMode.IfRequired);
+ }
+ }
+ }
+ }
+
+ HeightTreeNode GetNode(DocumentLine ls)
+ {
+ return GetNodeByIndex(ls.LineNumber - 1);
+ }
+ #endregion
+
+ #region RebuildDocument
+ void ILineTracker.SetLineLength(DocumentLine ls, int newTotalLength)
+ {
+ }
+
+ /// <summary>
+ /// Rebuild the tree, in O(n).
+ /// </summary>
+ public void RebuildDocument()
+ {
+ foreach (CollapsedLineSection s in GetAllCollapsedSections()) {
+ s.Start = null;
+ s.End = null;
+ }
+
+ HeightTreeNode[] nodes = new HeightTreeNode[document.LineCount];
+ int lineNumber = 0;
+ foreach (DocumentLine ls in document.Lines) {
+ nodes[lineNumber++] = new HeightTreeNode(ls, defaultLineHeight);
+ }
+ Debug.Assert(nodes.Length > 0);
+ // now build the corresponding balanced tree
+ int height = DocumentLineTree.GetTreeHeight(nodes.Length);
+ Debug.WriteLine("HeightTree will have height: " + height);
+ root = BuildTree(nodes, 0, nodes.Length, height);
+ root.color = BLACK;
+ #if DEBUG
+ CheckProperties();
+ #endif
+ }
+
+ /// <summary>
+ /// build a tree from a list of nodes
+ /// </summary>
+ HeightTreeNode BuildTree(HeightTreeNode[] nodes, int start, int end, int subtreeHeight)
+ {
+ Debug.Assert(start <= end);
+ if (start == end) {
+ return null;
+ }
+ int middle = (start + end) / 2;
+ HeightTreeNode node = nodes[middle];
+ node.left = BuildTree(nodes, start, middle, subtreeHeight - 1);
+ node.right = BuildTree(nodes, middle + 1, end, subtreeHeight - 1);
+ if (node.left != null) node.left.parent = node;
+ if (node.right != null) node.right.parent = node;
+ if (subtreeHeight == 1)
+ node.color = RED;
+ UpdateAugmentedData(node, UpdateAfterChildrenChangeRecursionMode.None);
+ return node;
+ }
+ #endregion
+
+ #region Insert/Remove lines
+ void ILineTracker.BeforeRemoveLine(DocumentLine line)
+ {
+ HeightTreeNode node = GetNode(line);
+ if (node.lineNode.collapsedSections != null) {
+ foreach (CollapsedLineSection cs in node.lineNode.collapsedSections.ToArray()) {
+ if (cs.Start == line && cs.End == line) {
+ cs.Start = null;
+ cs.End = null;
+ } else if (cs.Start == line) {
+ Uncollapse(cs);
+ cs.Start = line.NextLine;
+ AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1);
+ } else if (cs.End == line) {
+ Uncollapse(cs);
+ cs.End = line.PreviousLine;
+ AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1);
+ }
+ }
+ }
+ BeginRemoval();
+ RemoveNode(node);
+ // clear collapsedSections from removed line: prevent damage if removed line is in "nodesToCheckForMerging"
+ node.lineNode.collapsedSections = null;
+ EndRemoval();
+ }
+
+// void ILineTracker.AfterRemoveLine(DocumentLine line)
+// {
+//
+// }
+
+ void ILineTracker.LineInserted(DocumentLine insertionPos, DocumentLine newLine)
+ {
+ InsertAfter(GetNode(insertionPos), newLine);
+ #if DEBUG
+ CheckProperties();
+ #endif
+ }
+
+ HeightTreeNode InsertAfter(HeightTreeNode node, DocumentLine newLine)
+ {
+ HeightTreeNode newNode = new HeightTreeNode(newLine, defaultLineHeight);
+ if (node.right == null) {
+ if (node.lineNode.collapsedSections != null) {
+ // we are inserting directly after node - so copy all collapsedSections
+ // that do not end at node.
+ foreach (CollapsedLineSection cs in node.lineNode.collapsedSections) {
+ if (cs.End != node.documentLine)
+ newNode.AddDirectlyCollapsed(cs);
+ }
+ }
+ InsertAsRight(node, newNode);
+ } else {
+ node = node.right.LeftMost;
+ if (node.lineNode.collapsedSections != null) {
+ // we are inserting directly before node - so copy all collapsedSections
+ // that do not start at node.
+ foreach (CollapsedLineSection cs in node.lineNode.collapsedSections) {
+ if (cs.Start != node.documentLine)
+ newNode.AddDirectlyCollapsed(cs);
+ }
+ }
+ InsertAsLeft(node, newNode);
+ }
+ return newNode;
+ }
+ #endregion
+
+ #region Rotation callbacks
+ enum UpdateAfterChildrenChangeRecursionMode
+ {
+ None,
+ IfRequired,
+ WholeBranch
+ }
+
+ static void UpdateAfterChildrenChange(HeightTreeNode node)
+ {
+ UpdateAugmentedData(node, UpdateAfterChildrenChangeRecursionMode.IfRequired);
+ }
+
+ static void UpdateAugmentedData(HeightTreeNode node, UpdateAfterChildrenChangeRecursionMode mode)
+ {
+ int totalCount = 1;
+ double totalHeight = node.lineNode.TotalHeight;
+ if (node.left != null) {
+ totalCount += node.left.totalCount;
+ totalHeight += node.left.totalHeight;
+ }
+ if (node.right != null) {
+ totalCount += node.right.totalCount;
+ totalHeight += node.right.totalHeight;
+ }
+ if (node.IsDirectlyCollapsed)
+ totalHeight = 0;
+ if (totalCount != node.totalCount
+ || !totalHeight.IsClose(node.totalHeight)
+ || mode == UpdateAfterChildrenChangeRecursionMode.WholeBranch)
+ {
+ node.totalCount = totalCount;
+ node.totalHeight = totalHeight;
+ if (node.parent != null && mode != UpdateAfterChildrenChangeRecursionMode.None)
+ UpdateAugmentedData(node.parent, mode);
+ }
+ }
+
+ void UpdateAfterRotateLeft(HeightTreeNode node)
+ {
+ // node = old parent
+ // node.parent = pivot, new parent
+ var collapsedP = node.parent.collapsedSections;
+ var collapsedQ = node.collapsedSections;
+ // move collapsedSections from old parent to new parent
+ node.parent.collapsedSections = collapsedQ;
+ node.collapsedSections = null;
+ // split the collapsedSections from the new parent into its old children:
+ if (collapsedP != null) {
+ foreach (CollapsedLineSection cs in collapsedP) {
+ if (node.parent.right != null)
+ node.parent.right.AddDirectlyCollapsed(cs);
+ node.parent.lineNode.AddDirectlyCollapsed(cs);
+ if (node.right != null)
+ node.right.AddDirectlyCollapsed(cs);
+ }
+ }
+ MergeCollapsedSectionsIfPossible(node);
+
+ UpdateAfterChildrenChange(node);
+
+ // not required: rotations only happen on insertions/deletions
+ // -> totalCount changes -> the parent is always updated
+ //UpdateAfterChildrenChange(node.parent);
+ }
+
+ void UpdateAfterRotateRight(HeightTreeNode node)
+ {
+ // node = old parent
+ // node.parent = pivot, new parent
+ var collapsedP = node.parent.collapsedSections;
+ var collapsedQ = node.collapsedSections;
+ // move collapsedSections from old parent to new parent
+ node.parent.collapsedSections = collapsedQ;
+ node.collapsedSections = null;
+ // split the collapsedSections from the new parent into its old children:
+ if (collapsedP != null) {
+ foreach (CollapsedLineSection cs in collapsedP) {
+ if (node.parent.left != null)
+ node.parent.left.AddDirectlyCollapsed(cs);
+ node.parent.lineNode.AddDirectlyCollapsed(cs);
+ if (node.left != null)
+ node.left.AddDirectlyCollapsed(cs);
+ }
+ }
+ MergeCollapsedSectionsIfPossible(node);
+
+ UpdateAfterChildrenChange(node);
+
+ // not required: rotations only happen on insertions/deletions
+ // -> totalCount changes -> the parent is always updated
+ //UpdateAfterChildrenChange(node.parent);
+ }
+
+ // node removal:
+ // a node in the middle of the tree is removed as following:
+ // its successor is removed
+ // it is replaced with its successor
+
+ void BeforeNodeRemove(HeightTreeNode removedNode)
+ {
+ Debug.Assert(removedNode.left == null || removedNode.right == null);
+
+ var collapsed = removedNode.collapsedSections;
+ if (collapsed != null) {
+ HeightTreeNode childNode = removedNode.left ?? removedNode.right;
+ if (childNode != null) {
+ foreach (CollapsedLineSection cs in collapsed)
+ childNode.AddDirectlyCollapsed(cs);
+ }
+ }
+ if (removedNode.parent != null)
+ MergeCollapsedSectionsIfPossible(removedNode.parent);
+ }
+
+ void BeforeNodeReplace(HeightTreeNode removedNode, HeightTreeNode newNode, HeightTreeNode newNodeOldParent)
+ {
+ Debug.Assert(removedNode != null);
+ Debug.Assert(newNode != null);
+ while (newNodeOldParent != removedNode) {
+ if (newNodeOldParent.collapsedSections != null) {
+ foreach (CollapsedLineSection cs in newNodeOldParent.collapsedSections) {
+ newNode.lineNode.AddDirectlyCollapsed(cs);
+ }
+ }
+ newNodeOldParent = newNodeOldParent.parent;
+ }
+ if (newNode.collapsedSections != null) {
+ foreach (CollapsedLineSection cs in newNode.collapsedSections) {
+ newNode.lineNode.AddDirectlyCollapsed(cs);
+ }
+ }
+ newNode.collapsedSections = removedNode.collapsedSections;
+ MergeCollapsedSectionsIfPossible(newNode);
+ }
+
+ bool inRemoval;
+ List<HeightTreeNode> nodesToCheckForMerging;
+
+ void BeginRemoval()
+ {
+ Debug.Assert(!inRemoval);
+ if (nodesToCheckForMerging == null) {
+ nodesToCheckForMerging = new List<HeightTreeNode>();
+ }
+ inRemoval = true;
+ }
+
+ void EndRemoval()
+ {
+ Debug.Assert(inRemoval);
+ inRemoval = false;
+ foreach (HeightTreeNode node in nodesToCheckForMerging) {
+ MergeCollapsedSectionsIfPossible(node);
+ }
+ nodesToCheckForMerging.Clear();
+ }
+
+ void MergeCollapsedSectionsIfPossible(HeightTreeNode node)
+ {
+ Debug.Assert(node != null);
+ if (inRemoval) {
+ nodesToCheckForMerging.Add(node);
+ return;
+ }
+ // now check if we need to merge collapsedSections together
+ bool merged = false;
+ var collapsedL = node.lineNode.collapsedSections;
+ if (collapsedL != null) {
+ for (int i = collapsedL.Count - 1; i >= 0; i--) {
+ CollapsedLineSection cs = collapsedL[i];
+ if (cs.Start == node.documentLine || cs.End == node.documentLine)
+ continue;
+ if (node.left == null
+ || (node.left.collapsedSections != null && node.left.collapsedSections.Contains(cs)))
+ {
+ if (node.right == null
+ || (node.right.collapsedSections != null && node.right.collapsedSections.Contains(cs)))
+ {
+ // all children of node contain cs: -> merge!
+ if (node.left != null) node.left.RemoveDirectlyCollapsed(cs);
+ if (node.right != null) node.right.RemoveDirectlyCollapsed(cs);
+ collapsedL.RemoveAt(i);
+ node.AddDirectlyCollapsed(cs);
+ merged = true;
+ }
+ }
+ }
+ if (collapsedL.Count == 0)
+ node.lineNode.collapsedSections = null;
+ }
+ if (merged && node.parent != null) {
+ MergeCollapsedSectionsIfPossible(node.parent);
+ }
+ }
+ #endregion
+
+ #region GetNodeBy... / Get...FromNode
+ HeightTreeNode GetNodeByIndex(int index)
+ {
+ Debug.Assert(index >= 0);
+ Debug.Assert(index < root.totalCount);
+ HeightTreeNode node = root;
+ while (true) {
+ if (node.left != null && index < node.left.totalCount) {
+ node = node.left;
+ } else {
+ if (node.left != null) {
+ index -= node.left.totalCount;
+ }
+ if (index == 0)
+ return node;
+ index--;
+ node = node.right;
+ }
+ }
+ }
+
+ HeightTreeNode GetNodeByVisualPosition(double position)
+ {
+ HeightTreeNode node = root;
+ while (true) {
+ double positionAfterLeft = position;
+ if (node.left != null) {
+ positionAfterLeft -= node.left.totalHeight;
+ if (positionAfterLeft < 0) {
+ // Descend into left
+ node = node.left;
+ continue;
+ }
+ }
+ double positionBeforeRight = positionAfterLeft - node.lineNode.TotalHeight;
+ if (positionBeforeRight < 0) {
+ // Found the correct node
+ return node;
+ }
+ if (node.right == null || node.right.totalHeight == 0) {
+ // Can happen when position>node.totalHeight,
+ // i.e. at the end of the document, or due to rounding errors in previous loop iterations.
+
+ // If node.lineNode isn't collapsed, return that.
+ // Also return node.lineNode if there is no previous node that we could return instead.
+ if (node.lineNode.TotalHeight > 0 || node.left == null)
+ return node;
+ // Otherwise, descend into left (find the last non-collapsed node)
+ node = node.left;
+ } else {
+ // Descend into right
+ position = positionBeforeRight;
+ node = node.right;
+ }
+ }
+ }
+
+ static double GetVisualPositionFromNode(HeightTreeNode node)
+ {
+ double position = (node.left != null) ? node.left.totalHeight : 0;
+ while (node.parent != null) {
+ if (node.IsDirectlyCollapsed)
+ position = 0;
+ if (node == node.parent.right) {
+ if (node.parent.left != null)
+ position += node.parent.left.totalHeight;
+ position += node.parent.lineNode.TotalHeight;
+ }
+ node = node.parent;
+ }
+ return position;
+ }
+ #endregion
+
+ #region Public methods
+ public DocumentLine GetLineByNumber(int number)
+ {
+ return GetNodeByIndex(number - 1).documentLine;
+ }
+
+ public DocumentLine GetLineByVisualPosition(double position)
+ {
+ return GetNodeByVisualPosition(position).documentLine;
+ }
+
+ public double GetVisualPosition(DocumentLine line)
+ {
+ return GetVisualPositionFromNode(GetNode(line));
+ }
+
+ public double GetHeight(DocumentLine line)
+ {
+ return GetNode(line).lineNode.height;
+ }
+
+ public void SetHeight(DocumentLine line, double val)
+ {
+ var node = GetNode(line);
+ node.lineNode.height = val;
+ UpdateAfterChildrenChange(node);
+ }
+
+ public bool GetIsCollapsed(int lineNumber)
+ {
+ var node = GetNodeByIndex(lineNumber - 1);
+ return node.lineNode.IsDirectlyCollapsed || GetIsCollapedFromNode(node);
+ }
+
+ /// <summary>
+ /// Collapses the specified text section.
+ /// Runtime: O(log n)
+ /// </summary>
+ public CollapsedLineSection CollapseText(DocumentLine start, DocumentLine end)
+ {
+ if (!document.Lines.Contains(start))
+ throw new ArgumentException("Line is not part of this document", "start");
+ if (!document.Lines.Contains(end))
+ throw new ArgumentException("Line is not part of this document", "end");
+ int length = end.LineNumber - start.LineNumber + 1;
+ if (length < 0)
+ throw new ArgumentException("start must be a line before end");
+ CollapsedLineSection section = new CollapsedLineSection(this, start, end);
+ AddCollapsedSection(section, length);
+ #if DEBUG
+ CheckProperties();
+ #endif
+ return section;
+ }
+ #endregion
+
+ #region LineCount & TotalHeight
+ public int LineCount {
+ get {
+ return root.totalCount;
+ }
+ }
+
+ public double TotalHeight {
+ get {
+ return root.totalHeight;
+ }
+ }
+ #endregion
+
+ #region GetAllCollapsedSections
+ IEnumerable<HeightTreeNode> AllNodes {
+ get {
+ if (root != null) {
+ HeightTreeNode node = root.LeftMost;
+ while (node != null) {
+ yield return node;
+ node = node.Successor;
+ }
+ }
+ }
+ }
+
+ internal IEnumerable<CollapsedLineSection> GetAllCollapsedSections()
+ {
+ List<CollapsedLineSection> emptyCSList = new List<CollapsedLineSection>();
+ return System.Linq.Enumerable.Distinct(
+ System.Linq.Enumerable.SelectMany(
+ AllNodes, node => System.Linq.Enumerable.Concat(node.lineNode.collapsedSections ?? emptyCSList,
+ node.collapsedSections ?? emptyCSList)
+ ));
+ }
+ #endregion
+
+ #region CheckProperties
+ #if DEBUG
+ [Conditional("DATACONSISTENCYTEST")]
+ internal void CheckProperties()
+ {
+ CheckProperties(root);
+
+ foreach (CollapsedLineSection cs in GetAllCollapsedSections()) {
+ Debug.Assert(GetNode(cs.Start).lineNode.collapsedSections.Contains(cs));
+ Debug.Assert(GetNode(cs.End).lineNode.collapsedSections.Contains(cs));
+ int endLine = cs.End.LineNumber;
+ for (int i = cs.Start.LineNumber; i <= endLine; i++) {
+ CheckIsInSection(cs, GetLineByNumber(i));
+ }
+ }
+
+ // check red-black property:
+ int blackCount = -1;
+ CheckNodeProperties(root, null, RED, 0, ref blackCount);
+ }
+
+ void CheckIsInSection(CollapsedLineSection cs, DocumentLine line)
+ {
+ HeightTreeNode node = GetNode(line);
+ if (node.lineNode.collapsedSections != null && node.lineNode.collapsedSections.Contains(cs))
+ return;
+ while (node != null) {
+ if (node.collapsedSections != null && node.collapsedSections.Contains(cs))
+ return;
+ node = node.parent;
+ }
+ throw new InvalidOperationException(cs + " not found for line " + line);
+ }
+
+ void CheckProperties(HeightTreeNode node)
+ {
+ int totalCount = 1;
+ double totalHeight = node.lineNode.TotalHeight;
+ if (node.lineNode.IsDirectlyCollapsed)
+ Debug.Assert(node.lineNode.collapsedSections.Count > 0);
+ if (node.left != null) {
+ CheckProperties(node.left);
+ totalCount += node.left.totalCount;
+ totalHeight += node.left.totalHeight;
+
+ CheckAllContainedIn(node.left.collapsedSections, node.lineNode.collapsedSections);
+ }
+ if (node.right != null) {
+ CheckProperties(node.right);
+ totalCount += node.right.totalCount;
+ totalHeight += node.right.totalHeight;
+
+ CheckAllContainedIn(node.right.collapsedSections, node.lineNode.collapsedSections);
+ }
+ if (node.left != null && node.right != null) {
+ if (node.left.collapsedSections != null && node.right.collapsedSections != null) {
+ var intersection = System.Linq.Enumerable.Intersect(node.left.collapsedSections, node.right.collapsedSections);
+ Debug.Assert(System.Linq.Enumerable.Count(intersection) == 0);
+ }
+ }
+ if (node.IsDirectlyCollapsed) {
+ Debug.Assert(node.collapsedSections.Count > 0);
+ totalHeight = 0;
+ }
+ Debug.Assert(node.totalCount == totalCount);
+ Debug.Assert(node.totalHeight.IsClose(totalHeight));
+ }
+
+ /// <summary>
+ /// Checks that all elements in list1 are contained in list2.
+ /// </summary>
+ static void CheckAllContainedIn(IEnumerable<CollapsedLineSection> list1, ICollection<CollapsedLineSection> list2)
+ {
+ if (list1 == null) list1 = new List<CollapsedLineSection>();
+ if (list2 == null) list2 = new List<CollapsedLineSection>();
+ foreach (CollapsedLineSection cs in list1) {
+ Debug.Assert(list2.Contains(cs));
+ }
+ }
+
+ /*
+ 1. A node is either red or black.
+ 2. The root is black.
+ 3. All leaves are black. (The leaves are the NIL children.)
+ 4. Both children of every red node are black. (So every red node must have a black parent.)
+ 5. Every simple path from a node to a descendant leaf contains the same number of black nodes. (Not counting the leaf node.)
+ */
+ void CheckNodeProperties(HeightTreeNode node, HeightTreeNode parentNode, bool parentColor, int blackCount, ref int expectedBlackCount)
+ {
+ if (node == null) return;
+
+ Debug.Assert(node.parent == parentNode);
+
+ if (parentColor == RED) {
+ Debug.Assert(node.color == BLACK);
+ }
+ if (node.color == BLACK) {
+ blackCount++;
+ }
+ if (node.left == null && node.right == null) {
+ // node is a leaf node:
+ if (expectedBlackCount == -1)
+ expectedBlackCount = blackCount;
+ else
+ Debug.Assert(expectedBlackCount == blackCount);
+ }
+ CheckNodeProperties(node.left, node, node.color, blackCount, ref expectedBlackCount);
+ CheckNodeProperties(node.right, node, node.color, blackCount, ref expectedBlackCount);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ public string GetTreeAsString()
+ {
+ StringBuilder b = new StringBuilder();
+ AppendTreeToString(root, b, 0);
+ return b.ToString();
+ }
+
+ static void AppendTreeToString(HeightTreeNode node, StringBuilder b, int indent)
+ {
+ if (node.color == RED)
+ b.Append("RED ");
+ else
+ b.Append("BLACK ");
+ b.AppendLine(node.ToString());
+ indent += 2;
+ if (node.left != null) {
+ b.Append(' ', indent);
+ b.Append("L: ");
+ AppendTreeToString(node.left, b, indent);
+ }
+ if (node.right != null) {
+ b.Append(' ', indent);
+ b.Append("R: ");
+ AppendTreeToString(node.right, b, indent);
+ }
+ }
+ #endif
+ #endregion
+
+ #region Red/Black Tree
+ const bool RED = true;
+ const bool BLACK = false;
+
+ void InsertAsLeft(HeightTreeNode parentNode, HeightTreeNode newNode)
+ {
+ Debug.Assert(parentNode.left == null);
+ parentNode.left = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAfterChildrenChange(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void InsertAsRight(HeightTreeNode parentNode, HeightTreeNode newNode)
+ {
+ Debug.Assert(parentNode.right == null);
+ parentNode.right = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAfterChildrenChange(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void FixTreeOnInsert(HeightTreeNode node)
+ {
+ Debug.Assert(node != null);
+ Debug.Assert(node.color == RED);
+ Debug.Assert(node.left == null || node.left.color == BLACK);
+ Debug.Assert(node.right == null || node.right.color == BLACK);
+
+ HeightTreeNode parentNode = node.parent;
+ if (parentNode == null) {
+ // we inserted in the root -> the node must be black
+ // since this is a root node, making the node black increments the number of black nodes
+ // on all paths by one, so it is still the same for all paths.
+ node.color = BLACK;
+ return;
+ }
+ if (parentNode.color == BLACK) {
+ // if the parent node where we inserted was black, our red node is placed correctly.
+ // since we inserted a red node, the number of black nodes on each path is unchanged
+ // -> the tree is still balanced
+ return;
+ }
+ // parentNode is red, so there is a conflict here!
+
+ // because the root is black, parentNode is not the root -> there is a grandparent node
+ HeightTreeNode grandparentNode = parentNode.parent;
+ HeightTreeNode uncleNode = Sibling(parentNode);
+ if (uncleNode != null && uncleNode.color == RED) {
+ parentNode.color = BLACK;
+ uncleNode.color = BLACK;
+ grandparentNode.color = RED;
+ FixTreeOnInsert(grandparentNode);
+ return;
+ }
+ // now we know: parent is red but uncle is black
+ // First rotation:
+ if (node == parentNode.right && parentNode == grandparentNode.left) {
+ RotateLeft(parentNode);
+ node = node.left;
+ } else if (node == parentNode.left && parentNode == grandparentNode.right) {
+ RotateRight(parentNode);
+ node = node.right;
+ }
+ // because node might have changed, reassign variables:
+ parentNode = node.parent;
+ grandparentNode = parentNode.parent;
+
+ // Now recolor a bit:
+ parentNode.color = BLACK;
+ grandparentNode.color = RED;
+ // Second rotation:
+ if (node == parentNode.left && parentNode == grandparentNode.left) {
+ RotateRight(grandparentNode);
+ } else {
+ // because of the first rotation, this is guaranteed:
+ Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right);
+ RotateLeft(grandparentNode);
+ }
+ }
+
+ void RemoveNode(HeightTreeNode removedNode)
+ {
+ if (removedNode.left != null && removedNode.right != null) {
+ // replace removedNode with it's in-order successor
+
+ HeightTreeNode leftMost = removedNode.right.LeftMost;
+ HeightTreeNode parentOfLeftMost = leftMost.parent;
+ RemoveNode(leftMost); // remove leftMost from its current location
+
+ BeforeNodeReplace(removedNode, leftMost, parentOfLeftMost);
+ // and overwrite the removedNode with it
+ ReplaceNode(removedNode, leftMost);
+ leftMost.left = removedNode.left;
+ if (leftMost.left != null) leftMost.left.parent = leftMost;
+ leftMost.right = removedNode.right;
+ if (leftMost.right != null) leftMost.right.parent = leftMost;
+ leftMost.color = removedNode.color;
+
+ UpdateAfterChildrenChange(leftMost);
+ if (leftMost.parent != null) UpdateAfterChildrenChange(leftMost.parent);
+ return;
+ }
+
+ // now either removedNode.left or removedNode.right is null
+ // get the remaining child
+ HeightTreeNode parentNode = removedNode.parent;
+ HeightTreeNode childNode = removedNode.left ?? removedNode.right;
+ BeforeNodeRemove(removedNode);
+ ReplaceNode(removedNode, childNode);
+ if (parentNode != null) UpdateAfterChildrenChange(parentNode);
+ if (removedNode.color == BLACK) {
+ if (childNode != null && childNode.color == RED) {
+ childNode.color = BLACK;
+ } else {
+ FixTreeOnDelete(childNode, parentNode);
+ }
+ }
+ }
+
+ void FixTreeOnDelete(HeightTreeNode node, HeightTreeNode parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (parentNode == null)
+ return;
+
+ // warning: node may be null
+ HeightTreeNode sibling = Sibling(node, parentNode);
+ if (sibling.color == RED) {
+ parentNode.color = RED;
+ sibling.color = BLACK;
+ if (node == parentNode.left) {
+ RotateLeft(parentNode);
+ } else {
+ RotateRight(parentNode);
+ }
+
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+ }
+
+ if (parentNode.color == BLACK
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ FixTreeOnDelete(parentNode, parentNode.parent);
+ return;
+ }
+
+ if (parentNode.color == RED
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ parentNode.color = BLACK;
+ return;
+ }
+
+ if (node == parentNode.left &&
+ sibling.color == BLACK &&
+ GetColor(sibling.left) == RED &&
+ GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.left.color = BLACK;
+ RotateRight(sibling);
+ }
+ else if (node == parentNode.right &&
+ sibling.color == BLACK &&
+ GetColor(sibling.right) == RED &&
+ GetColor(sibling.left) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.right.color = BLACK;
+ RotateLeft(sibling);
+ }
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+
+ sibling.color = parentNode.color;
+ parentNode.color = BLACK;
+ if (node == parentNode.left) {
+ if (sibling.right != null) {
+ Debug.Assert(sibling.right.color == RED);
+ sibling.right.color = BLACK;
+ }
+ RotateLeft(parentNode);
+ } else {
+ if (sibling.left != null) {
+ Debug.Assert(sibling.left.color == RED);
+ sibling.left.color = BLACK;
+ }
+ RotateRight(parentNode);
+ }
+ }
+
+ void ReplaceNode(HeightTreeNode replacedNode, HeightTreeNode newNode)
+ {
+ if (replacedNode.parent == null) {
+ Debug.Assert(replacedNode == root);
+ root = newNode;
+ } else {
+ if (replacedNode.parent.left == replacedNode)
+ replacedNode.parent.left = newNode;
+ else
+ replacedNode.parent.right = newNode;
+ }
+ if (newNode != null) {
+ newNode.parent = replacedNode.parent;
+ }
+ replacedNode.parent = null;
+ }
+
+ void RotateLeft(HeightTreeNode p)
+ {
+ // let q be p's right child
+ HeightTreeNode q = p.right;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's right child to be q's left child
+ p.right = q.left;
+ if (p.right != null) p.right.parent = p;
+ // set q's left child to be p
+ q.left = p;
+ p.parent = q;
+ UpdateAfterRotateLeft(p);
+ }
+
+ void RotateRight(HeightTreeNode p)
+ {
+ // let q be p's left child
+ HeightTreeNode q = p.left;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's left child to be q's right child
+ p.left = q.right;
+ if (p.left != null) p.left.parent = p;
+ // set q's right child to be p
+ q.right = p;
+ p.parent = q;
+ UpdateAfterRotateRight(p);
+ }
+
+ static HeightTreeNode Sibling(HeightTreeNode node)
+ {
+ if (node == node.parent.left)
+ return node.parent.right;
+ else
+ return node.parent.left;
+ }
+
+ static HeightTreeNode Sibling(HeightTreeNode node, HeightTreeNode parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (node == parentNode.left)
+ return parentNode.right;
+ else
+ return parentNode.left;
+ }
+
+ static bool GetColor(HeightTreeNode node)
+ {
+ return node != null ? node.color : BLACK;
+ }
+ #endregion
+
+ #region Collapsing support
+ static bool GetIsCollapedFromNode(HeightTreeNode node)
+ {
+ while (node != null) {
+ if (node.IsDirectlyCollapsed)
+ return true;
+ node = node.parent;
+ }
+ return false;
+ }
+
+ internal void AddCollapsedSection(CollapsedLineSection section, int sectionLength)
+ {
+ AddRemoveCollapsedSection(section, sectionLength, true);
+ }
+
+ void AddRemoveCollapsedSection(CollapsedLineSection section, int sectionLength, bool add)
+ {
+ Debug.Assert(sectionLength > 0);
+
+ HeightTreeNode node = GetNode(section.Start);
+ // Go up in the tree.
+ while (true) {
+ // Mark all middle nodes as collapsed
+ if (add)
+ node.lineNode.AddDirectlyCollapsed(section);
+ else
+ node.lineNode.RemoveDirectlyCollapsed(section);
+ sectionLength -= 1;
+ if (sectionLength == 0) {
+ // we are done!
+ Debug.Assert(node.documentLine == section.End);
+ break;
+ }
+ // Mark all right subtrees as collapsed.
+ if (node.right != null) {
+ if (node.right.totalCount < sectionLength) {
+ if (add)
+ node.right.AddDirectlyCollapsed(section);
+ else
+ node.right.RemoveDirectlyCollapsed(section);
+ sectionLength -= node.right.totalCount;
+ } else {
+ // mark partially into the right subtree: go down the right subtree.
+ AddRemoveCollapsedSectionDown(section, node.right, sectionLength, add);
+ break;
+ }
+ }
+ // go up to the next node
+ HeightTreeNode parentNode = node.parent;
+ Debug.Assert(parentNode != null);
+ while (parentNode.right == node) {
+ node = parentNode;
+ parentNode = node.parent;
+ Debug.Assert(parentNode != null);
+ }
+ node = parentNode;
+ }
+ UpdateAugmentedData(GetNode(section.Start), UpdateAfterChildrenChangeRecursionMode.WholeBranch);
+ UpdateAugmentedData(GetNode(section.End), UpdateAfterChildrenChangeRecursionMode.WholeBranch);
+ }
+
+ static void AddRemoveCollapsedSectionDown(CollapsedLineSection section, HeightTreeNode node, int sectionLength, bool add)
+ {
+ while (true) {
+ if (node.left != null) {
+ if (node.left.totalCount < sectionLength) {
+ // mark left subtree
+ if (add)
+ node.left.AddDirectlyCollapsed(section);
+ else
+ node.left.RemoveDirectlyCollapsed(section);
+ sectionLength -= node.left.totalCount;
+ } else {
+ // mark only inside the left subtree
+ node = node.left;
+ Debug.Assert(node != null);
+ continue;
+ }
+ }
+ if (add)
+ node.lineNode.AddDirectlyCollapsed(section);
+ else
+ node.lineNode.RemoveDirectlyCollapsed(section);
+ sectionLength -= 1;
+ if (sectionLength == 0) {
+ // done!
+ Debug.Assert(node.documentLine == section.End);
+ break;
+ }
+ // mark inside right subtree:
+ node = node.right;
+ Debug.Assert(node != null);
+ }
+ }
+
+ public void Uncollapse(CollapsedLineSection section)
+ {
+ int sectionLength = section.End.LineNumber - section.Start.LineNumber + 1;
+ AddRemoveCollapsedSection(section, sectionLength, false);
+ // do not call CheckProperties() in here - Uncollapse is also called during line removals
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeLineNode.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeLineNode.cs
new file mode 100644
index 000000000..459f2a56a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeLineNode.cs
@@ -0,0 +1,49 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ struct HeightTreeLineNode
+ {
+ internal HeightTreeLineNode(double height)
+ {
+ this.collapsedSections = null;
+ this.height = height;
+ }
+
+ internal double height;
+ internal List<CollapsedLineSection> collapsedSections;
+
+ internal bool IsDirectlyCollapsed {
+ get { return collapsedSections != null; }
+ }
+
+ internal void AddDirectlyCollapsed(CollapsedLineSection section)
+ {
+ if (collapsedSections == null)
+ collapsedSections = new List<CollapsedLineSection>();
+ collapsedSections.Add(section);
+ }
+
+ internal void RemoveDirectlyCollapsed(CollapsedLineSection section)
+ {
+ Debug.Assert(collapsedSections.Contains(section));
+ collapsedSections.Remove(section);
+ if (collapsedSections.Count == 0)
+ collapsedSections = null;
+ }
+
+ /// <summary>
+ /// Returns 0 if the line is directly collapsed, otherwise, returns <see cref="height"/>.
+ /// </summary>
+ internal double TotalHeight {
+ get {
+ return IsDirectlyCollapsed ? 0 : height;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeNode.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeNode.cs
new file mode 100644
index 000000000..42661edeb
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/HeightTreeNode.cs
@@ -0,0 +1,155 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// A node in the text view's height tree.
+ /// </summary>
+ sealed class HeightTreeNode
+ {
+ internal readonly DocumentLine documentLine;
+ internal HeightTreeLineNode lineNode;
+
+ internal HeightTreeNode left, right, parent;
+ internal bool color;
+
+ internal HeightTreeNode()
+ {
+ }
+
+ internal HeightTreeNode(DocumentLine documentLine, double height)
+ {
+ this.documentLine = documentLine;
+ this.totalCount = 1;
+ this.lineNode = new HeightTreeLineNode(height);
+ this.totalHeight = height;
+ }
+
+ internal HeightTreeNode LeftMost {
+ get {
+ HeightTreeNode node = this;
+ while (node.left != null)
+ node = node.left;
+ return node;
+ }
+ }
+
+ internal HeightTreeNode RightMost {
+ get {
+ HeightTreeNode node = this;
+ while (node.right != null)
+ node = node.right;
+ return node;
+ }
+ }
+
+ /// <summary>
+ /// Gets the inorder successor of the node.
+ /// </summary>
+ internal HeightTreeNode Successor {
+ get {
+ if (right != null) {
+ return right.LeftMost;
+ } else {
+ HeightTreeNode node = this;
+ HeightTreeNode oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // go up until we are coming out of a left subtree
+ } while (node != null && node.right == oldNode);
+ return node;
+ }
+ }
+ }
+
+ /// <summary>
+ /// The number of lines in this node and its child nodes.
+ /// Invariant:
+ /// totalCount = 1 + left.totalCount + right.totalCount
+ /// </summary>
+ internal int totalCount;
+
+ /// <summary>
+ /// The total height of this node and its child nodes, excluding directly collapsed nodes.
+ /// Invariant:
+ /// totalHeight = left.IsDirectlyCollapsed ? 0 : left.totalHeight
+ /// + lineNode.IsDirectlyCollapsed ? 0 : lineNode.Height
+ /// + right.IsDirectlyCollapsed ? 0 : right.totalHeight
+ /// </summary>
+ internal double totalHeight;
+
+ /// <summary>
+ /// List of the sections that hold this node collapsed.
+ /// Invariant 1:
+ /// For each document line in the range described by a CollapsedSection, exactly one ancestor
+ /// contains that CollapsedSection.
+ /// Invariant 2:
+ /// A CollapsedSection is contained either in left+middle or middle+right or just middle.
+ /// Invariant 3:
+ /// Start and end of a CollapsedSection always contain the collapsedSection in their
+ /// documentLine (middle node).
+ /// </summary>
+ internal List<CollapsedLineSection> collapsedSections;
+
+ internal bool IsDirectlyCollapsed {
+ get {
+ return collapsedSections != null;
+ }
+ }
+
+ internal void AddDirectlyCollapsed(CollapsedLineSection section)
+ {
+ if (collapsedSections == null) {
+ collapsedSections = new List<CollapsedLineSection>();
+ totalHeight = 0;
+ }
+ Debug.Assert(!collapsedSections.Contains(section));
+ collapsedSections.Add(section);
+ }
+
+
+ internal void RemoveDirectlyCollapsed(CollapsedLineSection section)
+ {
+ Debug.Assert(collapsedSections.Contains(section));
+ collapsedSections.Remove(section);
+ if (collapsedSections.Count == 0) {
+ collapsedSections = null;
+ totalHeight = lineNode.TotalHeight;
+ if (left != null)
+ totalHeight += left.totalHeight;
+ if (right != null)
+ totalHeight += right.totalHeight;
+ }
+ }
+
+ #if DEBUG
+ public override string ToString()
+ {
+ return "[HeightTreeNode "
+ + documentLine.LineNumber + " CS=" + GetCollapsedSections(collapsedSections)
+ + " Line.CS=" + GetCollapsedSections(lineNode.collapsedSections)
+ + " Line.Height=" + lineNode.height
+ + " TotalHeight=" + totalHeight
+ + "]";
+ }
+
+ static string GetCollapsedSections(List<CollapsedLineSection> list)
+ {
+ if (list == null)
+ return "{}";
+ return "{" +
+ string.Join(",",
+ list.ConvertAll(cs=>cs.ID).ToArray())
+ + "}";
+ }
+ #endif
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IBackgroundRenderer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IBackgroundRenderer.cs
new file mode 100644
index 000000000..e16941147
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IBackgroundRenderer.cs
@@ -0,0 +1,26 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Background renderers draw in the background of a known layer.
+ /// You can use background renderers to draw non-interactive elements on the TextView
+ /// without introducing new UIElements.
+ /// </summary>
+ public interface IBackgroundRenderer
+ {
+ /// <summary>
+ /// Gets the layer on which this background renderer should draw.
+ /// </summary>
+ KnownLayer Layer { get; }
+
+ /// <summary>
+ /// Causes the background renderer to draw.
+ /// </summary>
+ void Draw(TextView textView, DrawingContext drawingContext);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextRunConstructionContext.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextRunConstructionContext.cs
new file mode 100644
index 000000000..94c319bbf
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextRunConstructionContext.cs
@@ -0,0 +1,47 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Contains information relevant for text run creation.
+ /// </summary>
+ public interface ITextRunConstructionContext
+ {
+ /// <summary>
+ /// Gets the text document.
+ /// </summary>
+ TextDocument Document { get; }
+
+ /// <summary>
+ /// Gets the text view for which the construction runs.
+ /// </summary>
+ TextView TextView { get; }
+
+ /// <summary>
+ /// Gets the visual line that is currently being constructed.
+ /// </summary>
+ VisualLine VisualLine { get; }
+
+ /// <summary>
+ /// Gets the global text run properties.
+ /// </summary>
+ TextRunProperties GlobalTextRunProperties { get; }
+
+ /// <summary>
+ /// Gets a piece of text from the document.
+ /// </summary>
+ /// <remarks>
+ /// This method is allowed to return a larger string than requested.
+ /// It does this by returning a <see cref="StringSegment"/> that describes the requested segment within the returned string.
+ /// This method should be the preferred text access method in the text transformation pipeline, as it can avoid repeatedly allocating string instances
+ /// for text within the same line.
+ /// </remarks>
+ StringSegment GetText(int offset, int length);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextViewConnect.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextViewConnect.cs
new file mode 100644
index 000000000..55a5c000c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/ITextViewConnect.cs
@@ -0,0 +1,24 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Allows <see cref="VisualLineElementGenerator"/>s, <see cref="IVisualLineTransformer"/>s and
+ /// <see cref="IBackgroundRenderer"/>s to be notified when they are added or removed from a text view.
+ /// </summary>
+ public interface ITextViewConnect
+ {
+ /// <summary>
+ /// Called when added to a text view.
+ /// </summary>
+ void AddToTextView(TextView textView);
+
+ /// <summary>
+ /// Called when removed from a text view.
+ /// </summary>
+ void RemoveFromTextView(TextView textView);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IVisualLineTransformer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IVisualLineTransformer.cs
new file mode 100644
index 000000000..91c2b6863
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/IVisualLineTransformer.cs
@@ -0,0 +1,19 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Allows transforming visual line elements.
+ /// </summary>
+ public interface IVisualLineTransformer
+ {
+ /// <summary>
+ /// Applies the transformation to the specified list of visual line elements.
+ /// </summary>
+ void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/InlineObjectRun.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/InlineObjectRun.cs
new file mode 100644
index 000000000..7448a8007
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/InlineObjectRun.cs
@@ -0,0 +1,145 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// A inline UIElement in the document.
+ /// </summary>
+ public class InlineObjectElement : VisualLineElement
+ {
+ /// <summary>
+ /// Gets the inline element that is displayed.
+ /// </summary>
+ public UIElement Element { get; private set; }
+
+ /// <summary>
+ /// Creates a new InlineObjectElement.
+ /// </summary>
+ /// <param name="documentLength">The length of the element in the document. Must be non-negative.</param>
+ /// <param name="element">The element to display.</param>
+ public InlineObjectElement(int documentLength, UIElement element)
+ : base(1, documentLength)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+ this.Element = element;
+ }
+
+ /// <inheritdoc/>
+ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ return new InlineObjectRun(1, this.TextRunProperties, this.Element);
+ }
+ }
+
+ /// <summary>
+ /// A text run with an embedded UIElement.
+ /// </summary>
+ public class InlineObjectRun : TextEmbeddedObject
+ {
+ UIElement element;
+ int length;
+ TextRunProperties properties;
+ internal Size desiredSize;
+
+ /// <summary>
+ /// Creates a new InlineObjectRun instance.
+ /// </summary>
+ /// <param name="length">The length of the TextRun.</param>
+ /// <param name="properties">The <see cref="TextRunProperties"/> to use.</param>
+ /// <param name="element">The <see cref="UIElement"/> to display.</param>
+ public InlineObjectRun(int length, TextRunProperties properties, UIElement element)
+ {
+ if (length <= 0)
+ throw new ArgumentOutOfRangeException("length", length, "Value must be positive");
+ if (properties == null)
+ throw new ArgumentNullException("properties");
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ this.length = length;
+ this.properties = properties;
+ this.element = element;
+ }
+
+ /// <summary>
+ /// Gets the element displayed by the InlineObjectRun.
+ /// </summary>
+ public UIElement Element {
+ get { return element; }
+ }
+
+ /// <summary>
+ /// Gets the VisualLine that contains this object. This property is only available after the object
+ /// was added to the text view.
+ /// </summary>
+ public VisualLine VisualLine { get; internal set; }
+
+ /// <inheritdoc/>
+ public override LineBreakCondition BreakBefore {
+ get { return LineBreakCondition.BreakDesired; }
+ }
+
+ /// <inheritdoc/>
+ public override LineBreakCondition BreakAfter {
+ get { return LineBreakCondition.BreakDesired; }
+ }
+
+ /// <inheritdoc/>
+ public override bool HasFixedSize {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override CharacterBufferReference CharacterBufferReference {
+ get { return new CharacterBufferReference(); }
+ }
+
+ /// <inheritdoc/>
+ public override int Length {
+ get { return length; }
+ }
+
+ /// <inheritdoc/>
+ public override TextRunProperties Properties {
+ get { return properties; }
+ }
+
+ /// <inheritdoc/>
+ public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
+ {
+ double baseline = TextBlock.GetBaselineOffset(element);
+ if (double.IsNaN(baseline))
+ baseline = desiredSize.Height;
+ return new TextEmbeddedObjectMetrics(desiredSize.Width, desiredSize.Height, baseline);
+ }
+
+ /// <inheritdoc/>
+ public override Rect ComputeBoundingBox(bool rightToLeft, bool sideways)
+ {
+ if (this.element.IsArrangeValid) {
+ double baseline = TextBlock.GetBaselineOffset(element);
+ if (double.IsNaN(baseline))
+ baseline = desiredSize.Height;
+ return new Rect(new Point(0, -baseline), desiredSize);
+ } else {
+ return Rect.Empty;
+ }
+ }
+
+ /// <inheritdoc/>
+ public override void Draw(DrawingContext drawingContext, Point origin, bool rightToLeft, bool sideways)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/Layer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/Layer.cs
new file mode 100644
index 000000000..0d4777813
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/Layer.cs
@@ -0,0 +1,43 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Base class for known layers.
+ /// </summary>
+ class Layer : UIElement
+ {
+ protected readonly TextView textView;
+ protected readonly KnownLayer knownLayer;
+
+ public Layer(TextView textView, KnownLayer knownLayer)
+ {
+ Debug.Assert(textView != null);
+ this.textView = textView;
+ this.knownLayer = knownLayer;
+ this.Focusable = false;
+ }
+
+ protected override GeometryHitTestResult HitTestCore(GeometryHitTestParameters hitTestParameters)
+ {
+ return null;
+ }
+
+ protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
+ {
+ return null;
+ }
+
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ base.OnRender(drawingContext);
+ textView.RenderBackground(drawingContext, knownLayer);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LayerPosition.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LayerPosition.cs
new file mode 100644
index 000000000..f64923eea
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LayerPosition.cs
@@ -0,0 +1,91 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// An enumeration of well-known layers.
+ /// </summary>
+ public enum KnownLayer
+ {
+ /// <summary>
+ /// This layer is in the background.
+ /// There is no UIElement to represent this layer, it is directly drawn in the TextView.
+ /// It is not possible to replace the background layer or insert new layers below it.
+ /// </summary>
+ /// <remarks>This layer is below the Selection layer.</remarks>
+ Background,
+ /// <summary>
+ /// This layer contains the selection rectangle.
+ /// </summary>
+ /// <remarks>This layer is between the Background and the Text layers.</remarks>
+ Selection,
+ /// <summary>
+ /// This layer contains the text and inline UI elements.
+ /// </summary>
+ /// <remarks>This layer is between the Selection and the Caret layers.</remarks>
+ Text,
+ /// <summary>
+ /// This layer contains the blinking caret.
+ /// </summary>
+ /// <remarks>This layer is above the Text layer.</remarks>
+ Caret
+ }
+
+ /// <summary>
+ /// Specifies where a new layer is inserted, in relation to an old layer.
+ /// </summary>
+ public enum LayerInsertionPosition
+ {
+ /// <summary>
+ /// The new layer is inserted below the specified layer.
+ /// </summary>
+ Below,
+ /// <summary>
+ /// The new layer replaces the specified layer. The old layer is removed
+ /// from the <see cref="TextView.Layers"/> collection.
+ /// </summary>
+ Replace,
+ /// <summary>
+ /// The new layer is inserted above the specified layer.
+ /// </summary>
+ Above
+ }
+
+ sealed class LayerPosition : IComparable<LayerPosition>
+ {
+ internal static readonly DependencyProperty LayerPositionProperty =
+ DependencyProperty.RegisterAttached("LayerPosition", typeof(LayerPosition), typeof(LayerPosition));
+
+ public static void SetLayerPosition(UIElement layer, LayerPosition value)
+ {
+ layer.SetValue(LayerPositionProperty, value);
+ }
+
+ public static LayerPosition GetLayerPosition(UIElement layer)
+ {
+ return (LayerPosition)layer.GetValue(LayerPositionProperty);
+ }
+
+ internal readonly KnownLayer KnownLayer;
+ internal readonly LayerInsertionPosition Position;
+
+ public LayerPosition(KnownLayer knownLayer, LayerInsertionPosition position)
+ {
+ this.KnownLayer = knownLayer;
+ this.Position = position;
+ }
+
+ public int CompareTo(LayerPosition other)
+ {
+ int r = this.KnownLayer.CompareTo(other.KnownLayer);
+ if (r != 0)
+ return r;
+ else
+ return this.Position.CompareTo(other.Position);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LinkElementGenerator.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LinkElementGenerator.cs
new file mode 100644
index 000000000..a5cb81c12
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/LinkElementGenerator.cs
@@ -0,0 +1,144 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text.RegularExpressions;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ // This class is public because it can be used as a base class for custom links.
+
+ /// <summary>
+ /// Detects hyperlinks and makes them clickable.
+ /// </summary>
+ /// <remarks>
+ /// This element generator can be easily enabled and configured using the
+ /// <see cref="TextEditorOptions"/>.
+ /// </remarks>
+ public class LinkElementGenerator : VisualLineElementGenerator, IBuiltinElementGenerator
+ {
+ // a link starts with a protocol (or just with www), followed by 0 or more 'link characters', followed by a link end character
+ // (this allows accepting punctuation inside links but not at the end)
+ internal readonly static Regex defaultLinkRegex = new Regex(@"\b(https?://|ftp://|www\.)[\w\d\._/\-~%@()+:?&=#!]*[\w\d/]");
+
+ // try to detect email addresses
+ internal readonly static Regex defaultMailRegex = new Regex(@"\b[\w\d\.\-]+\@[\w\d\.\-]+\.[a-z]{2,6}\b");
+
+ readonly Regex linkRegex;
+
+ /// <summary>
+ /// Gets/Sets whether the user needs to press Control to click the link.
+ /// The default value is true.
+ /// </summary>
+ public bool RequireControlModifierForClick { get; set; }
+
+ /// <summary>
+ /// Creates a new LinkElementGenerator.
+ /// </summary>
+ public LinkElementGenerator()
+ {
+ this.linkRegex = defaultLinkRegex;
+ this.RequireControlModifierForClick = true;
+ }
+
+ /// <summary>
+ /// Creates a new LinkElementGenerator using the specified regex.
+ /// </summary>
+ protected LinkElementGenerator(Regex regex) : this()
+ {
+ if (regex == null)
+ throw new ArgumentNullException("regex");
+ this.linkRegex = regex;
+ }
+
+ void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options)
+ {
+ this.RequireControlModifierForClick = options.RequireControlModifierForHyperlinkClick;
+ }
+
+ Match GetMatch(int startOffset, out int matchOffset)
+ {
+ int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
+ StringSegment relevantText = CurrentContext.GetText(startOffset, endOffset - startOffset);
+ Match m = linkRegex.Match(relevantText.Text, relevantText.Offset, relevantText.Count);
+ matchOffset = m.Success ? m.Index - relevantText.Offset + startOffset : -1;
+ return m;
+ }
+
+ /// <inheritdoc/>
+ public override int GetFirstInterestedOffset(int startOffset)
+ {
+ int matchOffset;
+ GetMatch(startOffset, out matchOffset);
+ return matchOffset;
+ }
+
+ /// <inheritdoc/>
+ public override VisualLineElement ConstructElement(int offset)
+ {
+ int matchOffset;
+ Match m = GetMatch(offset, out matchOffset);
+ if (m.Success && matchOffset == offset) {
+ return ConstructElementFromMatch(m);
+ } else {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Constructs a VisualLineElement that replaces the matched text.
+ /// The default implementation will create a <see cref="VisualLineLinkText"/>
+ /// based on the URI provided by <see cref="GetUriFromMatch"/>.
+ /// </summary>
+ protected virtual VisualLineElement ConstructElementFromMatch(Match m)
+ {
+ Uri uri = GetUriFromMatch(m);
+ if (uri == null)
+ return null;
+ VisualLineLinkText linkText = new VisualLineLinkText(CurrentContext.VisualLine, m.Length);
+ linkText.NavigateUri = uri;
+ linkText.RequireControlModifierForClick = this.RequireControlModifierForClick;
+ return linkText;
+ }
+
+ /// <summary>
+ /// Fetches the URI from the regex match. Returns null if the URI format is invalid.
+ /// </summary>
+ protected virtual Uri GetUriFromMatch(Match match)
+ {
+ string targetUrl = match.Value;
+ if (targetUrl.StartsWith("www.", StringComparison.Ordinal))
+ targetUrl = "http://" + targetUrl;
+ if (Uri.IsWellFormedUriString(targetUrl, UriKind.Absolute))
+ return new Uri(targetUrl);
+
+ return null;
+ }
+ }
+
+ // This class is internal because it does not need to be accessed by the user - it can be configured using TextEditorOptions.
+
+ /// <summary>
+ /// Detects e-mail addresses and makes them clickable.
+ /// </summary>
+ /// <remarks>
+ /// This element generator can be easily enabled and configured using the
+ /// <see cref="TextEditorOptions"/>.
+ /// </remarks>
+ sealed class MailLinkElementGenerator : LinkElementGenerator
+ {
+ /// <summary>
+ /// Creates a new MailLinkElementGenerator.
+ /// </summary>
+ public MailLinkElementGenerator()
+ : base(defaultMailRegex)
+ {
+ }
+
+ protected override Uri GetUriFromMatch(Match match)
+ {
+ return new Uri("mailto:" + match.Value);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/MouseHoverLogic.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/MouseHoverLogic.cs
new file mode 100644
index 000000000..857affcbb
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/MouseHoverLogic.cs
@@ -0,0 +1,134 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Threading;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Encapsulates and adds MouseHover support to UIElements.
+ /// </summary>
+ public class MouseHoverLogic : IDisposable
+ {
+ UIElement target;
+
+ DispatcherTimer mouseHoverTimer;
+ Point mouseHoverStartPoint;
+ MouseEventArgs mouseHoverLastEventArgs;
+ bool mouseHovering;
+
+ /// <summary>
+ /// Creates a new instance and attaches itself to the <paramref name="target" /> UIElement.
+ /// </summary>
+ public MouseHoverLogic(UIElement target)
+ {
+ if (target == null)
+ throw new ArgumentNullException("target");
+ this.target = target;
+ this.target.MouseLeave += MouseHoverLogicMouseLeave;
+ this.target.MouseMove += MouseHoverLogicMouseMove;
+ this.target.MouseEnter += MouseHoverLogicMouseEnter;
+ }
+
+ void MouseHoverLogicMouseMove(object sender, MouseEventArgs e)
+ {
+ Vector mouseMovement = mouseHoverStartPoint - e.GetPosition(this.target);
+ if (Math.Abs(mouseMovement.X) > SystemParameters.MouseHoverWidth
+ || Math.Abs(mouseMovement.Y) > SystemParameters.MouseHoverHeight)
+ {
+ StartHovering(e);
+ }
+ // do not set e.Handled - allow others to also handle MouseMove
+ }
+
+ void MouseHoverLogicMouseEnter(object sender, MouseEventArgs e)
+ {
+ StartHovering(e);
+ // do not set e.Handled - allow others to also handle MouseEnter
+ }
+
+ void StartHovering(MouseEventArgs e)
+ {
+ StopHovering();
+ mouseHoverStartPoint = e.GetPosition(this.target);
+ mouseHoverLastEventArgs = e;
+ mouseHoverTimer = new DispatcherTimer(SystemParameters.MouseHoverTime, DispatcherPriority.Background, OnMouseHoverTimerElapsed, this.target.Dispatcher);
+ mouseHoverTimer.Start();
+ }
+
+ void MouseHoverLogicMouseLeave(object sender, MouseEventArgs e)
+ {
+ StopHovering();
+ // do not set e.Handled - allow others to also handle MouseLeave
+ }
+
+ void StopHovering()
+ {
+ if (mouseHoverTimer != null) {
+ mouseHoverTimer.Stop();
+ mouseHoverTimer = null;
+ }
+ if (mouseHovering) {
+ mouseHovering = false;
+ OnMouseHoverStopped(mouseHoverLastEventArgs);
+ }
+ }
+
+ void OnMouseHoverTimerElapsed(object sender, EventArgs e)
+ {
+ mouseHoverTimer.Stop();
+ mouseHoverTimer = null;
+
+ mouseHovering = true;
+ OnMouseHover(mouseHoverLastEventArgs);
+ }
+
+ /// <summary>
+ /// Occurs when the mouse starts hovering over a certain location.
+ /// </summary>
+ public event EventHandler<MouseEventArgs> MouseHover;
+
+ /// <summary>
+ /// Raises the <see cref="MouseHover"/> event.
+ /// </summary>
+ protected virtual void OnMouseHover(MouseEventArgs e)
+ {
+ if (MouseHover != null) {
+ MouseHover(this, e);
+ }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse stops hovering over a certain location.
+ /// </summary>
+ public event EventHandler<MouseEventArgs> MouseHoverStopped;
+
+ /// <summary>
+ /// Raises the <see cref="MouseHoverStopped"/> event.
+ /// </summary>
+ protected virtual void OnMouseHoverStopped(MouseEventArgs e)
+ {
+ if (MouseHoverStopped != null) {
+ MouseHoverStopped(this, e);
+ }
+ }
+
+ bool disposed;
+
+ /// <summary>
+ /// Removes the MouseHover support from the target UIElement.
+ /// </summary>
+ public void Dispose()
+ {
+ if (!disposed) {
+ this.target.MouseLeave -= MouseHoverLogicMouseLeave;
+ this.target.MouseMove -= MouseHoverLogicMouseMove;
+ this.target.MouseEnter -= MouseHoverLogicMouseEnter;
+ }
+ disposed = true;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SimpleTextSource.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SimpleTextSource.cs
new file mode 100644
index 000000000..8e9ac568b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SimpleTextSource.cs
@@ -0,0 +1,39 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows.Media.TextFormatting;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ sealed class SimpleTextSource : TextSource
+ {
+ readonly string text;
+ readonly TextRunProperties properties;
+
+ public SimpleTextSource(string text, TextRunProperties properties)
+ {
+ this.text = text;
+ this.properties = properties;
+ }
+
+ public override TextRun GetTextRun(int textSourceCharacterIndex)
+ {
+ if (textSourceCharacterIndex < text.Length)
+ return new TextCharacters(text, textSourceCharacterIndex, text.Length - textSourceCharacterIndex, properties);
+ else
+ return new TextEndOfParagraph(1);
+ }
+
+ public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int textSourceCharacterIndex)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SingleCharacterElementGenerator.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SingleCharacterElementGenerator.cs
new file mode 100644
index 000000000..53c4aac81
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/SingleCharacterElementGenerator.cs
@@ -0,0 +1,268 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ // This class is internal because it does not need to be accessed by the user - it can be configured using TextEditorOptions.
+
+ /// <summary>
+ /// Element generator that displays · for spaces and » for tabs and a box for control characters.
+ /// </summary>
+ /// <remarks>
+ /// This element generator is present in every TextView by default; the enabled features can be configured using the
+ /// <see cref="TextEditorOptions"/>.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace")]
+ sealed class SingleCharacterElementGenerator : VisualLineElementGenerator, IBuiltinElementGenerator
+ {
+ /// <summary>
+ /// Gets/Sets whether to show · for spaces.
+ /// </summary>
+ public bool ShowSpaces { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether to show » for tabs.
+ /// </summary>
+ public bool ShowTabs { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether to show a box with the hex code for control characters.
+ /// </summary>
+ public bool ShowBoxForControlCharacters { get; set; }
+
+ /// <summary>
+ /// Creates a new SingleCharacterElementGenerator instance.
+ /// </summary>
+ public SingleCharacterElementGenerator()
+ {
+ this.ShowSpaces = true;
+ this.ShowTabs = true;
+ this.ShowBoxForControlCharacters = true;
+ }
+
+ void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options)
+ {
+ this.ShowSpaces = options.ShowSpaces;
+ this.ShowTabs = options.ShowTabs;
+ this.ShowBoxForControlCharacters = options.ShowBoxForControlCharacters;
+ }
+
+ public override int GetFirstInterestedOffset(int startOffset)
+ {
+ DocumentLine endLine = CurrentContext.VisualLine.LastDocumentLine;
+ StringSegment relevantText = CurrentContext.GetText(startOffset, endLine.EndOffset - startOffset);
+
+ for (int i = 0; i < relevantText.Count; i++) {
+ char c = relevantText.Text[relevantText.Offset + i];
+ switch (c) {
+ case ' ':
+ if (ShowSpaces)
+ return startOffset + i;
+ break;
+ case '\t':
+ if (ShowTabs)
+ return startOffset + i;
+ break;
+ default:
+ if (ShowBoxForControlCharacters && char.IsControl(c)) {
+ return startOffset + i;
+ }
+ break;
+ }
+ }
+ return -1;
+ }
+
+ public override VisualLineElement ConstructElement(int offset)
+ {
+ char c = CurrentContext.Document.GetCharAt(offset);
+ if (ShowSpaces && c == ' ') {
+ return new SpaceTextElement(CurrentContext.TextView.cachedElements.GetTextForNonPrintableCharacter("\u00B7", CurrentContext));
+ } else if (ShowTabs && c == '\t') {
+ return new TabTextElement(CurrentContext.TextView.cachedElements.GetTextForNonPrintableCharacter("\u00BB", CurrentContext));
+ } else if (ShowBoxForControlCharacters && char.IsControl(c)) {
+ var p = new VisualLineElementTextRunProperties(CurrentContext.GlobalTextRunProperties);
+ p.SetForegroundBrush(Brushes.White);
+ var textFormatter = TextFormatterFactory.Create(CurrentContext.TextView);
+ var text = FormattedTextElement.PrepareText(textFormatter,
+ TextUtilities.GetControlCharacterName(c), p);
+ return new SpecialCharacterBoxElement(text);
+ } else {
+ return null;
+ }
+ }
+
+ sealed class SpaceTextElement : FormattedTextElement
+ {
+ public SpaceTextElement(TextLine textLine) : base(textLine, 1)
+ {
+ BreakBefore = LineBreakCondition.BreakPossible;
+ BreakAfter = LineBreakCondition.BreakDesired;
+ }
+
+ public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
+ {
+ if (mode == CaretPositioningMode.Normal)
+ return base.GetNextCaretPosition(visualColumn, direction, mode);
+ else
+ return -1;
+ }
+
+ public override bool IsWhitespace(int visualColumn)
+ {
+ return true;
+ }
+ }
+
+ sealed class TabTextElement : VisualLineElement
+ {
+ internal readonly TextLine text;
+
+ public TabTextElement(TextLine text) : base(2, 1)
+ {
+ this.text = text;
+ }
+
+ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
+ {
+ // the TabTextElement consists of two TextRuns:
+ // first a TabGlyphRun, then TextCharacters '\t' to let WPF handle the tab indentation
+ if (startVisualColumn == this.VisualColumn)
+ return new TabGlyphRun(this, this.TextRunProperties);
+ else if (startVisualColumn == this.VisualColumn + 1)
+ return new TextCharacters("\t", 0, 1, this.TextRunProperties);
+ else
+ throw new ArgumentOutOfRangeException("startVisualColumn");
+ }
+
+ public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
+ {
+ if (mode == CaretPositioningMode.Normal)
+ return base.GetNextCaretPosition(visualColumn, direction, mode);
+ else
+ return -1;
+ }
+
+ public override bool IsWhitespace(int visualColumn)
+ {
+ return true;
+ }
+ }
+
+ sealed class TabGlyphRun : TextEmbeddedObject
+ {
+ readonly TabTextElement element;
+ TextRunProperties properties;
+
+ public TabGlyphRun(TabTextElement element, TextRunProperties properties)
+ {
+ if (properties == null)
+ throw new ArgumentNullException("properties");
+ this.properties = properties;
+ this.element = element;
+ }
+
+ public override LineBreakCondition BreakBefore {
+ get { return LineBreakCondition.BreakPossible; }
+ }
+
+ public override LineBreakCondition BreakAfter {
+ get { return LineBreakCondition.BreakRestrained; }
+ }
+
+ public override bool HasFixedSize {
+ get { return true; }
+ }
+
+ public override CharacterBufferReference CharacterBufferReference {
+ get { return new CharacterBufferReference(); }
+ }
+
+ public override int Length {
+ get { return 1; }
+ }
+
+ public override TextRunProperties Properties {
+ get { return properties; }
+ }
+
+ public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
+ {
+ double width = Math.Min(0, element.text.WidthIncludingTrailingWhitespace - 1);
+ return new TextEmbeddedObjectMetrics(width, element.text.Height, element.text.Baseline);
+ }
+
+ public override Rect ComputeBoundingBox(bool rightToLeft, bool sideways)
+ {
+ double width = Math.Min(0, element.text.WidthIncludingTrailingWhitespace - 1);
+ return new Rect(0, 0, width, element.text.Height);
+ }
+
+ public override void Draw(DrawingContext drawingContext, Point origin, bool rightToLeft, bool sideways)
+ {
+ origin.Y -= element.text.Baseline;
+ element.text.Draw(drawingContext, origin, InvertAxes.None);
+ }
+ }
+
+ sealed class SpecialCharacterBoxElement : FormattedTextElement
+ {
+ public SpecialCharacterBoxElement(TextLine text) : base(text, 1)
+ {
+ }
+
+ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
+ {
+ return new SpecialCharacterTextRun(this, this.TextRunProperties);
+ }
+ }
+
+ sealed class SpecialCharacterTextRun : FormattedTextRun
+ {
+ static readonly SolidColorBrush darkGrayBrush;
+
+ static SpecialCharacterTextRun()
+ {
+ darkGrayBrush = new SolidColorBrush(Color.FromArgb(200, 128, 128, 128));
+ darkGrayBrush.Freeze();
+ }
+
+ public SpecialCharacterTextRun(FormattedTextElement element, TextRunProperties properties)
+ : base(element, properties)
+ {
+ }
+
+ public override void Draw(DrawingContext drawingContext, Point origin, bool rightToLeft, bool sideways)
+ {
+ Point newOrigin = new Point(origin.X + 1.5, origin.Y);
+ var metrics = base.Format(double.PositiveInfinity);
+ Rect r = new Rect(newOrigin.X - 0.5, newOrigin.Y - metrics.Baseline, metrics.Width + 2, metrics.Height);
+ drawingContext.DrawRoundedRectangle(darkGrayBrush, null, r, 2.5, 2.5);
+ base.Draw(drawingContext, newOrigin, rightToLeft, sideways);
+ }
+
+ public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
+ {
+ TextEmbeddedObjectMetrics metrics = base.Format(remainingParagraphWidth);
+ return new TextEmbeddedObjectMetrics(metrics.Width + 3,
+ metrics.Height, metrics.Baseline);
+ }
+
+ public override Rect ComputeBoundingBox(bool rightToLeft, bool sideways)
+ {
+ Rect r = base.ComputeBoundingBox(rightToLeft, sideways);
+ r.Width += 3;
+ return r;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextLayer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextLayer.cs
new file mode 100644
index 000000000..3f4b8299b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextLayer.cs
@@ -0,0 +1,70 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// The control that contains the text.
+ ///
+ /// This control is used to allow other UIElements to be placed inside the TextView but
+ /// behind the text.
+ /// The text rendering process (VisualLine creation) is controlled by the TextView, this
+ /// class simply displays the created Visual Lines.
+ /// </summary>
+ /// <remarks>
+ /// This class does not contain any input handling and is invisible to hit testing. Input
+ /// is handled by the TextView.
+ /// This allows UIElements that are displayed behind the text, but still can react to mouse input.
+ /// </remarks>
+ sealed class TextLayer : Layer
+ {
+ /// <summary>
+ /// the index of the text layer in the layers collection
+ /// </summary>
+ internal int index;
+
+ public TextLayer(TextView textView) : base(textView, KnownLayer.Text)
+ {
+ }
+
+ List<VisualLineDrawingVisual> visuals = new List<VisualLineDrawingVisual>();
+
+ internal void SetVisualLines(ICollection<VisualLine> visualLines)
+ {
+ foreach (VisualLineDrawingVisual v in visuals) {
+ if (v.VisualLine.IsDisposed)
+ RemoveVisualChild(v);
+ }
+ visuals.Clear();
+ foreach (VisualLine newLine in visualLines) {
+ VisualLineDrawingVisual v = newLine.Render();
+ if (!v.IsAdded) {
+ AddVisualChild(v);
+ v.IsAdded = true;
+ }
+ visuals.Add(v);
+ }
+ InvalidateArrange();
+ }
+
+ protected override int VisualChildrenCount {
+ get { return visuals.Count; }
+ }
+
+ protected override Visual GetVisualChild(int index)
+ {
+ return visuals[index];
+ }
+
+ protected override void ArrangeCore(Rect finalRect)
+ {
+ textView.ArrangeTextLayer(visuals);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextView.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextView.cs
new file mode 100644
index 000000000..63f577634
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextView.cs
@@ -0,0 +1,2003 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using System.Windows.Threading;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// A virtualizing panel producing+showing <see cref="VisualLine"/>s for a <see cref="TextDocument"/>.
+ ///
+ /// This is the heart of the text editor, this class controls the text rendering process.
+ ///
+ /// Taken as a standalone control, it's a text viewer without any editing capability.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
+ Justification = "The user usually doesn't work with TextView but with TextEditor; and nulling the Document property is sufficient to dispose everything.")]
+ public class TextView : FrameworkElement, IScrollInfo, IWeakEventListener, ITextEditorComponent, IServiceProvider
+ {
+ #region Constructor
+ static TextView()
+ {
+ ClipToBoundsProperty.OverrideMetadata(typeof(TextView), new FrameworkPropertyMetadata(Boxes.True));
+ FocusableProperty.OverrideMetadata(typeof(TextView), new FrameworkPropertyMetadata(Boxes.False));
+ }
+
+ ColumnRulerRenderer columnRulerRenderer;
+
+ /// <summary>
+ /// Creates a new TextView instance.
+ /// </summary>
+ public TextView()
+ {
+ services.AddService(typeof(TextView), this);
+ textLayer = new TextLayer(this);
+ elementGenerators = new ObserveAddRemoveCollection<VisualLineElementGenerator>(ElementGenerator_Added, ElementGenerator_Removed);
+ lineTransformers = new ObserveAddRemoveCollection<IVisualLineTransformer>(LineTransformer_Added, LineTransformer_Removed);
+ backgroundRenderers = new ObserveAddRemoveCollection<IBackgroundRenderer>(BackgroundRenderer_Added, BackgroundRenderer_Removed);
+ columnRulerRenderer = new ColumnRulerRenderer(this);
+ this.Options = new TextEditorOptions();
+
+ Debug.Assert(singleCharacterElementGenerator != null); // assert that the option change created the builtin element generators
+
+ layers = new LayerCollection(this);
+ InsertLayer(textLayer, KnownLayer.Text, LayerInsertionPosition.Replace);
+
+ this.hoverLogic = new MouseHoverLogic(this);
+ this.hoverLogic.MouseHover += (sender, e) => RaiseHoverEventPair(e, PreviewMouseHoverEvent, MouseHoverEvent);
+ this.hoverLogic.MouseHoverStopped += (sender, e) => RaiseHoverEventPair(e, PreviewMouseHoverStoppedEvent, MouseHoverStoppedEvent);
+ }
+
+ #endregion
+
+ #region Document Property
+ /// <summary>
+ /// Document property.
+ /// </summary>
+ public static readonly DependencyProperty DocumentProperty =
+ DependencyProperty.Register("Document", typeof(TextDocument), typeof(TextView),
+ new FrameworkPropertyMetadata(OnDocumentChanged));
+
+ TextDocument document;
+ HeightTree heightTree;
+
+ /// <summary>
+ /// Gets/Sets the document displayed by the text editor.
+ /// </summary>
+ public TextDocument Document {
+ get { return (TextDocument)GetValue(DocumentProperty); }
+ set { SetValue(DocumentProperty, value); }
+ }
+
+ static void OnDocumentChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextView)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue);
+ }
+
+ internal double FontSize {
+ get {
+ return (double)GetValue(TextBlock.FontSizeProperty);
+ }
+ }
+
+ /// <summary>
+ /// Occurs when the document property has changed.
+ /// </summary>
+ public event EventHandler DocumentChanged;
+
+ void OnDocumentChanged(TextDocument oldValue, TextDocument newValue)
+ {
+ if (oldValue != null) {
+ heightTree.Dispose();
+ heightTree = null;
+ formatter.Dispose();
+ formatter = null;
+ cachedElements.Dispose();
+ cachedElements = null;
+ TextDocumentWeakEventManager.Changing.RemoveListener(oldValue, this);
+ }
+ this.document = newValue;
+ ClearScrollData();
+ ClearVisualLines();
+ if (newValue != null) {
+ TextDocumentWeakEventManager.Changing.AddListener(newValue, this);
+ formatter = TextFormatterFactory.Create(this);
+ InvalidateDefaultTextMetrics(); // measuring DefaultLineHeight depends on formatter
+ heightTree = new HeightTree(newValue, DefaultLineHeight);
+ cachedElements = new TextViewCachedElements();
+ }
+ InvalidateMeasure(DispatcherPriority.Normal);
+ if (DocumentChanged != null)
+ DocumentChanged(this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Recreates the text formatter that is used internally
+ /// by calling <see cref="TextFormatterFactory.Create"/>.
+ /// </summary>
+ void RecreateTextFormatter()
+ {
+ if (formatter != null) {
+ formatter.Dispose();
+ formatter = TextFormatterFactory.Create(this);
+ Redraw();
+ }
+ }
+
+ void RecreateCachedElements()
+ {
+ if (cachedElements != null) {
+ cachedElements.Dispose();
+ cachedElements = new TextViewCachedElements();
+ }
+ }
+
+ /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
+ protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(TextDocumentWeakEventManager.Changing)) {
+ // TODO: put redraw into background so that other input events can be handled before the redraw.
+ // Unfortunately the "easy" approach (just use DispatcherPriority.Background) here makes the editor twice as slow because
+ // the caret position change forces an immediate redraw, and the text input then forces a background redraw.
+ // When fixing this, make sure performance on the SharpDevelop "type text in C# comment" stress test doesn't get significantly worse.
+ DocumentChangeEventArgs change = (DocumentChangeEventArgs)e;
+ Redraw(change.Offset, change.RemovalLength, DispatcherPriority.Normal);
+ return true;
+ } else if (managerType == typeof(PropertyChangedWeakEventManager)) {
+ OnOptionChanged((PropertyChangedEventArgs)e);
+ return true;
+ }
+ return false;
+ }
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ return ReceiveWeakEvent(managerType, sender, e);
+ }
+ #endregion
+
+ #region Options property
+ /// <summary>
+ /// Options property.
+ /// </summary>
+ public static readonly DependencyProperty OptionsProperty =
+ DependencyProperty.Register("Options", typeof(TextEditorOptions), typeof(TextView),
+ new FrameworkPropertyMetadata(OnOptionsChanged));
+
+ /// <summary>
+ /// Gets/Sets the options used by the text editor.
+ /// </summary>
+ public TextEditorOptions Options {
+ get { return (TextEditorOptions)GetValue(OptionsProperty); }
+ set { SetValue(OptionsProperty, value); }
+ }
+
+ /// <summary>
+ /// Occurs when a text editor option has changed.
+ /// </summary>
+ public event PropertyChangedEventHandler OptionChanged;
+
+ /// <summary>
+ /// Raises the <see cref="OptionChanged"/> event.
+ /// </summary>
+ protected virtual void OnOptionChanged(PropertyChangedEventArgs e)
+ {
+ if (OptionChanged != null) {
+ OptionChanged(this, e);
+ }
+
+ if (Options.ShowColumnRuler)
+ columnRulerRenderer.SetRuler(Options.ColumnRulerPosition, ColumnRulerPen);
+ else
+ columnRulerRenderer.SetRuler(-1, ColumnRulerPen);
+
+ UpdateBuiltinElementGeneratorsFromOptions();
+ Redraw();
+ }
+
+ static void OnOptionsChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextView)dp).OnOptionsChanged((TextEditorOptions)e.OldValue, (TextEditorOptions)e.NewValue);
+ }
+
+ void OnOptionsChanged(TextEditorOptions oldValue, TextEditorOptions newValue)
+ {
+ if (oldValue != null) {
+ PropertyChangedWeakEventManager.RemoveListener(oldValue, this);
+ }
+ if (newValue != null) {
+ PropertyChangedWeakEventManager.AddListener(newValue, this);
+ }
+ OnOptionChanged(new PropertyChangedEventArgs(null));
+ }
+ #endregion
+
+ #region ElementGenerators+LineTransformers Properties
+ readonly ObserveAddRemoveCollection<VisualLineElementGenerator> elementGenerators;
+
+ /// <summary>
+ /// Gets a collection where element generators can be registered.
+ /// </summary>
+ public IList<VisualLineElementGenerator> ElementGenerators {
+ get { return elementGenerators; }
+ }
+
+ void ElementGenerator_Added(VisualLineElementGenerator generator)
+ {
+ ConnectToTextView(generator);
+ Redraw();
+ }
+
+ void ElementGenerator_Removed(VisualLineElementGenerator generator)
+ {
+ DisconnectFromTextView(generator);
+ Redraw();
+ }
+
+ readonly ObserveAddRemoveCollection<IVisualLineTransformer> lineTransformers;
+
+ /// <summary>
+ /// Gets a collection where line transformers can be registered.
+ /// </summary>
+ public IList<IVisualLineTransformer> LineTransformers {
+ get { return lineTransformers; }
+ }
+
+ void LineTransformer_Added(IVisualLineTransformer lineTransformer)
+ {
+ ConnectToTextView(lineTransformer);
+ Redraw();
+ }
+
+ void LineTransformer_Removed(IVisualLineTransformer lineTransformer)
+ {
+ DisconnectFromTextView(lineTransformer);
+ Redraw();
+ }
+ #endregion
+
+ #region Builtin ElementGenerators
+// NewLineElementGenerator newLineElementGenerator;
+ SingleCharacterElementGenerator singleCharacterElementGenerator;
+ LinkElementGenerator linkElementGenerator;
+ MailLinkElementGenerator mailLinkElementGenerator;
+
+ void UpdateBuiltinElementGeneratorsFromOptions()
+ {
+ TextEditorOptions options = this.Options;
+
+// AddRemoveDefaultElementGeneratorOnDemand(ref newLineElementGenerator, options.ShowEndOfLine);
+ AddRemoveDefaultElementGeneratorOnDemand(ref singleCharacterElementGenerator, options.ShowBoxForControlCharacters || options.ShowSpaces || options.ShowTabs);
+ AddRemoveDefaultElementGeneratorOnDemand(ref linkElementGenerator, options.EnableHyperlinks);
+ AddRemoveDefaultElementGeneratorOnDemand(ref mailLinkElementGenerator, options.EnableEmailHyperlinks);
+ }
+
+ void AddRemoveDefaultElementGeneratorOnDemand<T>(ref T generator, bool demand)
+ where T : VisualLineElementGenerator, IBuiltinElementGenerator, new()
+ {
+ bool hasGenerator = generator != null;
+ if (hasGenerator != demand) {
+ if (demand) {
+ generator = new T();
+ this.ElementGenerators.Add(generator);
+ } else {
+ this.ElementGenerators.Remove(generator);
+ generator = null;
+ }
+ }
+ if (generator != null)
+ generator.FetchOptions(this.Options);
+ }
+ #endregion
+
+ #region Layers
+ internal readonly TextLayer textLayer;
+ readonly LayerCollection layers;
+
+ /// <summary>
+ /// Gets the list of layers displayed in the text view.
+ /// </summary>
+ public UIElementCollection Layers {
+ get { return layers; }
+ }
+
+ sealed class LayerCollection : UIElementCollection
+ {
+ readonly TextView textView;
+
+ public LayerCollection(TextView textView)
+ : base(textView, textView)
+ {
+ this.textView = textView;
+ }
+
+ public override void Clear()
+ {
+ base.Clear();
+ textView.LayersChanged();
+ }
+
+ public override int Add(UIElement element)
+ {
+ int r = base.Add(element);
+ textView.LayersChanged();
+ return r;
+ }
+
+ public override void RemoveAt(int index)
+ {
+ base.RemoveAt(index);
+ textView.LayersChanged();
+ }
+
+ public override void RemoveRange(int index, int count)
+ {
+ base.RemoveRange(index, count);
+ textView.LayersChanged();
+ }
+ }
+
+ void LayersChanged()
+ {
+ textLayer.index = layers.IndexOf(textLayer);
+ }
+
+ /// <summary>
+ /// Inserts a new layer at a position specified relative to an existing layer.
+ /// </summary>
+ /// <param name="layer">The new layer to insert.</param>
+ /// <param name="referencedLayer">The existing layer</param>
+ /// <param name="position">Specifies whether the layer is inserted above,below, or replaces the referenced layer</param>
+ public void InsertLayer(UIElement layer, KnownLayer referencedLayer, LayerInsertionPosition position)
+ {
+ if (layer == null)
+ throw new ArgumentNullException("layer");
+ if (!Enum.IsDefined(typeof(KnownLayer), referencedLayer))
+ throw new InvalidEnumArgumentException("referencedLayer", (int)referencedLayer, typeof(KnownLayer));
+ if (!Enum.IsDefined(typeof(LayerInsertionPosition), position))
+ throw new InvalidEnumArgumentException("position", (int)position, typeof(LayerInsertionPosition));
+ if (referencedLayer == KnownLayer.Background && position != LayerInsertionPosition.Above)
+ throw new InvalidOperationException("Cannot replace or insert below the background layer.");
+
+ LayerPosition newPosition = new LayerPosition(referencedLayer, position);
+ LayerPosition.SetLayerPosition(layer, newPosition);
+ for (int i = 0; i < layers.Count; i++) {
+ LayerPosition p = LayerPosition.GetLayerPosition(layers[i]);
+ if (p != null) {
+ if (p.KnownLayer == referencedLayer && p.Position == LayerInsertionPosition.Replace) {
+ // found the referenced layer
+ switch (position) {
+ case LayerInsertionPosition.Below:
+ layers.Insert(i, layer);
+ return;
+ case LayerInsertionPosition.Above:
+ layers.Insert(i + 1, layer);
+ return;
+ case LayerInsertionPosition.Replace:
+ layers[i] = layer;
+ return;
+ }
+ } else if (p.KnownLayer == referencedLayer && p.Position == LayerInsertionPosition.Above
+ || p.KnownLayer > referencedLayer) {
+ // we skipped the insertion position (referenced layer does not exist?)
+ layers.Insert(i, layer);
+ return;
+ }
+ }
+ }
+ // inserting after all existing layers:
+ layers.Add(layer);
+ }
+
+ /// <inheritdoc/>
+ protected override int VisualChildrenCount {
+ get { return layers.Count + inlineObjects.Count; }
+ }
+
+ /// <inheritdoc/>
+ protected override Visual GetVisualChild(int index)
+ {
+ int cut = textLayer.index + 1;
+ if (index < cut)
+ return layers[index];
+ else if (index < cut + inlineObjects.Count)
+ return inlineObjects[index - cut].Element;
+ else
+ return layers[index - inlineObjects.Count];
+ }
+
+ /// <inheritdoc/>
+ protected override System.Collections.IEnumerator LogicalChildren {
+ get {
+ return inlineObjects.Select(io => io.Element).Concat(layers.Cast<UIElement>()).GetEnumerator();
+ }
+ }
+ #endregion
+
+ #region Inline object handling
+ List<InlineObjectRun> inlineObjects = new List<InlineObjectRun>();
+
+ /// <summary>
+ /// Adds a new inline object.
+ /// </summary>
+ internal void AddInlineObject(InlineObjectRun inlineObject)
+ {
+ Debug.Assert(inlineObject.VisualLine != null);
+
+ // Remove inline object if its already added, can happen e.g. when recreating textrun for word-wrapping
+ bool alreadyAdded = false;
+ for (int i = 0; i < inlineObjects.Count; i++) {
+ if (inlineObjects[i].Element == inlineObject.Element) {
+ RemoveInlineObjectRun(inlineObjects[i], true);
+ inlineObjects.RemoveAt(i);
+ alreadyAdded = true;
+ break;
+ }
+ }
+
+ inlineObjects.Add(inlineObject);
+ if (!alreadyAdded) {
+ AddVisualChild(inlineObject.Element);
+ }
+ inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+ inlineObject.desiredSize = inlineObject.Element.DesiredSize;
+ }
+
+ void MeasureInlineObjects()
+ {
+ // As part of MeasureOverride(), re-measure the inline objects
+ foreach (InlineObjectRun inlineObject in inlineObjects) {
+ if (inlineObject.VisualLine.IsDisposed) {
+ // Don't re-measure inline objects that are going to be removed anyways.
+ // If the inline object will be reused in a different VisualLine, we'll measure it in the AddInlineObject() call.
+ continue;
+ }
+ inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+ if (!inlineObject.Element.DesiredSize.IsClose(inlineObject.desiredSize)) {
+ // the element changed size -> recreate its parent visual line
+ inlineObject.desiredSize = inlineObject.Element.DesiredSize;
+ if (allVisualLines.Remove(inlineObject.VisualLine)) {
+ DisposeVisualLine(inlineObject.VisualLine);
+ }
+ }
+ }
+ }
+
+ List<VisualLine> visualLinesWithOutstandingInlineObjects = new List<VisualLine>();
+
+ void RemoveInlineObjects(VisualLine visualLine)
+ {
+ // Delay removing inline objects:
+ // A document change immediately invalidates affected visual lines, but it does not
+ // cause an immediate redraw.
+ // To prevent inline objects from flickering when they are recreated, we delay removing
+ // inline objects until the next redraw.
+ if (visualLine.hasInlineObjects) {
+ visualLinesWithOutstandingInlineObjects.Add(visualLine);
+ }
+ }
+
+ /// <summary>
+ /// Remove the inline objects that were marked for removal.
+ /// </summary>
+ void RemoveInlineObjectsNow()
+ {
+ if (visualLinesWithOutstandingInlineObjects.Count == 0)
+ return;
+ inlineObjects.RemoveAll(
+ ior => {
+ if (visualLinesWithOutstandingInlineObjects.Contains(ior.VisualLine)) {
+ RemoveInlineObjectRun(ior, false);
+ return true;
+ }
+ return false;
+ });
+ visualLinesWithOutstandingInlineObjects.Clear();
+ }
+
+ // Remove InlineObjectRun.Element from TextLayer.
+ // Caller of RemoveInlineObjectRun will remove it from inlineObjects collection.
+ void RemoveInlineObjectRun(InlineObjectRun ior, bool keepElement)
+ {
+ if (!keepElement && ior.Element.IsKeyboardFocusWithin) {
+ // When the inline element that has the focus is removed, WPF will reset the
+ // focus to the main window without raising appropriate LostKeyboardFocus events.
+ // To work around this, we manually set focus to the next focusable parent.
+ UIElement element = this;
+ while (element != null && !element.Focusable) {
+ element = VisualTreeHelper.GetParent(element) as UIElement;
+ }
+ if (element != null)
+ Keyboard.Focus(element);
+ }
+ ior.VisualLine = null;
+ if (!keepElement)
+ RemoveVisualChild(ior.Element);
+ }
+ #endregion
+
+ #region Brushes
+ /// <summary>
+ /// NonPrintableCharacterBrush dependency property.
+ /// </summary>
+ public static readonly DependencyProperty NonPrintableCharacterBrushProperty =
+ DependencyProperty.Register("NonPrintableCharacterBrush", typeof(Brush), typeof(TextView),
+ new FrameworkPropertyMetadata(Brushes.LightGray));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying non-printable characters.
+ /// </summary>
+ public Brush NonPrintableCharacterBrush {
+ get { return (Brush)GetValue(NonPrintableCharacterBrushProperty); }
+ set { SetValue(NonPrintableCharacterBrushProperty, value); }
+ }
+
+ /// <summary>
+ /// LinkTextForegroundBrush dependency property.
+ /// </summary>
+ public static readonly DependencyProperty LinkTextForegroundBrushProperty =
+ DependencyProperty.Register("LinkTextForegroundBrush", typeof(Brush), typeof(TextView),
+ new FrameworkPropertyMetadata(Brushes.Blue));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying link texts.
+ /// </summary>
+ public Brush LinkTextForegroundBrush {
+ get { return (Brush)GetValue(LinkTextForegroundBrushProperty); }
+ set { SetValue(LinkTextForegroundBrushProperty, value); }
+ }
+
+ /// <summary>
+ /// LinkTextBackgroundBrush dependency property.
+ /// </summary>
+ public static readonly DependencyProperty LinkTextBackgroundBrushProperty =
+ DependencyProperty.Register("LinkTextBackgroundBrush", typeof(Brush), typeof(TextView),
+ new FrameworkPropertyMetadata(Brushes.Transparent));
+
+ /// <summary>
+ /// Gets/sets the Brush used for the background of link texts.
+ /// </summary>
+ public Brush LinkTextBackgroundBrush {
+ get { return (Brush)GetValue(LinkTextBackgroundBrushProperty); }
+ set { SetValue(LinkTextBackgroundBrushProperty, value); }
+ }
+ #endregion
+
+ #region Redraw methods / VisualLine invalidation
+ /// <summary>
+ /// Causes the text editor to regenerate all visual lines.
+ /// </summary>
+ public void Redraw()
+ {
+ Redraw(DispatcherPriority.Normal);
+ }
+
+ /// <summary>
+ /// Causes the text editor to regenerate all visual lines.
+ /// </summary>
+ public void Redraw(DispatcherPriority redrawPriority)
+ {
+ VerifyAccess();
+ ClearVisualLines();
+ InvalidateMeasure(redrawPriority);
+ }
+
+ /// <summary>
+ /// Causes the text editor to regenerate the specified visual line.
+ /// </summary>
+ public void Redraw(VisualLine visualLine, DispatcherPriority redrawPriority = DispatcherPriority.Normal)
+ {
+ VerifyAccess();
+ if (allVisualLines.Remove(visualLine)) {
+ DisposeVisualLine(visualLine);
+ InvalidateMeasure(redrawPriority);
+ }
+ }
+
+ /// <summary>
+ /// Causes the text editor to redraw all lines overlapping with the specified segment.
+ /// </summary>
+ public void Redraw(int offset, int length, DispatcherPriority redrawPriority = DispatcherPriority.Normal)
+ {
+ VerifyAccess();
+ bool changedSomethingBeforeOrInLine = false;
+ for (int i = 0; i < allVisualLines.Count; i++) {
+ VisualLine visualLine = allVisualLines[i];
+ int lineStart = visualLine.FirstDocumentLine.Offset;
+ int lineEnd = visualLine.LastDocumentLine.Offset + visualLine.LastDocumentLine.TotalLength;
+ if (offset <= lineEnd) {
+ changedSomethingBeforeOrInLine = true;
+ if (offset + length >= lineStart) {
+ allVisualLines.RemoveAt(i--);
+ DisposeVisualLine(visualLine);
+ }
+ }
+ }
+ if (changedSomethingBeforeOrInLine) {
+ // Repaint not only when something in visible area was changed, but also when anything in front of it
+ // was changed. We might have to redraw the line number margin. Or the highlighting changed.
+ // However, we'll try to reuse the existing VisualLines.
+ InvalidateMeasure(redrawPriority);
+ }
+ }
+
+ /// <summary>
+ /// Causes a known layer to redraw.
+ /// This method does not invalidate visual lines;
+ /// use the <see cref="Redraw()"/> method to do that.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "knownLayer",
+ Justification="This method is meant to invalidate only a specific layer - I just haven't figured out how to do that, yet.")]
+ public void InvalidateLayer(KnownLayer knownLayer)
+ {
+ InvalidateMeasure(DispatcherPriority.Normal);
+ }
+
+ /// <summary>
+ /// Causes a known layer to redraw.
+ /// This method does not invalidate visual lines;
+ /// use the <see cref="Redraw()"/> method to do that.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "knownLayer",
+ Justification="This method is meant to invalidate only a specific layer - I just haven't figured out how to do that, yet.")]
+ public void InvalidateLayer(KnownLayer knownLayer, DispatcherPriority priority)
+ {
+ InvalidateMeasure(priority);
+ }
+
+ /// <summary>
+ /// Causes the text editor to redraw all lines overlapping with the specified segment.
+ /// Does nothing if segment is null.
+ /// </summary>
+ public void Redraw(ISegment segment, DispatcherPriority redrawPriority = DispatcherPriority.Normal)
+ {
+ if (segment != null) {
+ Redraw(segment.Offset, segment.Length, redrawPriority);
+ }
+ }
+
+ /// <summary>
+ /// Invalidates all visual lines.
+ /// The caller of ClearVisualLines() must also call InvalidateMeasure() to ensure
+ /// that the visual lines will be recreated.
+ /// </summary>
+ void ClearVisualLines()
+ {
+ visibleVisualLines = null;
+ if (allVisualLines.Count != 0) {
+ foreach (VisualLine visualLine in allVisualLines) {
+ DisposeVisualLine(visualLine);
+ }
+ allVisualLines.Clear();
+ }
+ }
+
+ void DisposeVisualLine(VisualLine visualLine)
+ {
+ if (newVisualLines != null && newVisualLines.Contains(visualLine)) {
+ throw new ArgumentException("Cannot dispose visual line because it is in construction!");
+ }
+ visibleVisualLines = null;
+ visualLine.Dispose();
+ RemoveInlineObjects(visualLine);
+ }
+ #endregion
+
+ #region InvalidateMeasure(DispatcherPriority)
+ DispatcherOperation invalidateMeasureOperation;
+
+ void InvalidateMeasure(DispatcherPriority priority)
+ {
+ if (priority >= DispatcherPriority.Render) {
+ if (invalidateMeasureOperation != null) {
+ invalidateMeasureOperation.Abort();
+ invalidateMeasureOperation = null;
+ }
+ base.InvalidateMeasure();
+ } else {
+ if (invalidateMeasureOperation != null) {
+ invalidateMeasureOperation.Priority = priority;
+ } else {
+ invalidateMeasureOperation = Dispatcher.BeginInvoke(
+ priority,
+ new Action(
+ delegate {
+ invalidateMeasureOperation = null;
+ base.InvalidateMeasure();
+ }
+ )
+ );
+ }
+ }
+ }
+ #endregion
+
+ #region Get(OrConstruct)VisualLine
+ /// <summary>
+ /// Gets the visual line that contains the document line with the specified number.
+ /// Returns null if the document line is outside the visible range.
+ /// </summary>
+ public VisualLine GetVisualLine(int documentLineNumber)
+ {
+ // TODO: EnsureVisualLines() ?
+ foreach (VisualLine visualLine in allVisualLines) {
+ Debug.Assert(visualLine.IsDisposed == false);
+ int start = visualLine.FirstDocumentLine.LineNumber;
+ int end = visualLine.LastDocumentLine.LineNumber;
+ if (documentLineNumber >= start && documentLineNumber <= end)
+ return visualLine;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Gets the visual line that contains the document line with the specified number.
+ /// If that line is outside the visible range, a new VisualLine for that document line is constructed.
+ /// </summary>
+ public VisualLine GetOrConstructVisualLine(DocumentLine documentLine)
+ {
+ if (documentLine == null)
+ throw new ArgumentNullException("documentLine");
+ if (!this.Document.Lines.Contains(documentLine))
+ throw new InvalidOperationException("Line belongs to wrong document");
+ VerifyAccess();
+
+ VisualLine l = GetVisualLine(documentLine.LineNumber);
+ if (l == null) {
+ TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties();
+ VisualLineTextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties);
+
+ while (heightTree.GetIsCollapsed(documentLine.LineNumber)) {
+ documentLine = documentLine.PreviousLine;
+ }
+
+ l = BuildVisualLine(documentLine,
+ globalTextRunProperties, paragraphProperties,
+ elementGenerators.ToArray(), lineTransformers.ToArray(),
+ lastAvailableSize);
+ allVisualLines.Add(l);
+ // update all visual top values (building the line might have changed visual top of other lines due to word wrapping)
+ foreach (var line in allVisualLines) {
+ line.VisualTop = heightTree.GetVisualPosition(line.FirstDocumentLine);
+ }
+ }
+ return l;
+ }
+ #endregion
+
+ #region Visual Lines (fields and properties)
+ List<VisualLine> allVisualLines = new List<VisualLine>();
+ ReadOnlyCollection<VisualLine> visibleVisualLines;
+ double clippedPixelsOnTop;
+ List<VisualLine> newVisualLines;
+
+ /// <summary>
+ /// Gets the currently visible visual lines.
+ /// </summary>
+ /// <exception cref="VisualLinesInvalidException">
+ /// Gets thrown if there are invalid visual lines when this property is accessed.
+ /// You can use the <see cref="VisualLinesValid"/> property to check for this case,
+ /// or use the <see cref="EnsureVisualLines()"/> method to force creating the visual lines
+ /// when they are invalid.
+ /// </exception>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
+ public ReadOnlyCollection<VisualLine> VisualLines {
+ get {
+ if (visibleVisualLines == null)
+ throw new VisualLinesInvalidException();
+ return visibleVisualLines;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the visual lines are valid.
+ /// Will return false after a call to Redraw().
+ /// Accessing the visual lines property will cause a <see cref="VisualLinesInvalidException"/>
+ /// if this property is <c>false</c>.
+ /// </summary>
+ public bool VisualLinesValid {
+ get { return visibleVisualLines != null; }
+ }
+
+ /// <summary>
+ /// Occurs when the TextView is about to be measured and will regenerate its visual lines.
+ /// This event may be used to mark visual lines as invalid that would otherwise be reused.
+ /// </summary>
+ public event EventHandler<VisualLineConstructionStartEventArgs> VisualLineConstructionStarting;
+
+ /// <summary>
+ /// Occurs when the TextView was measured and changed its visual lines.
+ /// </summary>
+ public event EventHandler VisualLinesChanged;
+
+ /// <summary>
+ /// If the visual lines are invalid, creates new visual lines for the visible part
+ /// of the document.
+ /// If all visual lines are valid, this method does nothing.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The visual line build process is already running.
+ /// It is not allowed to call this method during the construction of a visual line.</exception>
+ public void EnsureVisualLines()
+ {
+ Dispatcher.VerifyAccess();
+ if (inMeasure)
+ throw new InvalidOperationException("The visual line build process is already running! Cannot EnsureVisualLines() during Measure!");
+ if (!VisualLinesValid) {
+ // increase priority for re-measure
+ InvalidateMeasure(DispatcherPriority.Normal);
+ // force immediate re-measure
+ UpdateLayout();
+ }
+ // Sometimes we still have invalid lines after UpdateLayout - work around the problem
+ // by calling MeasureOverride directly.
+ if (!VisualLinesValid) {
+ Debug.WriteLine("UpdateLayout() failed in EnsureVisualLines");
+ MeasureOverride(lastAvailableSize);
+ }
+ if (!VisualLinesValid)
+ throw new VisualLinesInvalidException("Internal error: visual lines invalid after EnsureVisualLines call");
+ }
+ #endregion
+
+ #region Measure
+ /// <summary>
+ /// Additonal amount that allows horizontal scrolling past the end of the longest line.
+ /// This is necessary to ensure the caret always is visible, even when it is at the end of the longest line.
+ /// </summary>
+ const double AdditionalHorizontalScrollAmount = 3;
+
+ Size lastAvailableSize;
+ bool inMeasure;
+
+ /// <inheritdoc/>
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ // We don't support infinite available width, so we'll limit it to 32000 pixels.
+ if (availableSize.Width > 32000)
+ availableSize.Width = 32000;
+
+ if (!canHorizontallyScroll && !availableSize.Width.IsClose(lastAvailableSize.Width))
+ ClearVisualLines();
+ lastAvailableSize = availableSize;
+
+ foreach (UIElement layer in layers) {
+ layer.Measure(availableSize);
+ }
+ MeasureInlineObjects();
+
+ InvalidateVisual(); // = InvalidateArrange+InvalidateRender
+
+ double maxWidth;
+ if (document == null) {
+ // no document -> create empty list of lines
+ allVisualLines = new List<VisualLine>();
+ visibleVisualLines = allVisualLines.AsReadOnly();
+ maxWidth = 0;
+ } else {
+ inMeasure = true;
+ try {
+ maxWidth = CreateAndMeasureVisualLines(availableSize);
+ } finally {
+ inMeasure = false;
+ }
+ }
+
+ // remove inline objects only at the end, so that inline objects that were re-used are not removed from the editor
+ RemoveInlineObjectsNow();
+
+ maxWidth += AdditionalHorizontalScrollAmount;
+ double heightTreeHeight = this.DocumentHeight;
+ TextEditorOptions options = this.Options;
+ if (options.AllowScrollBelowDocument) {
+ if (!double.IsInfinity(scrollViewport.Height)) {
+ heightTreeHeight = Math.Max(heightTreeHeight, Math.Min(heightTreeHeight - 50, scrollOffset.Y) + scrollViewport.Height);
+ }
+ }
+
+ textLayer.SetVisualLines(visibleVisualLines);
+
+ SetScrollData(availableSize,
+ new Size(maxWidth, heightTreeHeight),
+ scrollOffset);
+ if (VisualLinesChanged != null)
+ VisualLinesChanged(this, EventArgs.Empty);
+
+ return new Size(Math.Min(availableSize.Width, maxWidth), Math.Min(availableSize.Height, heightTreeHeight));
+ }
+
+ /// <summary>
+ /// Build all VisualLines in the visible range.
+ /// </summary>
+ /// <returns>Width the longest line</returns>
+ double CreateAndMeasureVisualLines(Size availableSize)
+ {
+ TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties();
+ VisualLineTextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties);
+
+ Debug.WriteLine("Measure availableSize=" + availableSize + ", scrollOffset=" + scrollOffset);
+ var firstLineInView = heightTree.GetLineByVisualPosition(scrollOffset.Y);
+
+ // number of pixels clipped from the first visual line(s)
+ clippedPixelsOnTop = scrollOffset.Y - heightTree.GetVisualPosition(firstLineInView);
+ // clippedPixelsOnTop should be >= 0, except for floating point inaccurracy.
+ Debug.Assert(clippedPixelsOnTop >= -ExtensionMethods.Epsilon);
+
+ newVisualLines = new List<VisualLine>();
+
+ if (VisualLineConstructionStarting != null)
+ VisualLineConstructionStarting(this, new VisualLineConstructionStartEventArgs(firstLineInView));
+
+ var elementGeneratorsArray = elementGenerators.ToArray();
+ var lineTransformersArray = lineTransformers.ToArray();
+ var nextLine = firstLineInView;
+ double maxWidth = 0;
+ double yPos = -clippedPixelsOnTop;
+ while (yPos < availableSize.Height && nextLine != null) {
+ VisualLine visualLine = GetVisualLine(nextLine.LineNumber);
+ if (visualLine == null) {
+ visualLine = BuildVisualLine(nextLine,
+ globalTextRunProperties, paragraphProperties,
+ elementGeneratorsArray, lineTransformersArray,
+ availableSize);
+ }
+
+ visualLine.VisualTop = scrollOffset.Y + yPos;
+
+ nextLine = visualLine.LastDocumentLine.NextLine;
+
+ yPos += visualLine.Height;
+
+ foreach (TextLine textLine in visualLine.TextLines) {
+ if (textLine.WidthIncludingTrailingWhitespace > maxWidth)
+ maxWidth = textLine.WidthIncludingTrailingWhitespace;
+ }
+
+ newVisualLines.Add(visualLine);
+ }
+
+ foreach (VisualLine line in allVisualLines) {
+ Debug.Assert(line.IsDisposed == false);
+ if (!newVisualLines.Contains(line))
+ DisposeVisualLine(line);
+ }
+
+ allVisualLines = newVisualLines;
+ // visibleVisualLines = readonly copy of visual lines
+ visibleVisualLines = new ReadOnlyCollection<VisualLine>(newVisualLines.ToArray());
+ newVisualLines = null;
+
+ if (allVisualLines.Any(line => line.IsDisposed)) {
+ throw new InvalidOperationException("A visual line was disposed even though it is still in use.\n" +
+ "This can happen when Redraw() is called during measure for lines " +
+ "that are already constructed.");
+ }
+ return maxWidth;
+ }
+ #endregion
+
+ #region BuildVisualLine
+ TextFormatter formatter;
+ internal TextViewCachedElements cachedElements;
+
+ TextRunProperties CreateGlobalTextRunProperties()
+ {
+ var p = new GlobalTextRunProperties();
+ p.typeface = this.CreateTypeface();
+ p.fontRenderingEmSize = FontSize;
+ p.foregroundBrush = (Brush)GetValue(Control.ForegroundProperty);
+ ExtensionMethods.CheckIsFrozen(p.foregroundBrush);
+ p.cultureInfo = CultureInfo.CurrentCulture;
+ return p;
+ }
+
+ VisualLineTextParagraphProperties CreateParagraphProperties(TextRunProperties defaultTextRunProperties)
+ {
+ return new VisualLineTextParagraphProperties {
+ defaultTextRunProperties = defaultTextRunProperties,
+ textWrapping = canHorizontallyScroll ? TextWrapping.NoWrap : TextWrapping.Wrap,
+ tabSize = Options.IndentationSize * WideSpaceWidth
+ };
+ }
+
+ VisualLine BuildVisualLine(DocumentLine documentLine,
+ TextRunProperties globalTextRunProperties,
+ VisualLineTextParagraphProperties paragraphProperties,
+ VisualLineElementGenerator[] elementGeneratorsArray,
+ IVisualLineTransformer[] lineTransformersArray,
+ Size availableSize)
+ {
+ if (heightTree.GetIsCollapsed(documentLine.LineNumber))
+ throw new InvalidOperationException("Trying to build visual line from collapsed line");
+
+ Debug.WriteLine("Building line " + documentLine.LineNumber);
+
+ VisualLine visualLine = new VisualLine(this, documentLine);
+ VisualLineTextSource textSource = new VisualLineTextSource(visualLine) {
+ Document = document,
+ GlobalTextRunProperties = globalTextRunProperties,
+ TextView = this
+ };
+
+ visualLine.ConstructVisualElements(textSource, elementGeneratorsArray);
+
+ if (visualLine.FirstDocumentLine != visualLine.LastDocumentLine) {
+ // Check whether the lines are collapsed correctly:
+ double firstLinePos = heightTree.GetVisualPosition(visualLine.FirstDocumentLine.NextLine);
+ double lastLinePos = heightTree.GetVisualPosition(visualLine.LastDocumentLine.NextLine ?? visualLine.LastDocumentLine);
+ if (!firstLinePos.IsClose(lastLinePos)) {
+ for (int i = visualLine.FirstDocumentLine.LineNumber + 1; i <= visualLine.LastDocumentLine.LineNumber; i++) {
+ if (!heightTree.GetIsCollapsed(i))
+ throw new InvalidOperationException("Line " + i + " was skipped by a VisualLineElementGenerator, but it is not collapsed.");
+ }
+ throw new InvalidOperationException("All lines collapsed but visual pos different - height tree inconsistency?");
+ }
+ }
+
+ visualLine.RunTransformers(textSource, lineTransformersArray);
+
+ // now construct textLines:
+ int textOffset = 0;
+ TextLineBreak lastLineBreak = null;
+ var textLines = new List<TextLine>();
+ paragraphProperties.indent = 0;
+ paragraphProperties.firstLineInParagraph = true;
+ while (textOffset <= visualLine.VisualLengthWithEndOfLineMarker) {
+ TextLine textLine = formatter.FormatLine(
+ textSource,
+ textOffset,
+ availableSize.Width,
+ paragraphProperties,
+ lastLineBreak
+ );
+ textLines.Add(textLine);
+ textOffset += textLine.Length;
+
+ // exit loop so that we don't do the indentation calculation if there's only a single line
+ if (textOffset >= visualLine.VisualLengthWithEndOfLineMarker)
+ break;
+
+ if (paragraphProperties.firstLineInParagraph) {
+ paragraphProperties.firstLineInParagraph = false;
+
+ TextEditorOptions options = this.Options;
+ double indentation = 0;
+ if (options.InheritWordWrapIndentation) {
+ // determine indentation for next line:
+ int indentVisualColumn = GetIndentationVisualColumn(visualLine);
+ if (indentVisualColumn > 0 && indentVisualColumn < textOffset) {
+ indentation = textLine.GetDistanceFromCharacterHit(new CharacterHit(indentVisualColumn, 0));
+ }
+ }
+ indentation += options.WordWrapIndentation;
+ // apply the calculated indentation unless it's more than half of the text editor size:
+ if (indentation > 0 && indentation * 2 < availableSize.Width)
+ paragraphProperties.indent = indentation;
+ }
+ lastLineBreak = textLine.GetTextLineBreak();
+ }
+ visualLine.SetTextLines(textLines);
+ heightTree.SetHeight(visualLine.FirstDocumentLine, visualLine.Height);
+ return visualLine;
+ }
+
+ static int GetIndentationVisualColumn(VisualLine visualLine)
+ {
+ if (visualLine.Elements.Count == 0)
+ return 0;
+ int column = 0;
+ int elementIndex = 0;
+ VisualLineElement element = visualLine.Elements[elementIndex];
+ while (element.IsWhitespace(column)) {
+ column++;
+ if (column == element.VisualColumn + element.VisualLength) {
+ elementIndex++;
+ if (elementIndex == visualLine.Elements.Count)
+ break;
+ element = visualLine.Elements[elementIndex];
+ }
+ }
+ return column;
+ }
+ #endregion
+
+ #region Arrange
+ /// <summary>
+ /// Arrange implementation.
+ /// </summary>
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ EnsureVisualLines();
+
+ foreach (UIElement layer in layers) {
+ layer.Arrange(new Rect(new Point(0, 0), finalSize));
+ }
+
+ if (document == null || allVisualLines.Count == 0)
+ return finalSize;
+
+ // validate scroll position
+ Vector newScrollOffset = scrollOffset;
+ if (scrollOffset.X + finalSize.Width > scrollExtent.Width) {
+ newScrollOffset.X = Math.Max(0, scrollExtent.Width - finalSize.Width);
+ }
+ if (scrollOffset.Y + finalSize.Height > scrollExtent.Height) {
+ newScrollOffset.Y = Math.Max(0, scrollExtent.Height - finalSize.Height);
+ }
+ if (SetScrollData(scrollViewport, scrollExtent, newScrollOffset))
+ InvalidateMeasure(DispatcherPriority.Normal);
+
+ //Debug.WriteLine("Arrange finalSize=" + finalSize + ", scrollOffset=" + scrollOffset);
+
+// double maxWidth = 0;
+
+ if (visibleVisualLines != null) {
+ Point pos = new Point(-scrollOffset.X, -clippedPixelsOnTop);
+ foreach (VisualLine visualLine in visibleVisualLines) {
+ int offset = 0;
+ foreach (TextLine textLine in visualLine.TextLines) {
+ foreach (var span in textLine.GetTextRunSpans()) {
+ InlineObjectRun inline = span.Value as InlineObjectRun;
+ if (inline != null && inline.VisualLine != null) {
+ Debug.Assert(inlineObjects.Contains(inline));
+ double distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(offset, 0));
+ inline.Element.Arrange(new Rect(new Point(pos.X + distance, pos.Y), inline.Element.DesiredSize));
+ }
+ offset += span.Length;
+ }
+ pos.Y += textLine.Height;
+ }
+ }
+ }
+ InvalidateCursor();
+
+ return finalSize;
+ }
+ #endregion
+
+ #region Render
+ readonly ObserveAddRemoveCollection<IBackgroundRenderer> backgroundRenderers;
+
+ /// <summary>
+ /// Gets the list of background renderers.
+ /// </summary>
+ public IList<IBackgroundRenderer> BackgroundRenderers {
+ get { return backgroundRenderers; }
+ }
+
+ void BackgroundRenderer_Added(IBackgroundRenderer renderer)
+ {
+ ConnectToTextView(renderer);
+ InvalidateLayer(renderer.Layer);
+ }
+
+ void BackgroundRenderer_Removed(IBackgroundRenderer renderer)
+ {
+ DisconnectFromTextView(renderer);
+ InvalidateLayer(renderer.Layer);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ RenderBackground(drawingContext, KnownLayer.Background);
+ foreach (var line in visibleVisualLines) {
+ Brush currentBrush = null;
+ int startVC = 0;
+ int length = 0;
+ foreach (var element in line.Elements) {
+ if (currentBrush == null || !currentBrush.Equals(element.BackgroundBrush)) {
+ if (currentBrush != null) {
+ BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder();
+ builder.AlignToWholePixels = true;
+ builder.CornerRadius = 3;
+ foreach (var rect in BackgroundGeometryBuilder.GetRectsFromVisualSegment(this, line, startVC, startVC + length))
+ builder.AddRectangle(this, rect);
+ Geometry geometry = builder.CreateGeometry();
+ if (geometry != null) {
+ drawingContext.DrawGeometry(currentBrush, null, geometry);
+ }
+ }
+ startVC = element.VisualColumn;
+ length = element.DocumentLength;
+ currentBrush = element.BackgroundBrush;
+ } else {
+ length += element.VisualLength;
+ }
+ }
+ if (currentBrush != null) {
+ BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder();
+ builder.AlignToWholePixels = true;
+ builder.CornerRadius = 3;
+ foreach (var rect in BackgroundGeometryBuilder.GetRectsFromVisualSegment(this, line, startVC, startVC + length))
+ builder.AddRectangle(this, rect);
+ Geometry geometry = builder.CreateGeometry();
+ if (geometry != null) {
+ drawingContext.DrawGeometry(currentBrush, null, geometry);
+ }
+ }
+ }
+ }
+
+ internal void RenderBackground(DrawingContext drawingContext, KnownLayer layer)
+ {
+ foreach (IBackgroundRenderer bg in backgroundRenderers) {
+ if (bg.Layer == layer) {
+ bg.Draw(this, drawingContext);
+ }
+ }
+ }
+
+ internal void ArrangeTextLayer(IList<VisualLineDrawingVisual> visuals)
+ {
+ Point pos = new Point(-scrollOffset.X, -clippedPixelsOnTop);
+ foreach (VisualLineDrawingVisual visual in visuals) {
+ TranslateTransform t = visual.Transform as TranslateTransform;
+ if (t == null || t.X != pos.X || t.Y != pos.Y) {
+ visual.Transform = new TranslateTransform(pos.X, pos.Y);
+ visual.Transform.Freeze();
+ }
+ pos.Y += visual.Height;
+ }
+ }
+ #endregion
+
+ #region IScrollInfo implementation
+ /// <summary>
+ /// Size of the document, in pixels.
+ /// </summary>
+ Size scrollExtent;
+
+ /// <summary>
+ /// Offset of the scroll position.
+ /// </summary>
+ Vector scrollOffset;
+
+ /// <summary>
+ /// Size of the viewport.
+ /// </summary>
+ Size scrollViewport;
+
+ void ClearScrollData()
+ {
+ SetScrollData(new Size(), new Size(), new Vector());
+ }
+
+ bool SetScrollData(Size viewport, Size extent, Vector offset)
+ {
+ if (!(viewport.IsClose(this.scrollViewport)
+ && extent.IsClose(this.scrollExtent)
+ && offset.IsClose(this.scrollOffset)))
+ {
+ this.scrollViewport = viewport;
+ this.scrollExtent = extent;
+ SetScrollOffset(offset);
+ this.OnScrollChange();
+ return true;
+ }
+ return false;
+ }
+
+ void OnScrollChange()
+ {
+ ScrollViewer scrollOwner = ((IScrollInfo)this).ScrollOwner;
+ if (scrollOwner != null) {
+ scrollOwner.InvalidateScrollInfo();
+ }
+ }
+
+ bool canVerticallyScroll;
+ bool IScrollInfo.CanVerticallyScroll {
+ get { return canVerticallyScroll; }
+ set {
+ if (canVerticallyScroll != value) {
+ canVerticallyScroll = value;
+ InvalidateMeasure(DispatcherPriority.Normal);
+ }
+ }
+ }
+ bool canHorizontallyScroll;
+ bool IScrollInfo.CanHorizontallyScroll {
+ get { return canHorizontallyScroll; }
+ set {
+ if (canHorizontallyScroll != value) {
+ canHorizontallyScroll = value;
+ ClearVisualLines();
+ InvalidateMeasure(DispatcherPriority.Normal);
+ }
+ }
+ }
+
+ double IScrollInfo.ExtentWidth {
+ get { return scrollExtent.Width; }
+ }
+
+ double IScrollInfo.ExtentHeight {
+ get { return scrollExtent.Height; }
+ }
+
+ double IScrollInfo.ViewportWidth {
+ get { return scrollViewport.Width; }
+ }
+
+ double IScrollInfo.ViewportHeight {
+ get { return scrollViewport.Height; }
+ }
+
+ /// <summary>
+ /// Gets the horizontal scroll offset.
+ /// </summary>
+ public double HorizontalOffset {
+ get { return scrollOffset.X; }
+ }
+
+ /// <summary>
+ /// Gets the vertical scroll offset.
+ /// </summary>
+ public double VerticalOffset {
+ get { return scrollOffset.Y; }
+ }
+
+ /// <summary>
+ /// Gets the scroll offset;
+ /// </summary>
+ public Vector ScrollOffset {
+ get { return scrollOffset; }
+ }
+
+ /// <summary>
+ /// Occurs when the scroll offset has changed.
+ /// </summary>
+ public event EventHandler ScrollOffsetChanged;
+
+ void SetScrollOffset(Vector vector)
+ {
+ if (!canHorizontallyScroll)
+ vector.X = 0;
+ if (!canVerticallyScroll)
+ vector.Y = 0;
+
+ if (!scrollOffset.IsClose(vector)) {
+ scrollOffset = vector;
+ if (ScrollOffsetChanged != null)
+ ScrollOffsetChanged(this, EventArgs.Empty);
+ }
+ }
+
+ ScrollViewer IScrollInfo.ScrollOwner { get; set; }
+
+ void IScrollInfo.LineUp()
+ {
+ ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y - DefaultLineHeight);
+ }
+
+ void IScrollInfo.LineDown()
+ {
+ ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y + DefaultLineHeight);
+ }
+
+ void IScrollInfo.LineLeft()
+ {
+ ((IScrollInfo)this).SetHorizontalOffset(scrollOffset.X - WideSpaceWidth);
+ }
+
+ void IScrollInfo.LineRight()
+ {
+ ((IScrollInfo)this).SetHorizontalOffset(scrollOffset.X + WideSpaceWidth);
+ }
+
+ void IScrollInfo.PageUp()
+ {
+ ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y - scrollViewport.Height);
+ }
+
+ void IScrollInfo.PageDown()
+ {
+ ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y + scrollViewport.Height);
+ }
+
+ void IScrollInfo.PageLeft()
+ {
+ ((IScrollInfo)this).SetHorizontalOffset(scrollOffset.X - scrollViewport.Width);
+ }
+
+ void IScrollInfo.PageRight()
+ {
+ ((IScrollInfo)this).SetHorizontalOffset(scrollOffset.X + scrollViewport.Width);
+ }
+
+ void IScrollInfo.MouseWheelUp()
+ {
+ ((IScrollInfo)this).SetVerticalOffset(
+ scrollOffset.Y - (SystemParameters.WheelScrollLines * DefaultLineHeight));
+ OnScrollChange();
+ }
+
+ void IScrollInfo.MouseWheelDown()
+ {
+ ((IScrollInfo)this).SetVerticalOffset(
+ scrollOffset.Y + (SystemParameters.WheelScrollLines * DefaultLineHeight));
+ OnScrollChange();
+ }
+
+ void IScrollInfo.MouseWheelLeft()
+ {
+ ((IScrollInfo)this).SetHorizontalOffset(
+ scrollOffset.X - (SystemParameters.WheelScrollLines * WideSpaceWidth));
+ OnScrollChange();
+ }
+
+ void IScrollInfo.MouseWheelRight()
+ {
+ ((IScrollInfo)this).SetHorizontalOffset(
+ scrollOffset.X + (SystemParameters.WheelScrollLines * WideSpaceWidth));
+ OnScrollChange();
+ }
+
+ bool defaultTextMetricsValid;
+ double wideSpaceWidth; // Width of an 'x'. Used as basis for the tab width, and for scrolling.
+ double defaultLineHeight; // Height of a line containing 'x'. Used for scrolling.
+ double defaultBaseline; // Baseline of a line containing 'x'. Used for TextTop/TextBottom calculation.
+
+ /// <summary>
+ /// Gets the width of a 'wide space' (the space width used for calculating the tab size).
+ /// </summary>
+ /// <remarks>
+ /// This is the width of an 'x' in the current font.
+ /// We do not measure the width of an actual space as that would lead to tiny tabs in
+ /// some proportional fonts.
+ /// For monospaced fonts, this property will return the expected value, as 'x' and ' ' have the same width.
+ /// </remarks>
+ public double WideSpaceWidth {
+ get {
+ CalculateDefaultTextMetrics();
+ return wideSpaceWidth;
+ }
+ }
+
+ /// <summary>
+ /// Gets the default line height. This is the height of an empty line or a line containing regular text.
+ /// Lines that include formatted text or custom UI elements may have a different line height.
+ /// </summary>
+ public double DefaultLineHeight {
+ get {
+ CalculateDefaultTextMetrics();
+ return defaultLineHeight;
+ }
+ }
+
+ /// <summary>
+ /// Gets the default baseline position. This is the difference between <see cref="VisualYPosition.TextTop"/>
+ /// and <see cref="VisualYPosition.Baseline"/> for a line containing regular text.
+ /// Lines that include formatted text or custom UI elements may have a different baseline.
+ /// </summary>
+ public double DefaultBaseline {
+ get {
+ CalculateDefaultTextMetrics();
+ return defaultBaseline;
+ }
+ }
+
+ void InvalidateDefaultTextMetrics()
+ {
+ defaultTextMetricsValid = false;
+ if (heightTree != null) {
+ // calculate immediately so that height tree gets updated
+ CalculateDefaultTextMetrics();
+ }
+ }
+
+ void CalculateDefaultTextMetrics()
+ {
+ if (defaultTextMetricsValid)
+ return;
+ defaultTextMetricsValid = true;
+ if (formatter != null) {
+ var textRunProperties = CreateGlobalTextRunProperties();
+ using (var line = formatter.FormatLine(
+ new SimpleTextSource("x", textRunProperties),
+ 0, 32000,
+ new VisualLineTextParagraphProperties { defaultTextRunProperties = textRunProperties },
+ null))
+ {
+ wideSpaceWidth = Math.Max(1, line.WidthIncludingTrailingWhitespace);
+ defaultBaseline = Math.Max(1, line.Baseline);
+ defaultLineHeight = Math.Max(1, line.Height);
+ }
+ } else {
+ wideSpaceWidth = FontSize / 2;
+ defaultBaseline = FontSize;
+ defaultLineHeight = FontSize + 3;
+ }
+ // Update heightTree.DefaultLineHeight, if a document is loaded.
+ if (heightTree != null)
+ heightTree.DefaultLineHeight = defaultLineHeight;
+ }
+
+ static double ValidateVisualOffset(double offset)
+ {
+ if (double.IsNaN(offset))
+ throw new ArgumentException("offset must not be NaN");
+ if (offset < 0)
+ return 0;
+ else
+ return offset;
+ }
+
+ void IScrollInfo.SetHorizontalOffset(double offset)
+ {
+ offset = ValidateVisualOffset(offset);
+ if (!scrollOffset.X.IsClose(offset)) {
+ SetScrollOffset(new Vector(offset, scrollOffset.Y));
+ InvalidateVisual();
+ textLayer.InvalidateVisual();
+ }
+ }
+
+ void IScrollInfo.SetVerticalOffset(double offset)
+ {
+ offset = ValidateVisualOffset(offset);
+ if (!scrollOffset.Y.IsClose(offset)) {
+ SetScrollOffset(new Vector(scrollOffset.X, offset));
+ InvalidateMeasure(DispatcherPriority.Normal);
+ }
+ }
+
+ Rect IScrollInfo.MakeVisible(Visual visual, Rect rectangle)
+ {
+ if (rectangle.IsEmpty || visual == null || visual == this || !this.IsAncestorOf(visual)) {
+ return Rect.Empty;
+ }
+ // Convert rectangle into our coordinate space.
+ GeneralTransform childTransform = visual.TransformToAncestor(this);
+ rectangle = childTransform.TransformBounds(rectangle);
+
+ MakeVisible(Rect.Offset(rectangle, scrollOffset));
+
+ return rectangle;
+ }
+
+ /// <summary>
+ /// Scrolls the text view so that the specified rectangle gets visible.
+ /// </summary>
+ public void MakeVisible(Rect rectangle)
+ {
+ Rect visibleRectangle = new Rect(scrollOffset.X, scrollOffset.Y,
+ scrollViewport.Width, scrollViewport.Height);
+ Vector newScrollOffset = scrollOffset;
+ if (rectangle.Left < visibleRectangle.Left) {
+ if (rectangle.Right > visibleRectangle.Right) {
+ newScrollOffset.X = rectangle.Left + rectangle.Width / 2;
+ } else {
+ newScrollOffset.X = rectangle.Left;
+ }
+ } else if (rectangle.Right > visibleRectangle.Right) {
+ newScrollOffset.X = rectangle.Right - scrollViewport.Width;
+ }
+ if (rectangle.Top < visibleRectangle.Top) {
+ if (rectangle.Bottom > visibleRectangle.Bottom) {
+ newScrollOffset.Y = rectangle.Top + rectangle.Height / 2;
+ } else {
+ newScrollOffset.Y = rectangle.Top;
+ }
+ } else if (rectangle.Bottom > visibleRectangle.Bottom) {
+ newScrollOffset.Y = rectangle.Bottom - scrollViewport.Height;
+ }
+ newScrollOffset.X = ValidateVisualOffset(newScrollOffset.X);
+ newScrollOffset.Y = ValidateVisualOffset(newScrollOffset.Y);
+ if (!scrollOffset.IsClose(newScrollOffset)) {
+ SetScrollOffset(newScrollOffset);
+ this.OnScrollChange();
+ InvalidateMeasure(DispatcherPriority.Normal);
+ }
+ }
+ #endregion
+
+ #region Visual element mouse handling
+ /// <inheritdoc/>
+ protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
+ {
+ // accept clicks even where the text area draws no background
+ return new PointHitTestResult(this, hitTestParameters.HitPoint);
+ }
+
+ [ThreadStatic] static bool invalidCursor;
+
+ /// <summary>
+ /// Updates the mouse cursor by calling <see cref="Mouse.UpdateCursor"/>, but with input priority.
+ /// </summary>
+ public static void InvalidateCursor()
+ {
+ if (!invalidCursor) {
+ invalidCursor = true;
+ Dispatcher.CurrentDispatcher.BeginInvoke(
+ DispatcherPriority.Input,
+ new Action(
+ delegate {
+ invalidCursor = false;
+ Mouse.UpdateCursor();
+ }));
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnQueryCursor(QueryCursorEventArgs e)
+ {
+ VisualLineElement element = GetVisualLineElementFromPosition(e.GetPosition(this) + scrollOffset);
+ if (element != null) {
+ element.OnQueryCursor(e);
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnMouseDown(MouseButtonEventArgs e)
+ {
+ base.OnMouseDown(e);
+ if (!e.Handled) {
+ EnsureVisualLines();
+ VisualLineElement element = GetVisualLineElementFromPosition(e.GetPosition(this) + scrollOffset);
+ if (element != null) {
+ element.OnMouseDown(e);
+ }
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void OnMouseUp(MouseButtonEventArgs e)
+ {
+ base.OnMouseUp(e);
+ if (!e.Handled) {
+ EnsureVisualLines();
+ VisualLineElement element = GetVisualLineElementFromPosition(e.GetPosition(this) + scrollOffset);
+ if (element != null) {
+ element.OnMouseUp(e);
+ }
+ }
+ }
+ #endregion
+
+ #region Getting elements from Visual Position
+ /// <summary>
+ /// Gets the visual line at the specified document position (relative to start of document).
+ /// Returns null if there is no visual line for the position (e.g. the position is outside the visible
+ /// text area).
+ /// </summary>
+ public VisualLine GetVisualLineFromVisualTop(double visualTop)
+ {
+ // TODO: change this method to also work outside the visible range -
+ // required to make GetPosition work as expected!
+ EnsureVisualLines();
+ foreach (VisualLine vl in this.VisualLines) {
+ if (visualTop < vl.VisualTop)
+ continue;
+ if (visualTop < vl.VisualTop + vl.Height)
+ return vl;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Gets the visual top position (relative to start of document) from a document line number.
+ /// </summary>
+ public double GetVisualTopByDocumentLine(int line)
+ {
+ VerifyAccess();
+ if (heightTree == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return heightTree.GetVisualPosition(heightTree.GetLineByNumber(line));
+ }
+
+ VisualLineElement GetVisualLineElementFromPosition(Point visualPosition)
+ {
+ VisualLine vl = GetVisualLineFromVisualTop(visualPosition.Y);
+ if (vl != null) {
+ int column = vl.GetVisualColumnFloor(visualPosition);
+// Debug.WriteLine(vl.FirstDocumentLine.LineNumber + " vc " + column);
+ foreach (VisualLineElement element in vl.Elements) {
+ if (element.VisualColumn + element.VisualLength <= column)
+ continue;
+ return element;
+ }
+ }
+ return null;
+ }
+ #endregion
+
+ #region Visual Position <-> TextViewPosition
+ /// <summary>
+ /// Gets the visual position from a text view position.
+ /// </summary>
+ /// <param name="position">The text view position.</param>
+ /// <param name="yPositionMode">The mode how to retrieve the Y position.</param>
+ /// <returns>The position in WPF device-independent pixels relative
+ /// to the top left corner of the document.</returns>
+ public Point GetVisualPosition(TextViewPosition position, VisualYPosition yPositionMode)
+ {
+ VerifyAccess();
+ if (this.Document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ DocumentLine documentLine = this.Document.GetLineByNumber(position.Line);
+ VisualLine visualLine = GetOrConstructVisualLine(documentLine);
+ int visualColumn = position.VisualColumn;
+ if (visualColumn < 0) {
+ int offset = documentLine.Offset + position.Column - 1;
+ visualColumn = visualLine.GetVisualColumn(offset - visualLine.FirstDocumentLine.Offset);
+ }
+ return visualLine.GetVisualPosition(visualColumn, yPositionMode);
+ }
+
+ /// <summary>
+ /// Gets the text view position from the specified visual position.
+ /// If the position is within a character, it is rounded to the next character boundary.
+ /// </summary>
+ /// <param name="visualPosition">The position in WPF device-independent pixels relative
+ /// to the top left corner of the document.</param>
+ /// <returns>The logical position, or null if the position is outside the document.</returns>
+ public TextViewPosition? GetPosition(Point visualPosition)
+ {
+ VerifyAccess();
+ if (this.Document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ VisualLine line = GetVisualLineFromVisualTop(visualPosition.Y);
+ if (line == null)
+ return null;
+ int visualColumn = line.GetVisualColumn(visualPosition);
+ int documentOffset = line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
+ return new TextViewPosition(document.GetLocation(documentOffset), visualColumn);
+ }
+
+ /// <summary>
+ /// Gets the text view position from the specified visual position.
+ /// If the position is inside a character, the position in front of the character is returned.
+ /// </summary>
+ /// <param name="visualPosition">The position in WPF device-independent pixels relative
+ /// to the top left corner of the document.</param>
+ /// <returns>The logical position, or null if the position is outside the document.</returns>
+ public TextViewPosition? GetPositionFloor(Point visualPosition)
+ {
+ VerifyAccess();
+ if (this.Document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ VisualLine line = GetVisualLineFromVisualTop(visualPosition.Y);
+ if (line == null)
+ return null;
+ int visualColumn = line.GetVisualColumnFloor(visualPosition);
+ int documentOffset = line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
+ return new TextViewPosition(document.GetLocation(documentOffset), visualColumn);
+ }
+ #endregion
+
+ #region Service Provider
+ readonly ServiceContainer services = new ServiceContainer();
+
+ /// <summary>
+ /// Gets a service container used to associate services with the text view.
+ /// </summary>
+ public ServiceContainer Services {
+ get { return services; }
+ }
+
+ object IServiceProvider.GetService(Type serviceType)
+ {
+ return services.GetService(serviceType);
+ }
+
+ void ConnectToTextView(object obj)
+ {
+ ITextViewConnect c = obj as ITextViewConnect;
+ if (c != null)
+ c.AddToTextView(this);
+ }
+
+ void DisconnectFromTextView(object obj)
+ {
+ ITextViewConnect c = obj as ITextViewConnect;
+ if (c != null)
+ c.RemoveFromTextView(this);
+ }
+ #endregion
+
+ #region MouseHover
+ /// <summary>
+ /// The PreviewMouseHover event.
+ /// </summary>
+ public static readonly RoutedEvent PreviewMouseHoverEvent =
+ EventManager.RegisterRoutedEvent("PreviewMouseHover", RoutingStrategy.Tunnel,
+ typeof(MouseEventHandler), typeof(TextView));
+ /// <summary>
+ /// The MouseHover event.
+ /// </summary>
+ public static readonly RoutedEvent MouseHoverEvent =
+ EventManager.RegisterRoutedEvent("MouseHover", RoutingStrategy.Bubble,
+ typeof(MouseEventHandler), typeof(TextView));
+
+ /// <summary>
+ /// The PreviewMouseHoverStopped event.
+ /// </summary>
+ public static readonly RoutedEvent PreviewMouseHoverStoppedEvent =
+ EventManager.RegisterRoutedEvent("PreviewMouseHoverStopped", RoutingStrategy.Tunnel,
+ typeof(MouseEventHandler), typeof(TextView));
+ /// <summary>
+ /// The MouseHoverStopped event.
+ /// </summary>
+ public static readonly RoutedEvent MouseHoverStoppedEvent =
+ EventManager.RegisterRoutedEvent("MouseHoverStopped", RoutingStrategy.Bubble,
+ typeof(MouseEventHandler), typeof(TextView));
+
+
+ /// <summary>
+ /// Occurs when the mouse has hovered over a fixed location for some time.
+ /// </summary>
+ public event MouseEventHandler PreviewMouseHover {
+ add { AddHandler(PreviewMouseHoverEvent, value); }
+ remove { RemoveHandler(PreviewMouseHoverEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse has hovered over a fixed location for some time.
+ /// </summary>
+ public event MouseEventHandler MouseHover {
+ add { AddHandler(MouseHoverEvent, value); }
+ remove { RemoveHandler(MouseHoverEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse had previously hovered but now started moving again.
+ /// </summary>
+ public event MouseEventHandler PreviewMouseHoverStopped {
+ add { AddHandler(PreviewMouseHoverStoppedEvent, value); }
+ remove { RemoveHandler(PreviewMouseHoverStoppedEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse had previously hovered but now started moving again.
+ /// </summary>
+ public event MouseEventHandler MouseHoverStopped {
+ add { AddHandler(MouseHoverStoppedEvent, value); }
+ remove { RemoveHandler(MouseHoverStoppedEvent, value); }
+ }
+
+ MouseHoverLogic hoverLogic;
+
+ void RaiseHoverEventPair(MouseEventArgs e, RoutedEvent tunnelingEvent, RoutedEvent bubblingEvent)
+ {
+ var mouseDevice = e.MouseDevice;
+ var stylusDevice = e.StylusDevice;
+ int inputTime = Environment.TickCount;
+ var args1 = new MouseEventArgs(mouseDevice, inputTime, stylusDevice) {
+ RoutedEvent = tunnelingEvent,
+ Source = this
+ };
+ RaiseEvent(args1);
+ var args2 = new MouseEventArgs(mouseDevice, inputTime, stylusDevice) {
+ RoutedEvent = bubblingEvent,
+ Source = this,
+ Handled = args1.Handled
+ };
+ RaiseEvent(args2);
+ }
+ #endregion
+
+ /// <summary>
+ /// Collapses lines for the purpose of scrolling. <see cref="DocumentLine"/>s marked as collapsed will be hidden
+ /// and not used to start the generation of a <see cref="VisualLine"/>.
+ /// </summary>
+ /// <remarks>
+ /// This method is meant for <see cref="VisualLineElementGenerator"/>s that cause <see cref="VisualLine"/>s to span
+ /// multiple <see cref="DocumentLine"/>s. Do not call it without providing a corresponding
+ /// <see cref="VisualLineElementGenerator"/>.
+ /// If you want to create collapsible text sections, see <see cref="Folding.FoldingManager"/>.
+ ///
+ /// Note that if you want a VisualLineElement to span from line N to line M, then you need to collapse only the lines
+ /// N+1 to M. Do not collapse line N itself.
+ ///
+ /// When you no longer need the section to be collapsed, call <see cref="CollapsedLineSection.Uncollapse()"/> on the
+ /// <see cref="CollapsedLineSection"/> returned from this method.
+ /// </remarks>
+ public CollapsedLineSection CollapseLines(DocumentLine start, DocumentLine end)
+ {
+ VerifyAccess();
+ if (heightTree == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return heightTree.CollapseText(start, end);
+ }
+
+ /// <summary>
+ /// Gets the height of the document.
+ /// </summary>
+ public double DocumentHeight {
+ get {
+ // return 0 if there is no document = no heightTree
+ return heightTree != null ? heightTree.TotalHeight : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the document line at the specified visual position.
+ /// </summary>
+ public DocumentLine GetDocumentLineByVisualTop(double visualTop)
+ {
+ VerifyAccess();
+ if (heightTree == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return heightTree.GetLineByVisualPosition(visualTop);
+ }
+
+ /// <inheritdoc/>
+ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
+ {
+ base.OnPropertyChanged(e);
+ if (TextFormatterFactory.PropertyChangeAffectsTextFormatter(e.Property)) {
+ // first, create the new text formatter:
+ RecreateTextFormatter();
+ // changing text formatter requires recreating the cached elements
+ RecreateCachedElements();
+ // and we need to re-measure the font metrics:
+ InvalidateDefaultTextMetrics();
+ } else if (e.Property == Control.ForegroundProperty
+ || e.Property == TextView.NonPrintableCharacterBrushProperty
+ || e.Property == TextView.LinkTextBackgroundBrushProperty
+ || e.Property == TextView.LinkTextForegroundBrushProperty)
+ {
+ // changing brushes requires recreating the cached elements
+ RecreateCachedElements();
+ Redraw();
+ }
+ if (e.Property == Control.FontFamilyProperty
+ || e.Property == Control.FontSizeProperty
+ || e.Property == Control.FontStretchProperty
+ || e.Property == Control.FontStyleProperty
+ || e.Property == Control.FontWeightProperty)
+ {
+ // changing font properties requires recreating cached elements
+ RecreateCachedElements();
+ // and we need to re-measure the font metrics:
+ InvalidateDefaultTextMetrics();
+ Redraw();
+ }
+ if (e.Property == ColumnRulerPenProperty) {
+ columnRulerRenderer.SetRuler(this.Options.ColumnRulerPosition, this.ColumnRulerPen);
+ }
+ }
+
+ /// <summary>
+ /// The pen used to draw the column ruler.
+ /// <seealso cref="TextEditorOptions.ShowColumnRuler"/>
+ /// </summary>
+ public static readonly DependencyProperty ColumnRulerPenProperty =
+ DependencyProperty.Register("ColumnRulerBrush", typeof(Pen), typeof(TextView),
+ new FrameworkPropertyMetadata(CreateFrozenPen(Brushes.LightGray)));
+
+ static Pen CreateFrozenPen(SolidColorBrush brush)
+ {
+ Pen pen = new Pen(brush, 1);
+ pen.Freeze();
+ return pen;
+ }
+
+ /// <summary>
+ /// Gets/Sets the pen used to draw the column ruler.
+ /// <seealso cref="TextEditorOptions.ShowColumnRuler"/>
+ /// </summary>
+ public Pen ColumnRulerPen {
+ get { return (Pen)GetValue(ColumnRulerPenProperty); }
+ set { SetValue(ColumnRulerPenProperty, value); }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewCachedElements.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewCachedElements.cs
new file mode 100644
index 000000000..c56e22eaf
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewCachedElements.cs
@@ -0,0 +1,43 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ sealed class TextViewCachedElements : IDisposable
+ {
+ TextFormatter formatter;
+ Dictionary<string, TextLine> nonPrintableCharacterTexts;
+
+ public TextLine GetTextForNonPrintableCharacter(string text, ITextRunConstructionContext context)
+ {
+ if (nonPrintableCharacterTexts == null)
+ nonPrintableCharacterTexts = new Dictionary<string, TextLine>();
+ TextLine textLine;
+ if (!nonPrintableCharacterTexts.TryGetValue(text, out textLine)) {
+ var p = new VisualLineElementTextRunProperties(context.GlobalTextRunProperties);
+ p.SetForegroundBrush(context.TextView.NonPrintableCharacterBrush);
+ if (formatter == null)
+ formatter = TextFormatterFactory.Create(context.TextView);
+ textLine = FormattedTextElement.PrepareText(formatter, text, p);
+ nonPrintableCharacterTexts[text] = textLine;
+ }
+ return textLine;
+ }
+
+ public void Dispose()
+ {
+ if (nonPrintableCharacterTexts != null) {
+ foreach (TextLine line in nonPrintableCharacterTexts.Values)
+ line.Dispose();
+ }
+ if (formatter != null)
+ formatter.Dispose();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewWeakEventManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewWeakEventManager.cs
new file mode 100644
index 000000000..ca1bb5959
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/TextViewWeakEventManager.cs
@@ -0,0 +1,71 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Contains weak event managers for the TextView events.
+ /// </summary>
+ public static class TextViewWeakEventManager
+ {
+ /// <summary>
+ /// Weak event manager for the <see cref="TextView.DocumentChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class DocumentChanged : WeakEventManagerBase<DocumentChanged, TextView>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextView source)
+ {
+ source.DocumentChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextView source)
+ {
+ source.DocumentChanged -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextView.VisualLinesChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class VisualLinesChanged : WeakEventManagerBase<VisualLinesChanged, TextView>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextView source)
+ {
+ source.VisualLinesChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextView source)
+ {
+ source.VisualLinesChanged -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="TextView.ScrollOffsetChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class ScrollOffsetChanged : WeakEventManagerBase<ScrollOffsetChanged, TextView>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(TextView source)
+ {
+ source.ScrollOffsetChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(TextView source)
+ {
+ source.ScrollOffsetChanged -= DeliverEvent;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLine.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLine.cs
new file mode 100644
index 000000000..70727dd07
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLine.cs
@@ -0,0 +1,681 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System.Linq;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Tango.Scripting.Editors.Utils;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Represents a visual line in the document.
+ /// A visual line usually corresponds to one DocumentLine, but it can span multiple lines if
+ /// all but the first are collapsed.
+ /// </summary>
+ public sealed class VisualLine
+ {
+ enum LifetimePhase : byte
+ {
+ Generating,
+ Transforming,
+ Live,
+ Disposed
+ }
+
+ TextView textView;
+ List<VisualLineElement> elements;
+ internal bool hasInlineObjects;
+ LifetimePhase phase;
+
+ /// <summary>
+ /// Gets the document to which this VisualLine belongs.
+ /// </summary>
+ public TextDocument Document { get; private set; }
+
+ /// <summary>
+ /// Gets the first document line displayed by this visual line.
+ /// </summary>
+ public DocumentLine FirstDocumentLine { get; private set; }
+
+ /// <summary>
+ /// Gets the last document line displayed by this visual line.
+ /// </summary>
+ public DocumentLine LastDocumentLine { get; private set; }
+
+ /// <summary>
+ /// Gets a read-only collection of line elements.
+ /// </summary>
+ public ReadOnlyCollection<VisualLineElement> Elements { get; private set; }
+
+ ReadOnlyCollection<TextLine> textLines;
+
+ /// <summary>
+ /// Gets a read-only collection of text lines.
+ /// </summary>
+ public ReadOnlyCollection<TextLine> TextLines {
+ get {
+ if (phase < LifetimePhase.Live)
+ throw new InvalidOperationException();
+ return textLines;
+ }
+ }
+
+ /// <summary>
+ /// Gets the start offset of the VisualLine inside the document.
+ /// This is equivalent to <c>FirstDocumentLine.Offset</c>.
+ /// </summary>
+ public int StartOffset {
+ get {
+ return FirstDocumentLine.Offset;
+ }
+ }
+
+ /// <summary>
+ /// Length in visual line coordinates.
+ /// </summary>
+ public int VisualLength { get; private set; }
+
+ /// <summary>
+ /// Length in visual line coordinates including the end of line marker, if TextEditorOptions.ShowEndOfLine is enabled.
+ /// </summary>
+ public int VisualLengthWithEndOfLineMarker {
+ get {
+ int length = VisualLength;
+ if (textView.Options.ShowEndOfLine && LastDocumentLine.NextLine != null) length++;
+ return length;
+ }
+ }
+
+ /// <summary>
+ /// Gets the height of the visual line in device-independent pixels.
+ /// </summary>
+ public double Height { get; private set; }
+
+ /// <summary>
+ /// Gets the Y position of the line. This is measured in device-independent pixels relative to the start of the document.
+ /// </summary>
+ public double VisualTop { get; internal set; }
+
+ internal VisualLine(TextView textView, DocumentLine firstDocumentLine)
+ {
+ Debug.Assert(textView != null);
+ Debug.Assert(firstDocumentLine != null);
+ this.textView = textView;
+ this.Document = textView.Document;
+ this.FirstDocumentLine = firstDocumentLine;
+ }
+
+ internal void ConstructVisualElements(ITextRunConstructionContext context, VisualLineElementGenerator[] generators)
+ {
+ Debug.Assert(phase == LifetimePhase.Generating);
+ foreach (VisualLineElementGenerator g in generators) {
+ g.StartGeneration(context);
+ }
+ elements = new List<VisualLineElement>();
+ PerformVisualElementConstruction(generators);
+ foreach (VisualLineElementGenerator g in generators) {
+ g.FinishGeneration();
+ }
+
+ var globalTextRunProperties = context.GlobalTextRunProperties;
+ foreach (var element in elements) {
+ element.SetTextRunProperties(new VisualLineElementTextRunProperties(globalTextRunProperties));
+ }
+ this.Elements = elements.AsReadOnly();
+ CalculateOffsets();
+ phase = LifetimePhase.Transforming;
+ }
+
+ void PerformVisualElementConstruction(VisualLineElementGenerator[] generators)
+ {
+ TextDocument document = this.Document;
+ int offset = FirstDocumentLine.Offset;
+ int currentLineEnd = offset + FirstDocumentLine.Length;
+ LastDocumentLine = FirstDocumentLine;
+ int askInterestOffset = 0; // 0 or 1
+ while (offset + askInterestOffset <= currentLineEnd) {
+ int textPieceEndOffset = currentLineEnd;
+ foreach (VisualLineElementGenerator g in generators) {
+ g.cachedInterest = g.GetFirstInterestedOffset(offset + askInterestOffset);
+ if (g.cachedInterest != -1) {
+ if (g.cachedInterest < offset)
+ throw new ArgumentOutOfRangeException(g.GetType().Name + ".GetFirstInterestedOffset",
+ g.cachedInterest,
+ "GetFirstInterestedOffset must not return an offset less than startOffset. Return -1 to signal no interest.");
+ if (g.cachedInterest < textPieceEndOffset)
+ textPieceEndOffset = g.cachedInterest;
+ }
+ }
+ Debug.Assert(textPieceEndOffset >= offset);
+ if (textPieceEndOffset > offset) {
+ int textPieceLength = textPieceEndOffset - offset;
+ elements.Add(new VisualLineText(this, textPieceLength));
+ offset = textPieceEndOffset;
+ }
+ // If no elements constructed / only zero-length elements constructed:
+ // do not asking the generators again for the same location (would cause endless loop)
+ askInterestOffset = 1;
+ foreach (VisualLineElementGenerator g in generators) {
+ if (g.cachedInterest == offset) {
+ VisualLineElement element = g.ConstructElement(offset);
+ if (element != null) {
+ elements.Add(element);
+ if (element.DocumentLength > 0) {
+ // a non-zero-length element was constructed
+ askInterestOffset = 0;
+ offset += element.DocumentLength;
+ if (offset > currentLineEnd) {
+ DocumentLine newEndLine = document.GetLineByOffset(offset);
+ currentLineEnd = newEndLine.Offset + newEndLine.Length;
+ this.LastDocumentLine = newEndLine;
+ //if (currentLineEnd < offset) {
+ // throw new InvalidOperationException(
+ // "The VisualLineElementGenerator " + g.GetType().Name +
+ // " produced an element which ends within the line delimiter");
+ //}
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void CalculateOffsets()
+ {
+ int visualOffset = 0;
+ int textOffset = 0;
+ foreach (VisualLineElement element in elements) {
+ element.VisualColumn = visualOffset;
+ element.RelativeTextOffset = textOffset;
+ visualOffset += element.VisualLength;
+ textOffset += element.DocumentLength;
+ }
+ VisualLength = visualOffset;
+ //Debug.Assert(textOffset == LastDocumentLine.EndOffset - FirstDocumentLine.Offset);
+ }
+
+ internal void RunTransformers(ITextRunConstructionContext context, IVisualLineTransformer[] transformers)
+ {
+ Debug.Assert(phase == LifetimePhase.Transforming);
+ foreach (IVisualLineTransformer transformer in transformers) {
+ transformer.Transform(context, elements);
+ }
+ // For some strange reason, WPF requires that either all or none of the typography properties are set.
+ if (elements.Any(e => e.TextRunProperties.TypographyProperties != null)) {
+ // Fix typographic properties
+ foreach (VisualLineElement element in elements) {
+ if (element.TextRunProperties.TypographyProperties == null) {
+ element.TextRunProperties.SetTypographyProperties(new DefaultTextRunTypographyProperties());
+ }
+ }
+ }
+ phase = LifetimePhase.Live;
+ }
+
+ /// <summary>
+ /// Replaces the single element at <paramref name="elementIndex"/> with the specified elements.
+ /// The replacement operation must preserve the document length, but may change the visual length.
+ /// </summary>
+ /// <remarks>
+ /// This method may only be called by line transformers.
+ /// </remarks>
+ public void ReplaceElement(int elementIndex, params VisualLineElement[] newElements)
+ {
+ ReplaceElement(elementIndex, 1, newElements);
+ }
+
+ /// <summary>
+ /// Replaces <paramref name="count"/> elements starting at <paramref name="elementIndex"/> with the specified elements.
+ /// The replacement operation must preserve the document length, but may change the visual length.
+ /// </summary>
+ /// <remarks>
+ /// This method may only be called by line transformers.
+ /// </remarks>
+ public void ReplaceElement(int elementIndex, int count, params VisualLineElement[] newElements)
+ {
+ if (phase != LifetimePhase.Transforming)
+ throw new InvalidOperationException("This method may only be called by line transformers.");
+ int oldDocumentLength = 0;
+ for (int i = elementIndex; i < elementIndex + count; i++) {
+ oldDocumentLength += elements[i].DocumentLength;
+ }
+ int newDocumentLength = 0;
+ foreach (var newElement in newElements) {
+ newDocumentLength += newElement.DocumentLength;
+ }
+ if (oldDocumentLength != newDocumentLength)
+ throw new InvalidOperationException("Old elements have document length " + oldDocumentLength + ", but new elements have length " + newDocumentLength);
+ elements.RemoveRange(elementIndex, count);
+ elements.InsertRange(elementIndex, newElements);
+ CalculateOffsets();
+ }
+
+ internal void SetTextLines(List<TextLine> textLines)
+ {
+ this.textLines = textLines.AsReadOnly();
+ Height = 0;
+ foreach (TextLine line in textLines)
+ Height += line.Height;
+ }
+
+ /// <summary>
+ /// Gets the visual column from a document offset relative to the first line start.
+ /// </summary>
+ public int GetVisualColumn(int relativeTextOffset)
+ {
+ ThrowUtil.CheckNotNegative(relativeTextOffset, "relativeTextOffset");
+ foreach (VisualLineElement element in elements) {
+ if (element.RelativeTextOffset <= relativeTextOffset
+ && element.RelativeTextOffset + element.DocumentLength >= relativeTextOffset)
+ {
+ return element.GetVisualColumn(relativeTextOffset);
+ }
+ }
+ return VisualLength;
+ }
+
+ /// <summary>
+ /// Gets the document offset (relative to the first line start) from a visual column.
+ /// </summary>
+ public int GetRelativeOffset(int visualColumn)
+ {
+ ThrowUtil.CheckNotNegative(visualColumn, "visualColumn");
+ int documentLength = 0;
+ foreach (VisualLineElement element in elements) {
+ if (element.VisualColumn <= visualColumn
+ && element.VisualColumn + element.VisualLength > visualColumn)
+ {
+ return element.GetRelativeOffset(visualColumn);
+ }
+ documentLength += element.DocumentLength;
+ }
+ return documentLength;
+ }
+
+ /// <summary>
+ /// Gets the text line containing the specified visual column.
+ /// </summary>
+ public TextLine GetTextLine(int visualColumn)
+ {
+ if (visualColumn < 0)
+ throw new ArgumentOutOfRangeException("visualColumn");
+ if (visualColumn >= VisualLengthWithEndOfLineMarker)
+ return TextLines[TextLines.Count - 1];
+ foreach (TextLine line in TextLines) {
+ if (visualColumn < line.Length)
+ return line;
+ else
+ visualColumn -= line.Length;
+ }
+ throw new InvalidOperationException("Shouldn't happen (VisualLength incorrect?)");
+ }
+
+ /// <summary>
+ /// Gets the visual top from the specified text line.
+ /// </summary>
+ /// <returns>Distance in device-independent pixels
+ /// from the top of the document to the top of the specified text line.</returns>
+ public double GetTextLineVisualYPosition(TextLine textLine, VisualYPosition yPositionMode)
+ {
+ if (textLine == null)
+ throw new ArgumentNullException("textLine");
+ double pos = VisualTop;
+ foreach (TextLine tl in TextLines) {
+ if (tl == textLine) {
+ switch (yPositionMode) {
+ case VisualYPosition.LineTop:
+ return pos;
+ case VisualYPosition.LineMiddle:
+ return pos + tl.Height / 2;
+ case VisualYPosition.LineBottom:
+ return pos + tl.Height;
+ case VisualYPosition.TextTop:
+ return pos + tl.Baseline - textView.DefaultBaseline;
+ case VisualYPosition.TextBottom:
+ return pos + tl.Baseline - textView.DefaultBaseline + textView.DefaultLineHeight;
+ case VisualYPosition.TextMiddle:
+ return pos + tl.Baseline - textView.DefaultBaseline + textView.DefaultLineHeight / 2;
+ case VisualYPosition.Baseline:
+ return pos + tl.Baseline;
+ default:
+ throw new ArgumentException("Invalid yPositionMode:" + yPositionMode);
+ }
+ } else {
+ pos += tl.Height;
+ }
+ }
+ throw new ArgumentException("textLine is not a line in this VisualLine");
+ }
+
+ /// <summary>
+ /// Gets the start visual column from the specified text line.
+ /// </summary>
+ public int GetTextLineVisualStartColumn(TextLine textLine)
+ {
+ if (!TextLines.Contains(textLine))
+ throw new ArgumentException("textLine is not a line in this VisualLine");
+ int col = 0;
+ foreach (TextLine tl in TextLines) {
+ if (tl == textLine)
+ break;
+ else
+ col += tl.Length;
+ }
+ return col;
+ }
+
+ /// <summary>
+ /// Gets a TextLine by the visual position.
+ /// </summary>
+ public TextLine GetTextLineByVisualYPosition(double visualTop)
+ {
+ const double epsilon = 0.0001;
+ double pos = this.VisualTop;
+ foreach (TextLine tl in TextLines) {
+ pos += tl.Height;
+ if (visualTop + epsilon < pos)
+ return tl;
+ }
+ return TextLines[TextLines.Count - 1];
+ }
+
+ /// <summary>
+ /// Gets the visual position from the specified visualColumn.
+ /// </summary>
+ /// <returns>Position in device-independent pixels
+ /// relative to the top left of the document.</returns>
+ public Point GetVisualPosition(int visualColumn, VisualYPosition yPositionMode)
+ {
+ TextLine textLine = GetTextLine(visualColumn);
+ double xPos = GetTextLineVisualXPosition(textLine, visualColumn);
+ double yPos = GetTextLineVisualYPosition(textLine, yPositionMode);
+ return new Point(xPos, yPos);
+ }
+
+ /// <summary>
+ /// Gets the distance to the left border of the text area of the specified visual column.
+ /// The visual column must belong to the specified text line.
+ /// </summary>
+ public double GetTextLineVisualXPosition(TextLine textLine, int visualColumn)
+ {
+ if (textLine == null)
+ throw new ArgumentNullException("textLine");
+ double xPos = textLine.GetDistanceFromCharacterHit(
+ new CharacterHit(Math.Min(visualColumn, VisualLengthWithEndOfLineMarker), 0));
+ if (visualColumn > VisualLengthWithEndOfLineMarker) {
+ xPos += (visualColumn - VisualLengthWithEndOfLineMarker) * textView.WideSpaceWidth;
+ }
+ return xPos;
+ }
+
+ /// <summary>
+ /// Gets the visual column from a document position (relative to top left of the document).
+ /// If the user clicks between two visual columns, rounds to the nearest column.
+ /// </summary>
+ public int GetVisualColumn(Point point)
+ {
+ return GetVisualColumn(point, textView.Options.EnableVirtualSpace);
+ }
+
+ /// <summary>
+ /// Gets the visual column from a document position (relative to top left of the document).
+ /// If the user clicks between two visual columns, rounds to the nearest column.
+ /// </summary>
+ public int GetVisualColumn(Point point, bool allowVirtualSpace)
+ {
+ return GetVisualColumn(GetTextLineByVisualYPosition(point.Y), point.X, allowVirtualSpace);
+ }
+
+ /// <summary>
+ /// Gets the visual column from a document position (relative to top left of the document).
+ /// If the user clicks between two visual columns, rounds to the nearest column.
+ /// </summary>
+ public int GetVisualColumn(TextLine textLine, double xPos, bool allowVirtualSpace)
+ {
+ if (xPos > textLine.WidthIncludingTrailingWhitespace) {
+ if (allowVirtualSpace && textLine == TextLines[TextLines.Count - 1]) {
+ int virtualX = (int)Math.Round((xPos - textLine.WidthIncludingTrailingWhitespace) / textView.WideSpaceWidth);
+ return VisualLengthWithEndOfLineMarker + virtualX;
+ }
+ }
+ CharacterHit ch = textLine.GetCharacterHitFromDistance(xPos);
+ return ch.FirstCharacterIndex + ch.TrailingLength;
+ }
+
+ /// <summary>
+ /// Validates the visual column and returns the correct one.
+ /// </summary>
+ public int ValidateVisualColumn(TextViewPosition position, bool allowVirtualSpace)
+ {
+ return ValidateVisualColumn(Document.GetOffset(position.Location), position.VisualColumn, allowVirtualSpace);
+ }
+
+ /// <summary>
+ /// Validates the visual column and returns the correct one.
+ /// </summary>
+ public int ValidateVisualColumn(int offset, int visualColumn, bool allowVirtualSpace)
+ {
+ int firstDocumentLineOffset = this.FirstDocumentLine.Offset;
+ if (visualColumn < 0) {
+ return GetVisualColumn(offset - firstDocumentLineOffset);
+ } else {
+ int offsetFromVisualColumn = GetRelativeOffset(visualColumn);
+ offsetFromVisualColumn += firstDocumentLineOffset;
+ if (offsetFromVisualColumn != offset) {
+ return GetVisualColumn(offset - firstDocumentLineOffset);
+ } else {
+ if (visualColumn > VisualLength && !allowVirtualSpace) {
+ return VisualLength;
+ }
+ }
+ }
+ return visualColumn;
+ }
+
+ /// <summary>
+ /// Gets the visual column from a document position (relative to top left of the document).
+ /// If the user clicks between two visual columns, returns the first of those columns.
+ /// </summary>
+ public int GetVisualColumnFloor(Point point)
+ {
+ return GetVisualColumnFloor(point, textView.Options.EnableVirtualSpace);
+ }
+
+ /// <summary>
+ /// Gets the visual column from a document position (relative to top left of the document).
+ /// If the user clicks between two visual columns, returns the first of those columns.
+ /// </summary>
+ public int GetVisualColumnFloor(Point point, bool allowVirtualSpace)
+ {
+ TextLine textLine = GetTextLineByVisualYPosition(point.Y);
+ if (point.X > textLine.WidthIncludingTrailingWhitespace) {
+ if (allowVirtualSpace && textLine == TextLines[TextLines.Count - 1]) {
+ // clicking virtual space in the last line
+ int virtualX = (int)((point.X - textLine.WidthIncludingTrailingWhitespace) / textView.WideSpaceWidth);
+ return VisualLengthWithEndOfLineMarker + virtualX;
+ } else {
+ // GetCharacterHitFromDistance returns a hit with FirstCharacterIndex=last character in line
+ // and TrailingLength=1 when clicking behind the line, so the floor function needs to handle this case
+ // specially and return the line's end column instead.
+ return GetTextLineVisualStartColumn(textLine) + textLine.Length;
+ }
+ }
+ CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X);
+ return ch.FirstCharacterIndex;
+ }
+
+ /// <summary>
+ /// Gets whether the visual line was disposed.
+ /// </summary>
+ public bool IsDisposed {
+ get { return phase == LifetimePhase.Disposed; }
+ }
+
+ internal void Dispose()
+ {
+ if (phase == LifetimePhase.Disposed)
+ return;
+ Debug.Assert(phase == LifetimePhase.Live);
+ phase = LifetimePhase.Disposed;
+ foreach (TextLine textLine in TextLines) {
+ textLine.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Gets the next possible caret position after visualColumn, or -1 if there is no caret position.
+ /// </summary>
+ public int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode, bool allowVirtualSpace)
+ {
+ if (!HasStopsInVirtualSpace(mode))
+ allowVirtualSpace = false;
+
+ if (elements.Count == 0) {
+ // special handling for empty visual lines:
+ if (allowVirtualSpace) {
+ if (direction == LogicalDirection.Forward)
+ return Math.Max(0, visualColumn + 1);
+ else if (visualColumn > 0)
+ return visualColumn - 1;
+ else
+ return -1;
+ } else {
+ // even though we don't have any elements,
+ // there's a single caret stop at visualColumn 0
+ if (visualColumn < 0 && direction == LogicalDirection.Forward)
+ return 0;
+ else if (visualColumn > 0 && direction == LogicalDirection.Backward)
+ return 0;
+ else
+ return -1;
+ }
+ }
+
+ int i;
+ if (direction == LogicalDirection.Backward) {
+ // Search Backwards:
+ // If the last element doesn't handle line borders, return the line end as caret stop
+
+ if (visualColumn > this.VisualLength && !elements[elements.Count-1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode)) {
+ if (allowVirtualSpace)
+ return visualColumn - 1;
+ else
+ return this.VisualLength;
+ }
+ // skip elements that start after or at visualColumn
+ for (i = elements.Count - 1; i >= 0; i--) {
+ if (elements[i].VisualColumn < visualColumn)
+ break;
+ }
+ // search last element that has a caret stop
+ for (; i >= 0; i--) {
+ int pos = elements[i].GetNextCaretPosition(
+ Math.Min(visualColumn, elements[i].VisualColumn + elements[i].VisualLength + 1),
+ direction, mode);
+ if (pos >= 0)
+ return pos;
+ }
+ // If we've found nothing, and the first element doesn't handle line borders,
+ // return the line start as normal caret stop.
+ if (visualColumn > 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode))
+ return 0;
+ } else {
+ // Search Forwards:
+ // If the first element doesn't handle line borders, return the line start as caret stop
+ if (visualColumn < 0 && !elements[0].HandlesLineBorders && HasImplicitStopAtLineStart(mode))
+ return 0;
+ // skip elements that end before or at visualColumn
+ for (i = 0; i < elements.Count; i++) {
+ if (elements[i].VisualColumn + elements[i].VisualLength > visualColumn)
+ break;
+ }
+ // search first element that has a caret stop
+ for (; i < elements.Count; i++) {
+ int pos = elements[i].GetNextCaretPosition(
+ Math.Max(visualColumn, elements[i].VisualColumn - 1),
+ direction, mode);
+ if (pos >= 0)
+ return pos;
+ }
+ // if we've found nothing, and the last element doesn't handle line borders,
+ // return the line end as caret stop
+ if ((allowVirtualSpace || !elements[elements.Count-1].HandlesLineBorders) && HasImplicitStopAtLineEnd(mode)) {
+ if (visualColumn < this.VisualLength)
+ return this.VisualLength;
+ else if (allowVirtualSpace)
+ return visualColumn + 1;
+ }
+ }
+ // we've found nothing, return -1 and let the caret search continue in the next line
+ return -1;
+ }
+
+ static bool HasStopsInVirtualSpace(CaretPositioningMode mode)
+ {
+ return mode == CaretPositioningMode.Normal;
+ }
+
+ static bool HasImplicitStopAtLineStart(CaretPositioningMode mode)
+ {
+ return mode == CaretPositioningMode.Normal;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "mode",
+ Justification = "make method consistent with HasImplicitStopAtLineStart; might depend on mode in the future")]
+ static bool HasImplicitStopAtLineEnd(CaretPositioningMode mode)
+ {
+ return true;
+ }
+
+ VisualLineDrawingVisual visual;
+
+ internal VisualLineDrawingVisual Render()
+ {
+ Debug.Assert(phase == LifetimePhase.Live);
+ if (visual == null)
+ visual = new VisualLineDrawingVisual(this);
+ return visual;
+ }
+ }
+
+ sealed class VisualLineDrawingVisual : DrawingVisual
+ {
+ public readonly VisualLine VisualLine;
+ public readonly double Height;
+ internal bool IsAdded;
+
+ public VisualLineDrawingVisual(VisualLine visualLine)
+ {
+ this.VisualLine = visualLine;
+ var drawingContext = RenderOpen();
+ double pos = 0;
+ foreach (TextLine textLine in visualLine.TextLines) {
+ textLine.Draw(drawingContext, new Point(0, pos), InvertAxes.None);
+ pos += textLine.Height;
+ }
+ this.Height = pos;
+ drawingContext.Close();
+ }
+
+ protected override GeometryHitTestResult HitTestCore(GeometryHitTestParameters hitTestParameters)
+ {
+ return null;
+ }
+
+ protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
+ {
+ return null;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineConstructionStartEventArgs.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineConstructionStartEventArgs.cs
new file mode 100644
index 000000000..438e516bd
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineConstructionStartEventArgs.cs
@@ -0,0 +1,29 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// EventArgs for the <see cref="TextView.VisualLineConstructionStarting"/> event.
+ /// </summary>
+ public class VisualLineConstructionStartEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets/Sets the first line that is visible in the TextView.
+ /// </summary>
+ public DocumentLine FirstLineInView { get; private set; }
+
+ /// <summary>
+ /// Creates a new VisualLineConstructionStartEventArgs instance.
+ /// </summary>
+ public VisualLineConstructionStartEventArgs(DocumentLine firstLineInView)
+ {
+ if (firstLineInView == null)
+ throw new ArgumentNullException("firstLineInView");
+ this.FirstLineInView = firstLineInView;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElement.cs
new file mode 100644
index 000000000..7858ac038
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElement.cs
@@ -0,0 +1,249 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Represents a visual element in the document.
+ /// </summary>
+ public abstract class VisualLineElement
+ {
+ /// <summary>
+ /// Creates a new VisualLineElement.
+ /// </summary>
+ /// <param name="visualLength">The length of the element in VisualLine coordinates. Must be positive.</param>
+ /// <param name="documentLength">The length of the element in the document. Must be non-negative.</param>
+ protected VisualLineElement(int visualLength, int documentLength)
+ {
+ if (visualLength < 1)
+ throw new ArgumentOutOfRangeException("visualLength", visualLength, "Value must be at least 1");
+ if (documentLength < 0)
+ throw new ArgumentOutOfRangeException("documentLength", documentLength, "Value must be at least 0");
+ this.VisualLength = visualLength;
+ this.DocumentLength = documentLength;
+ }
+
+ /// <summary>
+ /// Gets the length of this element in visual columns.
+ /// </summary>
+ public int VisualLength { get; private set; }
+
+ /// <summary>
+ /// Gets the length of this element in the text document.
+ /// </summary>
+ public int DocumentLength { get; private set; }
+
+ /// <summary>
+ /// Gets the visual column where this element starts.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
+ Justification = "This property holds the start visual column, use GetVisualColumn to get inner visual columns.")]
+ public int VisualColumn { get; internal set; }
+
+ /// <summary>
+ /// Gets the text offset where this element starts, relative to the start text offset of the visual line.
+ /// </summary>
+ public int RelativeTextOffset { get; internal set; }
+
+ /// <summary>
+ /// Gets the text run properties.
+ /// A unique <see cref="VisualLineElementTextRunProperties"/> instance is used for each
+ /// <see cref="VisualLineElement"/>; colorizing code may assume that modifying the
+ /// <see cref="VisualLineElementTextRunProperties"/> will affect only this
+ /// <see cref="VisualLineElement"/>.
+ /// </summary>
+ public VisualLineElementTextRunProperties TextRunProperties { get; private set; }
+
+ /// <summary>
+ /// Gets/sets the brush used for the background of this <see cref="VisualLineElement" />.
+ /// </summary>
+ public Brush BackgroundBrush { get; set; }
+
+ internal void SetTextRunProperties(VisualLineElementTextRunProperties p)
+ {
+ this.TextRunProperties = p;
+ }
+
+ /// <summary>
+ /// Creates the TextRun for this line element.
+ /// </summary>
+ /// <param name="startVisualColumn">
+ /// The visual column from which the run should be constructed.
+ /// Normally the same value as the <see cref="VisualColumn"/> property is used to construct the full run;
+ /// but when word-wrapping is active, partial runs might be created.
+ /// </param>
+ /// <param name="context">
+ /// Context object that contains information relevant for text run creation.
+ /// </param>
+ public abstract TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context);
+
+ /// <summary>
+ /// Retrieves the text span immediately before the visual column.
+ /// </summary>
+ /// <remarks>This method is used for word-wrapping in bidirectional text.</remarks>
+ public virtual TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context)
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Gets if this VisualLineElement can be split.
+ /// </summary>
+ public virtual bool CanSplit {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Splits the element.
+ /// </summary>
+ /// <param name="splitVisualColumn">Position inside this element at which it should be broken</param>
+ /// <param name="elements">The collection of line elements</param>
+ /// <param name="elementIndex">The index at which this element is in the elements list.</param>
+ public virtual void Split(int splitVisualColumn, IList<VisualLineElement> elements, int elementIndex)
+ {
+ throw new NotSupportedException();
+ }
+
+ /// <summary>
+ /// Helper method for splitting this line element into two, correctly updating the
+ /// <see cref="VisualLength"/>, <see cref="DocumentLength"/>, <see cref="VisualColumn"/>
+ /// and <see cref="RelativeTextOffset"/> properties.
+ /// </summary>
+ /// <param name="firstPart">The element before the split position.</param>
+ /// <param name="secondPart">The element after the split position.</param>
+ /// <param name="splitVisualColumn">The split position as visual column.</param>
+ /// <param name="splitRelativeTextOffset">The split position as text offset.</param>
+ protected void SplitHelper(VisualLineElement firstPart, VisualLineElement secondPart, int splitVisualColumn, int splitRelativeTextOffset)
+ {
+ if (firstPart == null)
+ throw new ArgumentNullException("firstPart");
+ if (secondPart == null)
+ throw new ArgumentNullException("secondPart");
+ int relativeSplitVisualColumn = splitVisualColumn - VisualColumn;
+ int relativeSplitRelativeTextOffset = splitRelativeTextOffset - RelativeTextOffset;
+
+ if (relativeSplitVisualColumn <= 0 || relativeSplitVisualColumn >= VisualLength)
+ throw new ArgumentOutOfRangeException("splitVisualColumn", splitVisualColumn, "Value must be between " + (VisualColumn + 1) + " and " + (VisualColumn + VisualLength - 1));
+ if (relativeSplitRelativeTextOffset < 0 || relativeSplitRelativeTextOffset > DocumentLength)
+ throw new ArgumentOutOfRangeException("splitRelativeTextOffset", splitRelativeTextOffset, "Value must be between " + (RelativeTextOffset) + " and " + (RelativeTextOffset + DocumentLength));
+ int oldVisualLength = VisualLength;
+ int oldDocumentLength = DocumentLength;
+ int oldVisualColumn = VisualColumn;
+ int oldRelativeTextOffset = RelativeTextOffset;
+ firstPart.VisualColumn = oldVisualColumn;
+ secondPart.VisualColumn = oldVisualColumn + relativeSplitVisualColumn;
+ firstPart.RelativeTextOffset = oldRelativeTextOffset;
+ secondPart.RelativeTextOffset = oldRelativeTextOffset + relativeSplitRelativeTextOffset;
+ firstPart.VisualLength = relativeSplitVisualColumn;
+ secondPart.VisualLength = oldVisualLength - relativeSplitVisualColumn;
+ firstPart.DocumentLength = relativeSplitRelativeTextOffset;
+ secondPart.DocumentLength = oldDocumentLength - relativeSplitRelativeTextOffset;
+ if (firstPart.TextRunProperties == null)
+ firstPart.TextRunProperties = TextRunProperties.Clone();
+ if (secondPart.TextRunProperties == null)
+ secondPart.TextRunProperties = TextRunProperties.Clone();
+ }
+
+ /// <summary>
+ /// Gets the visual column of a text location inside this element.
+ /// The text offset is given relative to the visual line start.
+ /// </summary>
+ public virtual int GetVisualColumn(int relativeTextOffset)
+ {
+ if (relativeTextOffset >= this.RelativeTextOffset + DocumentLength)
+ return VisualColumn + VisualLength;
+ else
+ return VisualColumn;
+ }
+
+ /// <summary>
+ /// Gets the text offset of a visual column inside this element.
+ /// </summary>
+ /// <returns>A text offset relative to the visual line start.</returns>
+ public virtual int GetRelativeOffset(int visualColumn)
+ {
+ if (visualColumn >= this.VisualColumn + VisualLength)
+ return RelativeTextOffset + DocumentLength;
+ else
+ return RelativeTextOffset;
+ }
+
+ /// <summary>
+ /// Gets the next caret position inside this element.
+ /// </summary>
+ /// <param name="visualColumn">The visual column from which the search should be started.</param>
+ /// <param name="direction">The search direction (forwards or backwards).</param>
+ /// <param name="mode">Whether to stop only at word borders.</param>
+ /// <returns>The visual column of the next caret position, or -1 if there is no next caret position.</returns>
+ /// <remarks>
+ /// In the space between two line elements, it is sufficient that one of them contains a caret position;
+ /// though in many cases, both of them contain one.
+ /// </remarks>
+ public virtual int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
+ {
+ int stop1 = this.VisualColumn;
+ int stop2 = this.VisualColumn + this.VisualLength;
+ if (direction == LogicalDirection.Backward) {
+ if (visualColumn > stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol)
+ return stop2;
+ else if (visualColumn > stop1)
+ return stop1;
+ } else {
+ if (visualColumn < stop1)
+ return stop1;
+ else if (visualColumn < stop2 && mode != CaretPositioningMode.WordStart && mode != CaretPositioningMode.WordStartOrSymbol)
+ return stop2;
+ }
+ return -1;
+ }
+
+ /// <summary>
+ /// Gets whether the specified offset in this element is considered whitespace.
+ /// </summary>
+ public virtual bool IsWhitespace(int visualColumn)
+ {
+ return false;
+ }
+
+ /// <summary>
+ /// Gets whether the <see cref="GetNextCaretPosition"/> implementation handles line borders.
+ /// If this property returns false, the caller of GetNextCaretPosition should handle the line
+ /// borders (i.e. place caret stops at the start and end of the line).
+ /// This property has an effect only for VisualLineElements that are at the start or end of a
+ /// <see cref="VisualLine"/>.
+ /// </summary>
+ public virtual bool HandlesLineBorders {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Queries the cursor over the visual line element.
+ /// </summary>
+ protected internal virtual void OnQueryCursor(QueryCursorEventArgs e)
+ {
+ }
+
+ /// <summary>
+ /// Allows the visual line element to handle a mouse event.
+ /// </summary>
+ protected internal virtual void OnMouseDown(MouseButtonEventArgs e)
+ {
+ }
+
+ /// <summary>
+ /// Allows the visual line element to handle a mouse event.
+ /// </summary>
+ protected internal virtual void OnMouseUp(MouseButtonEventArgs e)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementGenerator.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementGenerator.cs
new file mode 100644
index 000000000..8b2f7ac5b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementGenerator.cs
@@ -0,0 +1,63 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// Abstract base class for generators that produce new visual line elements.
+ /// </summary>
+ public abstract class VisualLineElementGenerator
+ {
+ /// <summary>
+ /// Gets the text run construction context.
+ /// </summary>
+ protected ITextRunConstructionContext CurrentContext { get; private set; }
+
+ /// <summary>
+ /// Initializes the generator for the <see cref="ITextRunConstructionContext"/>
+ /// </summary>
+ public virtual void StartGeneration(ITextRunConstructionContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+ this.CurrentContext = context;
+ }
+
+ /// <summary>
+ /// De-initializes the generator.
+ /// </summary>
+ public virtual void FinishGeneration()
+ {
+ this.CurrentContext = null;
+ }
+
+ /// <summary>
+ /// Should only be used by VisualLine.ConstructVisualElements.
+ /// </summary>
+ internal int cachedInterest;
+
+ /// <summary>
+ /// Gets the first offset >= startOffset where the generator wants to construct an element.
+ /// Return -1 to signal no interest.
+ /// </summary>
+ public abstract int GetFirstInterestedOffset(int startOffset);
+
+ /// <summary>
+ /// Constructs an element at the specified offset.
+ /// May return null if no element should be constructed.
+ /// </summary>
+ /// <remarks>
+ /// Avoid signalling interest and then building no element by returning null - doing so
+ /// causes the generated <see cref="VisualLineText"/> elements to be unnecessarily split
+ /// at the position where you signalled interest.
+ /// </remarks>
+ public abstract VisualLineElement ConstructElement(int offset);
+ }
+
+ internal interface IBuiltinElementGenerator
+ {
+ void FetchOptions(TextEditorOptions options);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementTextRunProperties.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementTextRunProperties.cs
new file mode 100644
index 000000000..a39276c5b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineElementTextRunProperties.cs
@@ -0,0 +1,241 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// <see cref="TextRunProperties"/> implementation that allows changing the properties.
+ /// A <see cref="VisualLineElementTextRunProperties"/> instance usually is assigned to a single
+ /// <see cref="VisualLineElement"/>.
+ /// </summary>
+ public class VisualLineElementTextRunProperties : TextRunProperties, ICloneable
+ {
+ Brush backgroundBrush;
+ BaselineAlignment baselineAlignment;
+ CultureInfo cultureInfo;
+ double fontHintingEmSize;
+ double fontRenderingEmSize;
+ Brush foregroundBrush;
+ Typeface typeface;
+ TextDecorationCollection textDecorations;
+ TextEffectCollection textEffects;
+ TextRunTypographyProperties typographyProperties;
+ NumberSubstitution numberSubstitution;
+
+ /// <summary>
+ /// Creates a new VisualLineElementTextRunProperties instance that copies its values
+ /// from the specified <paramref name="textRunProperties"/>.
+ /// For the <see cref="TextDecorations"/> and <see cref="TextEffects"/> collections, deep copies
+ /// are created if those collections are not frozen.
+ /// </summary>
+ public VisualLineElementTextRunProperties(TextRunProperties textRunProperties)
+ {
+ if (textRunProperties == null)
+ throw new ArgumentNullException("textRunProperties");
+ backgroundBrush = textRunProperties.BackgroundBrush;
+ baselineAlignment = textRunProperties.BaselineAlignment;
+ cultureInfo = textRunProperties.CultureInfo;
+ fontHintingEmSize = textRunProperties.FontHintingEmSize;
+ fontRenderingEmSize = textRunProperties.FontRenderingEmSize;
+ foregroundBrush = textRunProperties.ForegroundBrush;
+ typeface = textRunProperties.Typeface;
+ textDecorations = textRunProperties.TextDecorations;
+ if (textDecorations != null && !textDecorations.IsFrozen) {
+ textDecorations = textDecorations.Clone();
+ }
+ textEffects = textRunProperties.TextEffects;
+ if (textEffects != null && !textEffects.IsFrozen) {
+ textEffects = textEffects.Clone();
+ }
+ typographyProperties = textRunProperties.TypographyProperties;
+ numberSubstitution = textRunProperties.NumberSubstitution;
+ }
+
+ /// <summary>
+ /// Creates a copy of this instance.
+ /// </summary>
+ public virtual VisualLineElementTextRunProperties Clone()
+ {
+ return new VisualLineElementTextRunProperties(this);
+ }
+
+ object ICloneable.Clone()
+ {
+ return Clone();
+ }
+
+ /// <inheritdoc/>
+ public override Brush BackgroundBrush {
+ get { return backgroundBrush; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="BackgroundBrush"/>.
+ /// </summary>
+ public void SetBackgroundBrush(Brush value)
+ {
+ ExtensionMethods.CheckIsFrozen(value);
+ backgroundBrush = value;
+ }
+
+ /// <inheritdoc/>
+ public override BaselineAlignment BaselineAlignment {
+ get { return baselineAlignment; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="BaselineAlignment"/>.
+ /// </summary>
+ public void SetBaselineAlignment(BaselineAlignment value)
+ {
+ baselineAlignment = value;
+ }
+
+ /// <inheritdoc/>
+ public override CultureInfo CultureInfo {
+ get { return cultureInfo; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="CultureInfo"/>.
+ /// </summary>
+ public void SetCultureInfo(CultureInfo value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ cultureInfo = value;
+ }
+
+ /// <inheritdoc/>
+ public override double FontHintingEmSize {
+ get { return fontHintingEmSize; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="FontHintingEmSize"/>.
+ /// </summary>
+ public void SetFontHintingEmSize(double value)
+ {
+ fontHintingEmSize = value;
+ }
+
+ /// <inheritdoc/>
+ public override double FontRenderingEmSize {
+ get { return fontRenderingEmSize; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="FontRenderingEmSize"/>.
+ /// </summary>
+ public void SetFontRenderingEmSize(double value)
+ {
+ fontRenderingEmSize = value;
+ }
+
+ /// <inheritdoc/>
+ public override Brush ForegroundBrush {
+ get { return foregroundBrush; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="ForegroundBrush"/>.
+ /// </summary>
+ public void SetForegroundBrush(Brush value)
+ {
+ ExtensionMethods.CheckIsFrozen(value);
+ foregroundBrush = value;
+ }
+
+ /// <inheritdoc/>
+ public override Typeface Typeface {
+ get { return typeface; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="Typeface"/>.
+ /// </summary>
+ public void SetTypeface(Typeface value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ typeface = value;
+ }
+
+ /// <summary>
+ /// Gets the text decorations. The value may be null, a frozen <see cref="TextDecorationCollection"/>
+ /// or an unfrozen <see cref="TextDecorationCollection"/>.
+ /// If the value is an unfrozen <see cref="TextDecorationCollection"/>, you may assume that the
+ /// collection instance is only used for this <see cref="TextRunProperties"/> instance and it is safe
+ /// to add <see cref="TextDecoration"/>s.
+ /// </summary>
+ public override TextDecorationCollection TextDecorations {
+ get { return textDecorations; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="TextDecorations"/>.
+ /// </summary>
+ public void SetTextDecorations(TextDecorationCollection value)
+ {
+ ExtensionMethods.CheckIsFrozen(value);
+ textDecorations = value;
+ }
+
+ /// <summary>
+ /// Gets the text effects. The value may be null, a frozen <see cref="TextEffectCollection"/>
+ /// or an unfrozen <see cref="TextEffectCollection"/>.
+ /// If the value is an unfrozen <see cref="TextEffectCollection"/>, you may assume that the
+ /// collection instance is only used for this <see cref="TextRunProperties"/> instance and it is safe
+ /// to add <see cref="TextEffect"/>s.
+ /// </summary>
+ public override TextEffectCollection TextEffects {
+ get { return textEffects; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="TextEffects"/>.
+ /// </summary>
+ public void SetTextEffects(TextEffectCollection value)
+ {
+ ExtensionMethods.CheckIsFrozen(value);
+ textEffects = value;
+ }
+
+ /// <summary>
+ /// Gets the typography properties for the text run.
+ /// </summary>
+ public override TextRunTypographyProperties TypographyProperties {
+ get { return typographyProperties; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="TypographyProperties"/>.
+ /// </summary>
+ public void SetTypographyProperties(TextRunTypographyProperties value)
+ {
+ typographyProperties = value;
+ }
+
+ /// <summary>
+ /// Gets the number substitution settings for the text run.
+ /// </summary>
+ public override NumberSubstitution NumberSubstitution {
+ get { return numberSubstitution; }
+ }
+
+ /// <summary>
+ /// Sets the <see cref="NumberSubstitution"/>.
+ /// </summary>
+ public void SetNumberSubstitution(NumberSubstitution value)
+ {
+ numberSubstitution = value;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineLinkText.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineLinkText.cs
new file mode 100644
index 000000000..57c472641
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineLinkText.cs
@@ -0,0 +1,114 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+using System.Windows.Navigation;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// VisualLineElement that represents a piece of text and is a clickable link.
+ /// </summary>
+ public class VisualLineLinkText : VisualLineText
+ {
+ /// <summary>
+ /// Gets/Sets the URL that is navigated to when the link is clicked.
+ /// </summary>
+ public Uri NavigateUri { get; set; }
+
+ /// <summary>
+ /// Gets/Sets the window name where the URL will be opened.
+ /// </summary>
+ public string TargetName { get; set; }
+
+ /// <summary>
+ /// Gets/Sets whether the user needs to press Control to click the link.
+ /// The default value is true.
+ /// </summary>
+ public bool RequireControlModifierForClick { get; set; }
+
+ /// <summary>
+ /// Creates a visual line text element with the specified length.
+ /// It uses the <see cref="ITextRunConstructionContext.VisualLine"/> and its
+ /// <see cref="VisualLineElement.RelativeTextOffset"/> to find the actual text string.
+ /// </summary>
+ public VisualLineLinkText(VisualLine parentVisualLine, int length) : base(parentVisualLine, length)
+ {
+ this.RequireControlModifierForClick = true;
+ }
+
+ /// <inheritdoc/>
+ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
+ {
+ this.TextRunProperties.SetForegroundBrush(context.TextView.LinkTextForegroundBrush);
+ this.TextRunProperties.SetBackgroundBrush(context.TextView.LinkTextBackgroundBrush);
+ this.TextRunProperties.SetTextDecorations(TextDecorations.Underline);
+ return base.CreateTextRun(startVisualColumn, context);
+ }
+
+ /// <summary>
+ /// Gets whether the link is currently clickable.
+ /// </summary>
+ /// <remarks>Returns true when control is pressed; or when
+ /// <see cref="RequireControlModifierForClick"/> is disabled.</remarks>
+ protected bool LinkIsClickable()
+ {
+ if (NavigateUri == null)
+ return false;
+ if (RequireControlModifierForClick)
+ return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
+ else
+ return true;
+ }
+
+ /// <inheritdoc/>
+ protected internal override void OnQueryCursor(QueryCursorEventArgs e)
+ {
+ if (LinkIsClickable()) {
+ e.Handled = true;
+ e.Cursor = Cursors.Hand;
+ }
+ }
+
+ /// <inheritdoc/>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
+ Justification = "I've seen Process.Start throw undocumented exceptions when the mail client / web browser is installed incorrectly")]
+ protected internal override void OnMouseDown(MouseButtonEventArgs e)
+ {
+ if (e.ChangedButton == MouseButton.Left && !e.Handled && LinkIsClickable()) {
+ RequestNavigateEventArgs args = new RequestNavigateEventArgs(this.NavigateUri, this.TargetName);
+ args.RoutedEvent = Hyperlink.RequestNavigateEvent;
+ FrameworkElement element = e.Source as FrameworkElement;
+ if (element != null) {
+ // allow user code to handle the navigation request
+ element.RaiseEvent(args);
+ }
+ if (!args.Handled) {
+ try {
+ Process.Start(this.NavigateUri.ToString());
+ } catch {
+ // ignore all kinds of errors during web browser start
+ }
+ }
+ e.Handled = true;
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override VisualLineText CreateInstance(int length)
+ {
+ return new VisualLineLinkText(ParentVisualLine, length) {
+ NavigateUri = this.NavigateUri,
+ TargetName = this.TargetName,
+ RequireControlModifierForClick = this.RequireControlModifierForClick
+ };
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineText.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineText.cs
new file mode 100644
index 000000000..85b0a9ac4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineText.cs
@@ -0,0 +1,122 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows.Documents;
+using System.Windows.Media.TextFormatting;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// VisualLineElement that represents a piece of text.
+ /// </summary>
+ public class VisualLineText : VisualLineElement
+ {
+ VisualLine parentVisualLine;
+
+ /// <summary>
+ /// Gets the parent visual line.
+ /// </summary>
+ public VisualLine ParentVisualLine {
+ get { return parentVisualLine; }
+ }
+
+ /// <summary>
+ /// Creates a visual line text element with the specified length.
+ /// It uses the <see cref="ITextRunConstructionContext.VisualLine"/> and its
+ /// <see cref="VisualLineElement.RelativeTextOffset"/> to find the actual text string.
+ /// </summary>
+ public VisualLineText(VisualLine parentVisualLine, int length) : base(length, length)
+ {
+ if (parentVisualLine == null)
+ throw new ArgumentNullException("parentVisualLine");
+ this.parentVisualLine = parentVisualLine;
+ }
+
+ /// <summary>
+ /// Override this method to control the type of new VisualLineText instances when
+ /// the visual line is split due to syntax highlighting.
+ /// </summary>
+ protected virtual VisualLineText CreateInstance(int length)
+ {
+ return new VisualLineText(parentVisualLine, length);
+ }
+
+ /// <inheritdoc/>
+ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ int relativeOffset = startVisualColumn - VisualColumn;
+ StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset);
+ return new TextCharacters(text.Text, text.Offset, text.Count, this.TextRunProperties);
+ }
+
+ /// <inheritdoc/>
+ public override bool IsWhitespace(int visualColumn)
+ {
+ int offset = visualColumn - this.VisualColumn + parentVisualLine.FirstDocumentLine.Offset + this.RelativeTextOffset;
+ return char.IsWhiteSpace(parentVisualLine.Document.GetCharAt(offset));
+ }
+
+ /// <inheritdoc/>
+ public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+
+ int relativeOffset = visualColumnLimit - VisualColumn;
+ StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset);
+ CharacterBufferRange range = new CharacterBufferRange(text.Text, text.Offset, text.Count);
+ return new TextSpan<CultureSpecificCharacterBufferRange>(range.Length, new CultureSpecificCharacterBufferRange(this.TextRunProperties.CultureInfo, range));
+ }
+
+ /// <inheritdoc/>
+ public override bool CanSplit {
+ get { return true; }
+ }
+
+ /// <inheritdoc/>
+ public override void Split(int splitVisualColumn, IList<VisualLineElement> elements, int elementIndex)
+ {
+ if (splitVisualColumn <= VisualColumn || splitVisualColumn >= VisualColumn + VisualLength)
+ throw new ArgumentOutOfRangeException("splitVisualColumn", splitVisualColumn, "Value must be between " + (VisualColumn + 1) + " and " + (VisualColumn + VisualLength - 1));
+ if (elements == null)
+ throw new ArgumentNullException("elements");
+ if (elements[elementIndex] != this)
+ throw new ArgumentException("Invalid elementIndex - couldn't find this element at the index");
+ int relativeSplitPos = splitVisualColumn - VisualColumn;
+ VisualLineText splitPart = CreateInstance(DocumentLength - relativeSplitPos);
+ SplitHelper(this, splitPart, splitVisualColumn, relativeSplitPos + RelativeTextOffset);
+ elements.Insert(elementIndex + 1, splitPart);
+ }
+
+ /// <inheritdoc/>
+ public override int GetRelativeOffset(int visualColumn)
+ {
+ return this.RelativeTextOffset + visualColumn - this.VisualColumn;
+ }
+
+ /// <inheritdoc/>
+ public override int GetVisualColumn(int relativeTextOffset)
+ {
+ return this.VisualColumn + relativeTextOffset - this.RelativeTextOffset;
+ }
+
+ /// <inheritdoc/>
+ public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
+ {
+ int textOffset = parentVisualLine.StartOffset + this.RelativeTextOffset;
+ int pos = TextUtilities.GetNextCaretPosition(parentVisualLine.Document, textOffset + visualColumn - this.VisualColumn, direction, mode);
+ if (pos < textOffset || pos > textOffset + this.DocumentLength)
+ return -1;
+ else
+ return this.VisualColumn + pos - textOffset;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextParagraphProperties.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextParagraphProperties.cs
new file mode 100644
index 000000000..9c3aab87f
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextParagraphProperties.cs
@@ -0,0 +1,31 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Media.TextFormatting;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ sealed class VisualLineTextParagraphProperties : TextParagraphProperties
+ {
+ internal TextRunProperties defaultTextRunProperties;
+ internal TextWrapping textWrapping;
+ internal double tabSize;
+ internal double indent;
+ internal bool firstLineInParagraph;
+
+ public override double DefaultIncrementalTab {
+ get { return tabSize; }
+ }
+
+ public override FlowDirection FlowDirection { get { return FlowDirection.LeftToRight; } }
+ public override TextAlignment TextAlignment { get { return TextAlignment.Left; } }
+ public override double LineHeight { get { return double.NaN; } }
+ public override bool FirstLineInParagraph { get { return firstLineInParagraph; } }
+ public override TextRunProperties DefaultTextRunProperties { get { return defaultTextRunProperties; } }
+ public override TextWrapping TextWrapping { get { return textWrapping; } }
+ public override TextMarkerProperties TextMarkerProperties { get { return null; } }
+ public override double Indent { get { return indent; } }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextSource.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextSource.cs
new file mode 100644
index 000000000..3a5167010
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLineTextSource.cs
@@ -0,0 +1,123 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows.Media.TextFormatting;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// WPF TextSource implementation that creates TextRuns for a VisualLine.
+ /// </summary>
+ sealed class VisualLineTextSource : TextSource, ITextRunConstructionContext
+ {
+ public VisualLineTextSource(VisualLine visualLine)
+ {
+ this.VisualLine = visualLine;
+ }
+
+ public VisualLine VisualLine { get; private set; }
+ public TextView TextView { get; set; }
+ public TextDocument Document { get; set; }
+ public TextRunProperties GlobalTextRunProperties { get; set; }
+
+ public override TextRun GetTextRun(int textSourceCharacterIndex)
+ {
+ try {
+ foreach (VisualLineElement element in VisualLine.Elements) {
+ if (textSourceCharacterIndex >= element.VisualColumn
+ && textSourceCharacterIndex < element.VisualColumn + element.VisualLength)
+ {
+ int relativeOffset = textSourceCharacterIndex - element.VisualColumn;
+ TextRun run = element.CreateTextRun(textSourceCharacterIndex, this);
+ if (run == null)
+ throw new ArgumentNullException(element.GetType().Name + ".CreateTextRun");
+ if (run.Length == 0)
+ throw new ArgumentException("The returned TextRun must not have length 0.", element.GetType().Name + ".Length");
+ if (relativeOffset + run.Length > element.VisualLength)
+ throw new ArgumentException("The returned TextRun is too long.", element.GetType().Name + ".CreateTextRun");
+ InlineObjectRun inlineRun = run as InlineObjectRun;
+ if (inlineRun != null) {
+ inlineRun.VisualLine = VisualLine;
+ VisualLine.hasInlineObjects = true;
+ TextView.AddInlineObject(inlineRun);
+ }
+ return run;
+ }
+ }
+ if (TextView.Options.ShowEndOfLine && textSourceCharacterIndex == VisualLine.VisualLength) {
+ return CreateTextRunForNewLine();
+ }
+ return new TextEndOfParagraph(1);
+ } catch (Exception ex) {
+ Debug.WriteLine(ex.ToString());
+ throw;
+ }
+ }
+
+ TextRun CreateTextRunForNewLine()
+ {
+ string newlineText = "";
+ DocumentLine lastDocumentLine = VisualLine.LastDocumentLine;
+ if (lastDocumentLine.DelimiterLength == 2) {
+ newlineText = "¶";
+ } else if (lastDocumentLine.DelimiterLength == 1) {
+ char newlineChar = Document.GetCharAt(lastDocumentLine.Offset + lastDocumentLine.Length);
+ if (newlineChar == '\r')
+ newlineText = "\\r";
+ else if (newlineChar == '\n')
+ newlineText = "\\n";
+ else
+ newlineText = "?";
+ }
+ return new FormattedTextRun(new FormattedTextElement(TextView.cachedElements.GetTextForNonPrintableCharacter(newlineText, this), 0), GlobalTextRunProperties);
+ }
+
+ public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
+ {
+ try {
+ foreach (VisualLineElement element in VisualLine.Elements) {
+ if (textSourceCharacterIndexLimit > element.VisualColumn
+ && textSourceCharacterIndexLimit <= element.VisualColumn + element.VisualLength)
+ {
+ TextSpan<CultureSpecificCharacterBufferRange> span = element.GetPrecedingText(textSourceCharacterIndexLimit, this);
+ if (span == null)
+ break;
+ int relativeOffset = textSourceCharacterIndexLimit - element.VisualColumn;
+ if (span.Length > relativeOffset)
+ throw new ArgumentException("The returned TextSpan is too long.", element.GetType().Name + ".GetPrecedingText");
+ return span;
+ }
+ }
+ CharacterBufferRange empty = CharacterBufferRange.Empty;
+ return new TextSpan<CultureSpecificCharacterBufferRange>(empty.Length, new CultureSpecificCharacterBufferRange(null, empty));
+ } catch (Exception ex) {
+ Debug.WriteLine(ex.ToString());
+ throw;
+ }
+ }
+
+ public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int textSourceCharacterIndex)
+ {
+ throw new NotSupportedException();
+ }
+
+ string cachedString;
+ int cachedStringOffset;
+
+ public StringSegment GetText(int offset, int length)
+ {
+ if (cachedString != null) {
+ if (offset >= cachedStringOffset && offset + length <= cachedStringOffset + cachedString.Length) {
+ return new StringSegment(cachedString, offset - cachedStringOffset, length);
+ }
+ }
+ cachedStringOffset = offset;
+ return new StringSegment(cachedString = this.Document.GetText(offset, length));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLinesInvalidException.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLinesInvalidException.cs
new file mode 100644
index 000000000..bfbae27b0
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualLinesInvalidException.cs
@@ -0,0 +1,44 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// A VisualLinesInvalidException indicates that you accessed the <see cref="TextView.VisualLines"/> property
+ /// of the <see cref="TextView"/> while the visual lines were invalid.
+ /// </summary>
+ [Serializable]
+ public class VisualLinesInvalidException : Exception
+ {
+ /// <summary>
+ /// Creates a new VisualLinesInvalidException instance.
+ /// </summary>
+ public VisualLinesInvalidException() : base()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new VisualLinesInvalidException instance.
+ /// </summary>
+ public VisualLinesInvalidException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new VisualLinesInvalidException instance.
+ /// </summary>
+ public VisualLinesInvalidException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new VisualLinesInvalidException instance.
+ /// </summary>
+ protected VisualLinesInvalidException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualYPosition.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualYPosition.cs
new file mode 100644
index 000000000..352a3eaa3
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Rendering/VisualYPosition.cs
@@ -0,0 +1,46 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Rendering
+{
+ /// <summary>
+ /// An enum that specifies the possible Y positions that can be returned by VisualLine.GetVisualPosition.
+ /// </summary>
+ public enum VisualYPosition
+ {
+ /// <summary>
+ /// Returns the top of the TextLine.
+ /// </summary>
+ LineTop,
+ /// <summary>
+ /// Returns the top of the text.
+ /// If the line contains inline UI elements larger than the text, TextTop may be below LineTop.
+ /// For a line containing regular text (all in the editor's main font), this will be equal to LineTop.
+ /// </summary>
+ TextTop,
+ /// <summary>
+ /// Returns the bottom of the TextLine.
+ /// </summary>
+ LineBottom,
+ /// <summary>
+ /// The middle between LineTop and LineBottom.
+ /// </summary>
+ LineMiddle,
+ /// <summary>
+ /// Returns the bottom of the text.
+ /// If the line contains inline UI elements larger than the text, TextBottom might be above LineBottom.
+ /// For a line containing regular text (all in the editor's main font), this will be equal to LineBottom.
+ /// </summary>
+ TextBottom,
+ /// <summary>
+ /// The middle between TextTop and TextBottom.
+ /// </summary>
+ TextMiddle,
+ /// <summary>
+ /// Returns the baseline of the text.
+ /// </summary>
+ Baseline
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/ScriptEditor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/ScriptEditor.cs
new file mode 100644
index 000000000..a42e743ad
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/ScriptEditor.cs
@@ -0,0 +1,276 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+using Tango.Core.Commands;
+using Tango.Scripting.Editors.CodeCompletion;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Folding;
+
+namespace Tango.Scripting.Editors
+{
+ public class ScriptEditor : TextEditor
+ {
+ private DispatcherTimer foldingUpdateTimer;
+ private FoldingManager foldingManager;
+ private BraceFoldingStrategy foldingStrategy;
+ private CompletionWindow completionWindow;
+ private List<Type> _types = new List<Type>();
+
+ #region Completion
+
+ /// <summary>
+ /// Represents an auto complete item.
+ /// </summary>
+ /// <seealso cref="ICSharpCode.AvalonEdit.CodeCompletion.ICompletionData" />
+ internal class CompletionData : ICompletionData
+ {
+ private String _description;
+
+ /// <summary>
+ /// Gets or sets the icon source.
+ /// </summary>
+ public BitmapSource Source { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CompletionData"/> class.
+ /// </summary>
+ /// <param name="text">The text.</param>
+ /// <param name="description">The description.</param>
+ public CompletionData(string text, String description)
+ {
+ this.Text = text;
+ _description = description;
+ }
+
+ /// <summary>
+ /// Gets the image.
+ /// </summary>
+ public System.Windows.Media.ImageSource Image
+ {
+ get { return Source; }
+ }
+
+ /// <summary>
+ /// Gets the text. This property is used to filter the list of visible elements.
+ /// </summary>
+ public string Text { get; private set; }
+
+ // Use this property if you want to show a fancy UIElement in the drop down list.
+ public object Content
+ {
+ get { return this.Text; }
+ }
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ public object Description
+ {
+ get { return _description; }
+ }
+
+ /// <summary>
+ /// Gets the priority. This property is used in the selection logic. You can use it to prefer selecting those items
+ /// which the user is accessing most frequently.
+ /// </summary>
+ public double Priority { get { return 0; } }
+
+ /// <summary>
+ /// Perform the completion.
+ /// </summary>
+ /// <param name="textArea">The text area on which completion is performed.</param>
+ /// <param name="completionSegment">The text segment that was used by the completion window if
+ /// the user types (segment between CompletionWindow.StartOffset and CompletionWindow.EndOffset).</param>
+ /// <param name="insertionRequestEventArgs">The EventArgs used for the insertion request.
+ /// These can be TextCompositionEventArgs, KeyEventArgs, MouseEventArgs, depending on how
+ /// the insertion was triggered.</param>
+ public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
+ {
+ textArea.Document.Replace(completionSegment, this.Text);
+ }
+
+ /// <summary>
+ /// Returns a <see cref="System.String" /> that represents this instance.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="System.String" /> that represents this instance.
+ /// </returns>
+ public override string ToString()
+ {
+ return Text;
+ }
+ }
+
+ #endregion
+
+ #region Properties
+
+ public bool EnableFolding
+ {
+ get { return (bool)GetValue(EnableFoldingProperty); }
+ set { SetValue(EnableFoldingProperty, value); }
+ }
+ public static readonly DependencyProperty EnableFoldingProperty =
+ DependencyProperty.Register("EnableFolding", typeof(bool), typeof(ScriptEditor), new PropertyMetadata(true, (d, e) => (d as ScriptEditor).OnEnableFoldingChanged()));
+
+ /// <summary>
+ ///
+ /// </summary>
+ public RelayCommand IndentCommand
+ {
+ get { return (RelayCommand)GetValue(IndentCommandProperty); }
+ set { SetValue(IndentCommandProperty, value); }
+ }
+ public static readonly DependencyProperty IndentCommandProperty =
+ DependencyProperty.Register("IndentCommand", typeof(RelayCommand), typeof(ScriptEditor), new PropertyMetadata(null));
+
+
+ #endregion
+
+ public ScriptEditor()
+ {
+ TextArea.IndentationStrategy = new Indentation.CSharp.CSharpIndentationStrategy(Options);
+ foldingStrategy = new BraceFoldingStrategy();
+
+ foldingUpdateTimer = new DispatcherTimer();
+ foldingUpdateTimer.Interval = TimeSpan.FromSeconds(2);
+ foldingUpdateTimer.Tick += FoldingUpdateTimer_Tick;
+ foldingUpdateTimer.Start();
+
+ IndentCommand = new RelayCommand(IndentCode);
+
+ TextArea.TextEntered += TextArea_TextEntered;
+ }
+
+ private void FoldingUpdateTimer_Tick(object sender, EventArgs e)
+ {
+ if (EnableFolding)
+ {
+ if (foldingManager == null)
+ {
+ foldingManager = FoldingManager.Install(TextArea);
+ }
+
+ foldingStrategy.UpdateFoldings(foldingManager, Document);
+ }
+ }
+
+ private void OnEnableFoldingChanged()
+ {
+ if (EnableFolding)
+ {
+ foldingManager = FoldingManager.Install(TextArea);
+ }
+ else
+ {
+ FoldingManager.Uninstall(foldingManager);
+ }
+ }
+
+ public void IndentCode()
+ {
+ Text = Indentation.CSharp.CSharpIndentationHelper.IndentCSharpCode(Text);
+ }
+
+ protected override void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ base.OnPreviewKeyDown(e);
+ HandleKeyCombinations(e);
+ }
+
+ private void HandleKeyCombinations(KeyEventArgs e)
+ {
+ if (Keyboard.IsKeyDown(Key.LeftCtrl) && Keyboard.IsKeyDown(Key.K) && Keyboard.IsKeyDown(Key.D))
+ {
+ try
+ {
+ int index = CaretOffset;
+ Document.BeginUpdate();
+ IndentCode();
+ Document.EndUpdate();
+ e.Handled = true;
+ CaretOffset = index;
+ }
+ catch
+ {
+ Debug.WriteLine("Error indenting code.");
+ }
+ }
+ else if (e.Key == Key.Oem2)
+ {
+ int offset = CaretOffset;
+ var line = Document.GetLineByOffset(offset);
+
+ String text = GetCurrentLineText();
+ if (text.TrimStart('\t', ' ').StartsWith("//"))
+ {
+ Document.BeginUpdate();
+ Document.Replace(line, "/// <summary>\n/// \n/// </summary>");
+ Document.EndUpdate();
+ e.Handled = true;
+ CaretOffset = Document.GetLineByNumber(line.LineNumber + 1).EndOffset;
+ }
+ }
+ }
+
+ public DocumentLine GetCurrentLine()
+ {
+ int offset = CaretOffset;
+ var line = Document.GetLineByOffset(offset);
+ return line;
+ }
+
+ public String GetCurrentLineText()
+ {
+ var text = Document.GetText(GetCurrentLine());
+ return text;
+ }
+
+ public void InsertTypes(List<Type> types)
+ {
+ _types = types;
+ }
+
+ private void TextArea_TextEntered(object sender, TextCompositionEventArgs e)
+ {
+ if (e.Text == ".")
+ {
+ String word = String.Empty;
+ var line = GetCurrentLine();
+ String lineText = Document.GetText(line);
+ Regex r = new Regex(@"(\w+\.)");
+ var match = r.Matches(lineText.Substring(0, line.EndOffset));
+
+ var g = match.OfType<Match>().LastOrDefault();
+
+ if (g != null)
+ {
+ word = g.Value.Replace(".", "");
+
+
+
+ }
+ }
+ }
+
+ static ScriptEditor()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(ScriptEditor), new FrameworkPropertyMetadata(typeof(ScriptEditor)));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.cs
new file mode 100644
index 000000000..dca5bea30
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.cs
@@ -0,0 +1,60 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+
+namespace Tango.Scripting.Editors.Search
+{
+ /// <summary>
+ /// A button that opens a drop-down menu when clicked.
+ /// </summary>
+ class DropDownButton : ButtonBase
+ {
+ public static readonly DependencyProperty DropDownContentProperty
+ = DependencyProperty.Register("DropDownContent", typeof(Popup),
+ typeof(DropDownButton), new FrameworkPropertyMetadata(null));
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
+ protected static readonly DependencyPropertyKey IsDropDownContentOpenPropertyKey
+ = DependencyProperty.RegisterReadOnly("IsDropDownContentOpen", typeof(bool),
+ typeof(DropDownButton), new FrameworkPropertyMetadata(false));
+
+ public static readonly DependencyProperty IsDropDownContentOpenProperty = IsDropDownContentOpenPropertyKey.DependencyProperty;
+
+ static DropDownButton()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(typeof(DropDownButton)));
+ }
+
+ public Popup DropDownContent {
+ get { return (Popup)GetValue(DropDownContentProperty); }
+ set { SetValue(DropDownContentProperty, value); }
+ }
+
+ public bool IsDropDownContentOpen {
+ get { return (bool)GetValue(IsDropDownContentOpenProperty); }
+ protected set { SetValue(IsDropDownContentOpenPropertyKey, value); }
+ }
+
+ protected override void OnClick()
+ {
+ if (DropDownContent != null && !IsDropDownContentOpen) {
+ DropDownContent.Placement = PlacementMode.Bottom;
+ DropDownContent.PlacementTarget = this;
+ DropDownContent.IsOpen = true;
+ DropDownContent.Closed += DropDownContent_Closed;
+ this.IsDropDownContentOpen = true;
+ }
+ }
+
+ void DropDownContent_Closed(object sender, EventArgs e)
+ {
+ ((Popup)sender).Closed -= DropDownContent_Closed;
+ this.IsDropDownContentOpen = false;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.xaml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.xaml
new file mode 100644
index 000000000..8c7649c00
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/DropDownButton.xaml
@@ -0,0 +1,71 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:local="clr-namespace:Tango.Scripting.Editors.Search"
+>
+ <!-- Colors for DropDownButton and SplitButton -->
+ <SolidColorBrush x:Key="{ComponentResourceKey {x:Type local:DropDownButton}, ActiveBorder}" Color="#FF0A246A"/>
+ <SolidColorBrush x:Key="{ComponentResourceKey {x:Type local:DropDownButton}, ActiveBackground}" Color="#FFB6BDD2"/>
+
+ <!-- Style and Template for DropDownButton -->
+ <Style TargetType="{x:Type local:DropDownButton}">
+ <Setter Property="TextElement.Foreground" Value = "{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
+ <Setter Property="Control.Padding" Value="2,2,2,2"/>
+ <Setter Property="Border.BorderThickness" Value="1,1,1,1"/>
+ <Setter Property="Panel.Background" Value="Transparent"/>
+ <Setter Property="Border.BorderBrush" Value="Transparent"/>
+ <Setter Property="FrameworkElement.HorizontalAlignment" Value="Center"/>
+ <Setter Property="FrameworkElement.VerticalAlignment" Value="Center"/>
+ <Setter Property="Control.HorizontalContentAlignment" Value="Center"/>
+ <Setter Property="Control.VerticalContentAlignment" Value="Center"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="local:DropDownButton"
+ xmlns:s="clr-namespace:System;assembly=mscorlib">
+ <Border
+ BorderThickness="{TemplateBinding Border.BorderThickness}"
+ BorderBrush="{TemplateBinding Border.BorderBrush}"
+ Background="{TemplateBinding Panel.Background}"
+ Name="OuterBorder"
+ SnapsToDevicePixels="True"
+ >
+ <StackPanel Orientation="Horizontal">
+ <ContentPresenter
+ Margin="{TemplateBinding Control.Padding}"
+ Content="{TemplateBinding ContentControl.Content}"
+ ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
+ ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
+ HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
+ VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
+ SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
+ <Path Margin="0,2,2,2"
+ Data = "M0,0 L1,0 0.5,1 z"
+ Fill = "{TemplateBinding TextElement.Foreground}"
+ Width = "7"
+ Height = "3.5"
+ Stretch = "Fill"/>
+ </StackPanel>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="UIElement.IsMouseOver" Value="True">
+ <Setter Property="Border.BorderBrush" TargetName="OuterBorder" Value="{DynamicResource {ComponentResourceKey {x:Type local:DropDownButton}, ActiveBorder}}" />
+ <Setter Property="Panel.Background" TargetName="OuterBorder" Value="{DynamicResource {ComponentResourceKey {x:Type local:DropDownButton}, ActiveBackground}}"/>
+ </Trigger>
+ <Trigger Property="UIElement.IsKeyboardFocused" Value="True">
+ <Setter Property="Border.BorderBrush" TargetName="OuterBorder" Value="{DynamicResource {ComponentResourceKey {x:Type local:DropDownButton}, ActiveBorder}}"/>
+ <Setter Property="Panel.Background" TargetName="OuterBorder" Value="{DynamicResource {ComponentResourceKey {x:Type local:DropDownButton}, ActiveBackground}}"/>
+ </Trigger>
+ <Trigger Property="UIElement.IsEnabled" Value="False">
+ <Setter Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
+ </Trigger>
+ <Trigger Property="local:DropDownButton.IsDropDownContentOpen" Value="True">
+ <Setter Property="Border.BorderBrush" TargetName="OuterBorder" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" />
+ <Setter Property="Border.BorderThickness" TargetName="OuterBorder" Value="1,1,1,0" />
+ <Setter Property="Border.Padding" TargetName="OuterBorder" Value="0,0,0,1" />
+ <Setter Property="Panel.Background" TargetName="OuterBorder" Value="Transparent"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/ISearchStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/ISearchStrategy.cs
new file mode 100644
index 000000000..dd4942f33
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/ISearchStrategy.cs
@@ -0,0 +1,88 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Search
+{
+ /// <summary>
+ /// Basic interface for search algorithms.
+ /// </summary>
+ public interface ISearchStrategy : IEquatable<ISearchStrategy>
+ {
+ /// <summary>
+ /// Finds all matches in the given ITextSource and the given range.
+ /// </summary>
+ /// <remarks>
+ /// This method must be implemented thread-safe.
+ /// All segments in the result must be within the given range, and they must be returned in order
+ /// (e.g. if two results are returned, EndOffset of first result must be less than or equal StartOffset of second result).
+ /// </remarks>
+ IEnumerable<ISearchResult> FindAll(ITextSource document, int offset, int length);
+
+ /// <summary>
+ /// Finds the next match in the given ITextSource and the given range.
+ /// </summary>
+ /// <remarks>This method must be implemented thread-safe.</remarks>
+ ISearchResult FindNext(ITextSource document, int offset, int length);
+ }
+
+ /// <summary>
+ /// Represents a search result.
+ /// </summary>
+ public interface ISearchResult : ISegment
+ {
+ /// <summary>
+ /// Replaces parts of the replacement string with parts from the match. (e.g. $1)
+ /// </summary>
+ string ReplaceWith(string replacement);
+ }
+
+ /// <summary>
+ /// Defines supported search modes.
+ /// </summary>
+ public enum SearchMode
+ {
+ /// <summary>
+ /// Standard search
+ /// </summary>
+ Normal,
+ /// <summary>
+ /// RegEx search
+ /// </summary>
+ RegEx,
+ /// <summary>
+ /// Wildcard search
+ /// </summary>
+ Wildcard
+ }
+
+ /// <inheritdoc/>
+ public class SearchPatternException : Exception, ISerializable
+ {
+ /// <inheritdoc/>
+ public SearchPatternException()
+ {
+ }
+
+ /// <inheritdoc/>
+ public SearchPatternException(string message) : base(message)
+ {
+ }
+
+ /// <inheritdoc/>
+ public SearchPatternException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ // This constructor is needed for serialization.
+ /// <inheritdoc/>
+ protected SearchPatternException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/Localization.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/Localization.cs
new file mode 100644
index 000000000..cbe8c9bb0
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/Localization.cs
@@ -0,0 +1,64 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+
+namespace Tango.Scripting.Editors.Search
+{
+ /// <summary>
+ /// Holds default texts for buttons and labels in the SearchPanel. Override properties to add other languages.
+ /// </summary>
+ public class Localization
+ {
+ /// <summary>
+ /// Default: 'Match case'
+ /// </summary>
+ public virtual string MatchCaseText {
+ get { return "Match case"; }
+ }
+
+ /// <summary>
+ /// Default: 'Match whole words'
+ /// </summary>
+ public virtual string MatchWholeWordsText {
+ get { return "Match whole words"; }
+ }
+
+
+ /// <summary>
+ /// Default: 'Use regular expressions'
+ /// </summary>
+ public virtual string UseRegexText {
+ get { return "Use regular expressions"; }
+ }
+
+ /// <summary>
+ /// Default: 'Find next (F3)'
+ /// </summary>
+ public virtual string FindNextText {
+ get { return "Find next (F3)"; }
+ }
+
+ /// <summary>
+ /// Default: 'Find previous (Shift+F3)'
+ /// </summary>
+ public virtual string FindPreviousText {
+ get { return "Find previous (Shift+F3)"; }
+ }
+
+ /// <summary>
+ /// Default: 'Error: '
+ /// </summary>
+ public virtual string ErrorText {
+ get { return "Error: "; }
+ }
+
+ /// <summary>
+ /// Default: 'No matches found!'
+ /// </summary>
+ public virtual string NoMatchesFoundText {
+ get { return "No matches found!"; }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/RegexSearchStrategy.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/RegexSearchStrategy.cs
new file mode 100644
index 000000000..e7ca7f8ce
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/RegexSearchStrategy.cs
@@ -0,0 +1,70 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows.Documents;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Search
+{
+ class RegexSearchStrategy : ISearchStrategy
+ {
+ readonly Regex searchPattern;
+ readonly bool matchWholeWords;
+
+ public RegexSearchStrategy(Regex searchPattern, bool matchWholeWords)
+ {
+ if (searchPattern == null)
+ throw new ArgumentNullException("searchPattern");
+ this.searchPattern = searchPattern;
+ this.matchWholeWords = matchWholeWords;
+ }
+
+ public IEnumerable<ISearchResult> FindAll(ITextSource document, int offset, int length)
+ {
+ int endOffset = offset + length;
+ foreach (Match result in searchPattern.Matches(document.Text)) {
+ int resultEndOffset = result.Length + result.Index;
+ if (offset > result.Index || endOffset < resultEndOffset)
+ continue;
+ if (matchWholeWords && (!IsWordBorder(document, result.Index) || !IsWordBorder(document, resultEndOffset)))
+ continue;
+ yield return new SearchResult { StartOffset = result.Index, Length = result.Length, Data = result };
+ }
+ }
+
+ static bool IsWordBorder(ITextSource document, int offset)
+ {
+ return TextUtilities.GetNextCaretPosition(document, offset - 1, LogicalDirection.Forward, CaretPositioningMode.WordBorder) == offset;
+ }
+
+ public ISearchResult FindNext(ITextSource document, int offset, int length)
+ {
+ return FindAll(document, offset, length).FirstOrDefault();
+ }
+
+ public bool Equals(ISearchStrategy other)
+ {
+ var strategy = other as RegexSearchStrategy;
+ return strategy != null &&
+ strategy.searchPattern.ToString() == searchPattern.ToString() &&
+ strategy.searchPattern.Options == searchPattern.Options &&
+ strategy.searchPattern.RightToLeft == searchPattern.RightToLeft;
+ }
+ }
+
+ class SearchResult : TextSegment, ISearchResult
+ {
+ public Match Data { get; set; }
+
+ public string ReplaceWith(string replacement)
+ {
+ return Data.Result(replacement);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchCommands.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchCommands.cs
new file mode 100644
index 000000000..4bb102e21
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchCommands.cs
@@ -0,0 +1,105 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Search
+{
+ /// <summary>
+ /// Search commands for AvalonEdit.
+ /// </summary>
+ public static class SearchCommands
+ {
+ /// <summary>
+ /// Finds the next occurrence in the file.
+ /// </summary>
+ public static readonly RoutedCommand FindNext = new RoutedCommand(
+ "FindNext", typeof(SearchPanel),
+ new InputGestureCollection { new KeyGesture(Key.F3) }
+ );
+
+ /// <summary>
+ /// Finds the previous occurrence in the file.
+ /// </summary>
+ public static readonly RoutedCommand FindPrevious = new RoutedCommand(
+ "FindPrevious", typeof(SearchPanel),
+ new InputGestureCollection { new KeyGesture(Key.F3, ModifierKeys.Shift) }
+ );
+
+ /// <summary>
+ /// Closes the SearchPanel.
+ /// </summary>
+ public static readonly RoutedCommand CloseSearchPanel = new RoutedCommand(
+ "CloseSearchPanel", typeof(SearchPanel),
+ new InputGestureCollection { new KeyGesture(Key.Escape) }
+ );
+ }
+
+ /// <summary>
+ /// TextAreaInputHandler that registers all search-related commands.
+ /// </summary>
+ public class SearchInputHandler : TextAreaInputHandler
+ {
+ /// <summary>
+ /// Creates a new SearchInputHandler and registers the search-related commands.
+ /// </summary>
+ public SearchInputHandler(TextArea textArea)
+ : base(textArea)
+ {
+ RegisterCommands(this.CommandBindings);
+ panel = new SearchPanel();
+ panel.Attach(TextArea);
+ }
+
+ void RegisterCommands(ICollection<CommandBinding> commandBindings)
+ {
+ commandBindings.Add(new CommandBinding(ApplicationCommands.Find, ExecuteFind));
+ commandBindings.Add(new CommandBinding(SearchCommands.FindNext, ExecuteFindNext));
+ commandBindings.Add(new CommandBinding(SearchCommands.FindPrevious, ExecuteFindPrevious));
+ commandBindings.Add(new CommandBinding(SearchCommands.CloseSearchPanel, ExecuteCloseSearchPanel));
+ }
+
+ SearchPanel panel;
+
+ void ExecuteFind(object sender, ExecutedRoutedEventArgs e)
+ {
+ panel.Open();
+ if (!(TextArea.Selection.IsEmpty || TextArea.Selection.IsMultiline))
+ panel.SearchPattern = TextArea.Selection.GetText();
+ Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate { panel.Reactivate(); });
+ }
+
+ void ExecuteFindNext(object sender, ExecutedRoutedEventArgs e)
+ {
+ panel.FindNext();
+ }
+
+ void ExecuteFindPrevious(object sender, ExecutedRoutedEventArgs e)
+ {
+ panel.FindPrevious();
+ }
+
+ void ExecuteCloseSearchPanel(object sender, ExecutedRoutedEventArgs e)
+ {
+ panel.Close();
+ }
+
+ /// <summary>
+ /// Fired when SearchOptions are modified inside the SearchPanel.
+ /// </summary>
+ public event EventHandler<SearchOptionsChangedEventArgs> SearchOptionsChanged {
+ add { panel.SearchOptionsChanged += value; }
+ remove { panel.SearchOptionsChanged -= value; }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.cs
new file mode 100644
index 000000000..b4af99f54
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.cs
@@ -0,0 +1,467 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Threading;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Folding;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Search
+{
+ /// <summary>
+ /// Provides search functionality for AvalonEdit. It is displayed in the top-right corner of the TextArea.
+ /// </summary>
+ public class SearchPanel : Control
+ {
+ TextArea textArea;
+ TextDocument currentDocument;
+ SearchResultBackgroundRenderer renderer;
+ TextBox searchTextBox;
+ SearchPanelAdorner adorner;
+
+ #region DependencyProperties
+ /// <summary>
+ /// Dependency property for <see cref="UseRegex"/>.
+ /// </summary>
+ public static readonly DependencyProperty UseRegexProperty =
+ DependencyProperty.Register("UseRegex", typeof(bool), typeof(SearchPanel),
+ new FrameworkPropertyMetadata(false, SearchPatternChangedCallback));
+
+ /// <summary>
+ /// Gets/sets whether the search pattern should be interpreted as regular expression.
+ /// </summary>
+ public bool UseRegex {
+ get { return (bool)GetValue(UseRegexProperty); }
+ set { SetValue(UseRegexProperty, value); }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="MatchCase"/>.
+ /// </summary>
+ public static readonly DependencyProperty MatchCaseProperty =
+ DependencyProperty.Register("MatchCase", typeof(bool), typeof(SearchPanel),
+ new FrameworkPropertyMetadata(false, SearchPatternChangedCallback));
+
+ /// <summary>
+ /// Gets/sets whether the search pattern should be interpreted case-sensitive.
+ /// </summary>
+ public bool MatchCase {
+ get { return (bool)GetValue(MatchCaseProperty); }
+ set { SetValue(MatchCaseProperty, value); }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="WholeWords"/>.
+ /// </summary>
+ public static readonly DependencyProperty WholeWordsProperty =
+ DependencyProperty.Register("WholeWords", typeof(bool), typeof(SearchPanel),
+ new FrameworkPropertyMetadata(false, SearchPatternChangedCallback));
+
+ /// <summary>
+ /// Gets/sets whether the search pattern should only match whole words.
+ /// </summary>
+ public bool WholeWords {
+ get { return (bool)GetValue(WholeWordsProperty); }
+ set { SetValue(WholeWordsProperty, value); }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="SearchPattern"/>.
+ /// </summary>
+ public static readonly DependencyProperty SearchPatternProperty =
+ DependencyProperty.Register("SearchPattern", typeof(string), typeof(SearchPanel),
+ new FrameworkPropertyMetadata("", SearchPatternChangedCallback));
+
+ /// <summary>
+ /// Gets/sets the search pattern.
+ /// </summary>
+ public string SearchPattern {
+ get { return (string)GetValue(SearchPatternProperty); }
+ set { SetValue(SearchPatternProperty, value); }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="MarkerBrush"/>.
+ /// </summary>
+ public static readonly DependencyProperty MarkerBrushProperty =
+ DependencyProperty.Register("MarkerBrush", typeof(Brush), typeof(SearchPanel),
+ new FrameworkPropertyMetadata(Brushes.LightGreen, MarkerBrushChangedCallback));
+
+ /// <summary>
+ /// Gets/sets the Brush used for marking search results in the TextView.
+ /// </summary>
+ public Brush MarkerBrush {
+ get { return (Brush)GetValue(MarkerBrushProperty); }
+ set { SetValue(MarkerBrushProperty, value); }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="Localization"/>.
+ /// </summary>
+ public static readonly DependencyProperty LocalizationProperty =
+ DependencyProperty.Register("Localization", typeof(Localization), typeof(SearchPanel),
+ new FrameworkPropertyMetadata(new Localization()));
+
+ /// <summary>
+ /// Gets/sets the localization for the SearchPanel.
+ /// </summary>
+ public Localization Localization {
+ get { return (Localization)GetValue(LocalizationProperty); }
+ set { SetValue(LocalizationProperty, value); }
+ }
+ #endregion
+
+ static void MarkerBrushChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ SearchPanel panel = d as SearchPanel;
+ if (panel != null) {
+ panel.renderer.MarkerBrush = (Brush)e.NewValue;
+ }
+ }
+
+ static SearchPanel()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchPanel), new FrameworkPropertyMetadata(typeof(SearchPanel)));
+ }
+
+ ISearchStrategy strategy;
+
+ static void SearchPatternChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ SearchPanel panel = d as SearchPanel;
+ if (panel != null) {
+ panel.ValidateSearchText();
+ panel.UpdateSearch();
+ }
+ }
+
+ void UpdateSearch()
+ {
+ // only reset as long as there are results
+ // if no results are found, the "no matches found" message should not flicker.
+ // if results are found by the next run, the message will be hidden inside DoSearch ...
+ if (renderer.CurrentResults.Any())
+ messageView.IsOpen = false;
+ strategy = SearchStrategyFactory.Create(SearchPattern ?? "", !MatchCase, WholeWords, UseRegex ? SearchMode.RegEx : SearchMode.Normal);
+ OnSearchOptionsChanged(new SearchOptionsChangedEventArgs(SearchPattern, MatchCase, UseRegex, WholeWords));
+ DoSearch(true);
+ }
+
+ /// <summary>
+ /// Creates a new SearchPanel.
+ /// </summary>
+ public SearchPanel()
+ {
+ }
+
+ /// <summary>
+ /// Attaches this SearchPanel to a TextArea instance.
+ /// </summary>
+ public void Attach(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+ adorner = new SearchPanelAdorner(textArea, this);
+ DataContext = this;
+
+ renderer = new SearchResultBackgroundRenderer();
+ currentDocument = textArea.Document;
+ if (currentDocument != null)
+ currentDocument.TextChanged += textArea_Document_TextChanged;
+ textArea.DocumentChanged += textArea_DocumentChanged;
+ KeyDown += SearchLayerKeyDown;
+
+ this.CommandBindings.Add(new CommandBinding(SearchCommands.FindNext, (sender, e) => FindNext()));
+ this.CommandBindings.Add(new CommandBinding(SearchCommands.FindPrevious, (sender, e) => FindPrevious()));
+ this.CommandBindings.Add(new CommandBinding(SearchCommands.CloseSearchPanel, (sender, e) => Close()));
+ IsClosed = true;
+ }
+
+ void textArea_DocumentChanged(object sender, EventArgs e)
+ {
+ if (currentDocument != null)
+ currentDocument.TextChanged -= textArea_Document_TextChanged;
+ currentDocument = textArea.Document;
+ if (currentDocument != null) {
+ currentDocument.TextChanged += textArea_Document_TextChanged;
+ DoSearch(false);
+ }
+ }
+
+ void textArea_Document_TextChanged(object sender, EventArgs e)
+ {
+ DoSearch(false);
+ }
+
+ /// <inheritdoc/>
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ searchTextBox = Template.FindName("PART_searchTextBox", this) as TextBox;
+ }
+
+ void ValidateSearchText()
+ {
+ if (searchTextBox == null)
+ return;
+ var be = searchTextBox.GetBindingExpression(TextBox.TextProperty);
+ try {
+ Validation.ClearInvalid(be);
+ UpdateSearch();
+ } catch (SearchPatternException ex) {
+ var ve = new ValidationError(be.ParentBinding.ValidationRules[0], be, ex.Message, ex);
+ Validation.MarkInvalid(be, ve);
+ }
+ }
+
+ /// <summary>
+ /// Reactivates the SearchPanel by setting the focus on the search box and selecting all text.
+ /// </summary>
+ public void Reactivate()
+ {
+ if (searchTextBox == null)
+ return;
+ searchTextBox.Focus();
+ searchTextBox.SelectAll();
+ }
+
+ /// <summary>
+ /// Moves to the next occurrence in the file.
+ /// </summary>
+ public void FindNext()
+ {
+ SearchResult result = renderer.CurrentResults.FindFirstSegmentWithStartAfter(textArea.Caret.Offset + 1);
+ if (result == null)
+ result = renderer.CurrentResults.FirstSegment;
+ if (result != null) {
+ SelectResult(result);
+ }
+ }
+
+ /// <summary>
+ /// Moves to the previous occurrence in the file.
+ /// </summary>
+ public void FindPrevious()
+ {
+ SearchResult result = renderer.CurrentResults.FindFirstSegmentWithStartAfter(textArea.Caret.Offset);
+ if (result != null)
+ result = renderer.CurrentResults.GetPreviousSegment(result);
+ if (result == null)
+ result = renderer.CurrentResults.LastSegment;
+ if (result != null) {
+ SelectResult(result);
+ }
+ }
+
+ ToolTip messageView = new ToolTip { Placement = PlacementMode.Bottom, StaysOpen = false };
+
+ void DoSearch(bool changeSelection)
+ {
+ if (IsClosed)
+ return;
+ renderer.CurrentResults.Clear();
+
+ if (!string.IsNullOrEmpty(SearchPattern)) {
+ int offset = textArea.Caret.Offset;
+ if (changeSelection) {
+ textArea.ClearSelection();
+ }
+ // We cast from ISearchResult to SearchResult; this is safe because we always use the built-in strategy
+ foreach (SearchResult result in strategy.FindAll(textArea.Document, 0, textArea.Document.TextLength)) {
+ if (changeSelection && result.StartOffset >= offset) {
+ SelectResult(result);
+ changeSelection = false;
+ }
+ renderer.CurrentResults.Add(result);
+ }
+ if (!renderer.CurrentResults.Any()) {
+ messageView.IsOpen = true;
+ messageView.Content = Localization.NoMatchesFoundText;
+ messageView.PlacementTarget = searchTextBox;
+ } else
+ messageView.IsOpen = false;
+ }
+ textArea.TextView.InvalidateLayer(KnownLayer.Selection);
+ }
+
+ void SelectResult(SearchResult result)
+ {
+ textArea.Caret.Offset = result.StartOffset;
+ textArea.Selection = Selection.Create(textArea, result.StartOffset, result.EndOffset);
+ textArea.Caret.BringCaretToView();
+ // show caret even if the editor does not have the Keyboard Focus
+ textArea.Caret.Show();
+ }
+
+ void SearchLayerKeyDown(object sender, KeyEventArgs e)
+ {
+ switch (e.Key) {
+ case Key.Enter:
+ e.Handled = true;
+ messageView.IsOpen = false;
+ if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
+ FindPrevious();
+ else
+ FindNext();
+ if (searchTextBox != null) {
+ var error = Validation.GetErrors(searchTextBox).FirstOrDefault();
+ if (error != null) {
+ messageView.Content = Localization.ErrorText + " " + error.ErrorContent;
+ messageView.PlacementTarget = searchTextBox;
+ messageView.IsOpen = true;
+ }
+ }
+ break;
+ case Key.Escape:
+ e.Handled = true;
+ Close();
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the Panel is already closed.
+ /// </summary>
+ public bool IsClosed { get; private set; }
+
+ /// <summary>
+ /// Closes the SearchPanel.
+ /// </summary>
+ public void Close()
+ {
+ bool hasFocus = this.IsKeyboardFocusWithin;
+
+ var layer = AdornerLayer.GetAdornerLayer(textArea);
+ if (layer != null)
+ layer.Remove(adorner);
+ messageView.IsOpen = false;
+ textArea.TextView.BackgroundRenderers.Remove(renderer);
+ if (hasFocus)
+ textArea.Focus();
+ IsClosed = true;
+
+ // Clear existing search results so that the segments don't have to be maintained
+ renderer.CurrentResults.Clear();
+ }
+
+ /// <summary>
+ /// Closes the SearchPanel and removes it.
+ /// </summary>
+ public void CloseAndRemove()
+ {
+ Close();
+ textArea.DocumentChanged -= textArea_DocumentChanged;
+ if (currentDocument != null)
+ currentDocument.TextChanged -= textArea_Document_TextChanged;
+ }
+
+ /// <summary>
+ /// Opens the an existing search panel.
+ /// </summary>
+ public void Open()
+ {
+ if (!IsClosed) return;
+ var layer = AdornerLayer.GetAdornerLayer(textArea);
+ if (layer != null)
+ layer.Add(adorner);
+ textArea.TextView.BackgroundRenderers.Add(renderer);
+ IsClosed = false;
+ DoSearch(false);
+ }
+
+ /// <summary>
+ /// Fired when SearchOptions are changed inside the SearchPanel.
+ /// </summary>
+ public event EventHandler<SearchOptionsChangedEventArgs> SearchOptionsChanged;
+
+ /// <summary>
+ /// Raises the <see cref="SearchPanel.SearchOptionsChanged" /> event.
+ /// </summary>
+ protected virtual void OnSearchOptionsChanged(SearchOptionsChangedEventArgs e)
+ {
+ if (SearchOptionsChanged != null) {
+ SearchOptionsChanged(this, e);
+ }
+ }
+ }
+
+ /// <summary>
+ /// EventArgs for <see cref="SearchPanel.SearchOptionsChanged"/> event.
+ /// </summary>
+ public class SearchOptionsChangedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets the search pattern.
+ /// </summary>
+ public string SearchPattern { get; private set; }
+
+ /// <summary>
+ /// Gets whether the search pattern should be interpreted case-sensitive.
+ /// </summary>
+ public bool MatchCase { get; private set; }
+
+ /// <summary>
+ /// Gets whether the search pattern should be interpreted as regular expression.
+ /// </summary>
+ public bool UseRegex { get; private set; }
+
+ /// <summary>
+ /// Gets whether the search pattern should only match whole words.
+ /// </summary>
+ public bool WholeWords { get; private set; }
+
+ /// <summary>
+ /// Creates a new SearchOptionsChangedEventArgs instance.
+ /// </summary>
+ public SearchOptionsChangedEventArgs(string searchPattern, bool matchCase, bool useRegex, bool wholeWords)
+ {
+ this.SearchPattern = searchPattern;
+ this.MatchCase = matchCase;
+ this.UseRegex = useRegex;
+ this.WholeWords = wholeWords;
+ }
+ }
+
+ class SearchPanelAdorner : Adorner
+ {
+ SearchPanel panel;
+
+ public SearchPanelAdorner(TextArea textArea, SearchPanel panel)
+ : base(textArea)
+ {
+ this.panel = panel;
+ AddVisualChild(panel);
+ }
+
+ protected override int VisualChildrenCount {
+ get { return 1; }
+ }
+
+ protected override Visual GetVisualChild(int index)
+ {
+ if (index != 0)
+ throw new ArgumentOutOfRangeException();
+ return panel;
+ }
+
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ panel.Arrange(new Rect(new Point(0, 0), finalSize));
+ return new Size(panel.ActualWidth, panel.ActualHeight);
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.xaml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.xaml
new file mode 100644
index 000000000..b787e9a4f
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchPanel.xaml
@@ -0,0 +1,47 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:search="clr-namespace:Tango.Scripting.Editors.Search">
+ <Style TargetType="search:SearchPanel">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type search:SearchPanel}">
+ <Border Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" BorderThickness="1" HorizontalAlignment="Right" VerticalAlignment="Top" Cursor="Arrow">
+ <StackPanel Orientation="Horizontal">
+ <TextBox Name="PART_searchTextBox" Focusable="True" Width="150" Height="24" Margin="3,3,3,0">
+ <TextBox.Text>
+ <Binding Path="SearchPattern" RelativeSource="{RelativeSource TemplatedParent}" UpdateSourceTrigger="PropertyChanged">
+ <Binding.ValidationRules>
+ <ExceptionValidationRule />
+ </Binding.ValidationRules>
+ </Binding>
+ </TextBox.Text>
+ </TextBox>
+ <search:DropDownButton Height="24">
+ <search:DropDownButton.DropDownContent>
+ <Popup StaysOpen="False">
+ <Border Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" BorderThickness="1">
+ <StackPanel Orientation="Vertical">
+ <CheckBox IsChecked="{Binding MatchCase, RelativeSource={RelativeSource TemplatedParent}}" Content="{Binding Localization.MatchCaseText, RelativeSource={RelativeSource TemplatedParent}}" Margin="3" />
+ <CheckBox IsChecked="{Binding WholeWords, RelativeSource={RelativeSource TemplatedParent}}" Content="{Binding Localization.MatchWholeWordsText, RelativeSource={RelativeSource TemplatedParent}}" Margin="3" />
+ <CheckBox IsChecked="{Binding UseRegex, RelativeSource={RelativeSource TemplatedParent}}" Content="{Binding Localization.UseRegexText, RelativeSource={RelativeSource TemplatedParent}}" Margin="3" />
+ </StackPanel>
+ </Border>
+ </Popup>
+ </search:DropDownButton.DropDownContent>
+ </search:DropDownButton>
+ <Button Margin="3" Height="24" Width="24" Command="search:SearchCommands.FindPrevious" ToolTip="{Binding Localization.FindPreviousText, RelativeSource={RelativeSource TemplatedParent}}">
+ <Image Width="16" Height="16" Stretch="Fill" Source="prev.png" />
+ </Button>
+ <Button Margin="3" Height="24" Width="24" Command="search:SearchCommands.FindNext" ToolTip="{Binding Localization.FindNextText, RelativeSource={RelativeSource TemplatedParent}}">
+ <Image Width="16" Height="16" Stretch="Fill" Source="next.png" />
+ </Button>
+ <Button Height="16" Width="16" HorizontalAlignment="Right" VerticalAlignment="Top" Command="search:SearchCommands.CloseSearchPanel"
+ VerticalContentAlignment="Center" HorizontalContentAlignment="Center">
+ <Path Data="M 0,0 L 8,8 M 8,0 L 0,8" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" />
+ </Button>
+ </StackPanel>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchResultBackgroundRenderer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchResultBackgroundRenderer.cs
new file mode 100644
index 000000000..35b9c0696
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchResultBackgroundRenderer.cs
@@ -0,0 +1,78 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Search
+{
+ class SearchResultBackgroundRenderer : IBackgroundRenderer
+ {
+ TextSegmentCollection<SearchResult> currentResults = new TextSegmentCollection<SearchResult>();
+
+ public TextSegmentCollection<SearchResult> CurrentResults {
+ get { return currentResults; }
+ }
+
+ public KnownLayer Layer {
+ get {
+ // draw behind selection
+ return KnownLayer.Selection;
+ }
+ }
+
+ public SearchResultBackgroundRenderer()
+ {
+ markerBrush = Brushes.LightGreen;
+ markerPen = new Pen(markerBrush, 1);
+ }
+
+ Brush markerBrush;
+ Pen markerPen;
+
+ public Brush MarkerBrush {
+ get { return markerBrush; }
+ set {
+ this.markerBrush = value;
+ markerPen = new Pen(markerBrush, 1);
+ }
+ }
+
+ public void Draw(TextView textView, DrawingContext drawingContext)
+ {
+ if (textView == null)
+ throw new ArgumentNullException("textView");
+ if (drawingContext == null)
+ throw new ArgumentNullException("drawingContext");
+
+ if (currentResults == null || !textView.VisualLinesValid)
+ return;
+
+ var visualLines = textView.VisualLines;
+ if (visualLines.Count == 0)
+ return;
+
+ int viewStart = visualLines.First().FirstDocumentLine.Offset;
+ int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
+
+ foreach (SearchResult result in currentResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
+ BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
+ geoBuilder.AlignToMiddleOfPixels = true;
+ geoBuilder.CornerRadius = 3;
+ geoBuilder.AddSegment(textView, result);
+ Geometry geometry = geoBuilder.CreateGeometry();
+ if (geometry != null) {
+ drawingContext.DrawGeometry(markerBrush, markerPen, geometry);
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchStrategyFactory.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchStrategyFactory.cs
new file mode 100644
index 000000000..01633c62a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/SearchStrategyFactory.cs
@@ -0,0 +1,68 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows.Controls;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Search
+{
+ /// <summary>
+ /// Provides factory methods for ISearchStrategies.
+ /// </summary>
+ public class SearchStrategyFactory
+ {
+ /// <summary>
+ /// Creates a default ISearchStrategy with the given parameters.
+ /// </summary>
+ public static ISearchStrategy Create(string searchPattern, bool ignoreCase, bool matchWholeWords, SearchMode mode)
+ {
+ if (searchPattern == null)
+ throw new ArgumentNullException("searchPattern");
+ RegexOptions options = RegexOptions.Compiled | RegexOptions.Multiline;
+ if (ignoreCase)
+ options |= RegexOptions.IgnoreCase;
+
+ switch (mode) {
+ case SearchMode.Normal:
+ searchPattern = Regex.Escape(searchPattern);
+ break;
+ case SearchMode.Wildcard:
+ searchPattern = ConvertWildcardsToRegex(searchPattern);
+ break;
+ }
+ try {
+ Regex pattern = new Regex(searchPattern, options);
+ return new RegexSearchStrategy(pattern, matchWholeWords);
+ } catch (ArgumentException ex) {
+ throw new SearchPatternException(ex.Message, ex);
+ }
+ }
+
+ static string ConvertWildcardsToRegex(string searchPattern)
+ {
+ if (string.IsNullOrEmpty(searchPattern))
+ return "";
+
+ StringBuilder builder = new StringBuilder();
+
+ foreach (char ch in searchPattern) {
+ switch (ch) {
+ case '?':
+ builder.Append(".");
+ break;
+ case '*':
+ builder.Append(".*");
+ break;
+ default:
+ builder.Append(Regex.Escape(ch.ToString()));
+ break;
+ }
+ }
+
+ return builder.ToString();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/next.png b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/next.png
new file mode 100644
index 000000000..250330790
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/next.png
Binary files differ
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/prev.png b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/prev.png
new file mode 100644
index 000000000..f0454a237
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Search/prev.png
Binary files differ
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/IActiveElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/IActiveElement.cs
new file mode 100644
index 000000000..07275f273
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/IActiveElement.cs
@@ -0,0 +1,35 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Media;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Represents an active element that allows the snippet to stay interactive after insertion.
+ /// </summary>
+ public interface IActiveElement
+ {
+ /// <summary>
+ /// Called when the all snippet elements have been inserted.
+ /// </summary>
+ void OnInsertionCompleted();
+
+ /// <summary>
+ /// Called when the interactive mode is deactivated.
+ /// </summary>
+ void Deactivate(SnippetEventArgs e);
+
+ /// <summary>
+ /// Gets whether this element is editable (the user will be able to select it with Tab).
+ /// </summary>
+ bool IsEditable { get; }
+
+ /// <summary>
+ /// Gets the segment associated with this element. May be null.
+ /// </summary>
+ ISegment Segment { get; }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/InsertionContext.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/InsertionContext.cs
new file mode 100644
index 000000000..d7de8311d
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/InsertionContext.cs
@@ -0,0 +1,269 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Input;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Represents the context of a snippet insertion.
+ /// </summary>
+ public class InsertionContext : IWeakEventListener
+ {
+ enum Status
+ {
+ Insertion,
+ RaisingInsertionCompleted,
+ Interactive,
+ RaisingDeactivated,
+ Deactivated
+ }
+
+ Status currentStatus = Status.Insertion;
+
+ /// <summary>
+ /// Creates a new InsertionContext instance.
+ /// </summary>
+ public InsertionContext(TextArea textArea, int insertionPosition)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.TextArea = textArea;
+ this.Document = textArea.Document;
+ this.SelectedText = textArea.Selection.GetText();
+ this.InsertionPosition = insertionPosition;
+ this.startPosition = insertionPosition;
+
+ DocumentLine startLine = this.Document.GetLineByOffset(insertionPosition);
+ ISegment indentation = TextUtilities.GetWhitespaceAfter(this.Document, startLine.Offset);
+ this.Indentation = Document.GetText(indentation.Offset, Math.Min(indentation.EndOffset, insertionPosition) - indentation.Offset);
+ this.Tab = textArea.Options.IndentationString;
+
+ this.LineTerminator = TextUtilities.GetNewLineFromDocument(this.Document, startLine.LineNumber);
+ }
+
+ /// <summary>
+ /// Gets the text area.
+ /// </summary>
+ public TextArea TextArea { get; private set; }
+
+ /// <summary>
+ /// Gets the text document.
+ /// </summary>
+ public TextDocument Document { get; private set; }
+
+ /// <summary>
+ /// Gets the text that was selected before the insertion of the snippet.
+ /// </summary>
+ public string SelectedText { get; private set; }
+
+ /// <summary>
+ /// Gets the indentation at the insertion position.
+ /// </summary>
+ public string Indentation { get; private set; }
+
+ /// <summary>
+ /// Gets the indentation string for a single indentation level.
+ /// </summary>
+ public string Tab { get; private set; }
+
+ /// <summary>
+ /// Gets the line terminator at the insertion position.
+ /// </summary>
+ public string LineTerminator { get; private set; }
+
+ /// <summary>
+ /// Gets/Sets the insertion position.
+ /// </summary>
+ public int InsertionPosition { get; set; }
+
+ readonly int startPosition;
+ AnchorSegment wholeSnippetAnchor;
+ bool deactivateIfSnippetEmpty;
+
+ /// <summary>
+ /// Gets the start position of the snippet insertion.
+ /// </summary>
+ public int StartPosition {
+ get {
+ if (wholeSnippetAnchor != null)
+ return wholeSnippetAnchor.Offset;
+ else
+ return startPosition;
+ }
+ }
+
+ /// <summary>
+ /// Inserts text at the insertion position and advances the insertion position.
+ /// This method will add the current indentation to every line in <paramref name="text"/> and will
+ /// replace newlines with the expected newline for the document.
+ /// </summary>
+ public void InsertText(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ if (currentStatus != Status.Insertion)
+ throw new InvalidOperationException();
+
+ text = text.Replace("\t", this.Tab);
+
+ using (this.Document.RunUpdate()) {
+ int textOffset = 0;
+ SimpleSegment segment;
+ while ((segment = NewLineFinder.NextNewLine(text, textOffset)) != SimpleSegment.Invalid) {
+ string insertString = text.Substring(textOffset, segment.Offset - textOffset)
+ + this.LineTerminator + this.Indentation;
+ this.Document.Insert(InsertionPosition, insertString);
+ this.InsertionPosition += insertString.Length;
+ textOffset = segment.EndOffset;
+ }
+ string remainingInsertString = text.Substring(textOffset);
+ this.Document.Insert(InsertionPosition, remainingInsertString);
+ this.InsertionPosition += remainingInsertString.Length;
+ }
+ }
+
+ Dictionary<SnippetElement, IActiveElement> elementMap = new Dictionary<SnippetElement, IActiveElement>();
+ List<IActiveElement> registeredElements = new List<IActiveElement>();
+
+ /// <summary>
+ /// Registers an active element. Elements should be registered during insertion and will be called back
+ /// when insertion has completed.
+ /// </summary>
+ /// <param name="owner">The snippet element that created the active element.</param>
+ /// <param name="element">The active element.</param>
+ public void RegisterActiveElement(SnippetElement owner, IActiveElement element)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+ if (element == null)
+ throw new ArgumentNullException("element");
+ if (currentStatus != Status.Insertion)
+ throw new InvalidOperationException();
+ elementMap.Add(owner, element);
+ registeredElements.Add(element);
+ }
+
+ /// <summary>
+ /// Returns the active element belonging to the specified snippet element, or null if no such active element is found.
+ /// </summary>
+ public IActiveElement GetActiveElement(SnippetElement owner)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+ IActiveElement element;
+ if (elementMap.TryGetValue(owner, out element))
+ return element;
+ else
+ return null;
+ }
+
+ /// <summary>
+ /// Gets the list of active elements.
+ /// </summary>
+ public IEnumerable<IActiveElement> ActiveElements {
+ get { return registeredElements; }
+ }
+
+ /// <summary>
+ /// Calls the <see cref="IActiveElement.OnInsertionCompleted"/> method on all registered active elements
+ /// and raises the <see cref="InsertionCompleted"/> event.
+ /// </summary>
+ /// <param name="e">The EventArgs to use</param>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate",
+ Justification="There is an event and this method is raising it.")]
+ public void RaiseInsertionCompleted(EventArgs e)
+ {
+ if (currentStatus != Status.Insertion)
+ throw new InvalidOperationException();
+ if (e == null)
+ e = EventArgs.Empty;
+
+ currentStatus = Status.RaisingInsertionCompleted;
+ int endPosition = this.InsertionPosition;
+ this.wholeSnippetAnchor = new AnchorSegment(Document, startPosition, endPosition - startPosition);
+ TextDocumentWeakEventManager.UpdateFinished.AddListener(Document, this);
+ deactivateIfSnippetEmpty = (endPosition != startPosition);
+
+ foreach (IActiveElement element in registeredElements) {
+ element.OnInsertionCompleted();
+ }
+ if (InsertionCompleted != null)
+ InsertionCompleted(this, e);
+ currentStatus = Status.Interactive;
+ if (registeredElements.Count == 0) {
+ // deactivate immediately if there are no interactive elements
+ Deactivate(new SnippetEventArgs(DeactivateReason.NoActiveElements));
+ } else {
+ myInputHandler = new SnippetInputHandler(this);
+ // disable existing snippet input handlers - there can be only 1 active snippet
+ foreach (TextAreaStackedInputHandler h in TextArea.StackedInputHandlers) {
+ if (h is SnippetInputHandler)
+ TextArea.PopStackedInputHandler(h);
+ }
+ TextArea.PushStackedInputHandler(myInputHandler);
+ }
+ }
+
+ SnippetInputHandler myInputHandler;
+
+ /// <summary>
+ /// Occurs when the all snippet elements have been inserted.
+ /// </summary>
+ public event EventHandler InsertionCompleted;
+
+ /// <summary>
+ /// Calls the <see cref="IActiveElement.Deactivate"/> method on all registered active elements.
+ /// </summary>
+ /// <param name="e">The EventArgs to use</param>
+ public void Deactivate(SnippetEventArgs e)
+ {
+ if (currentStatus == Status.Deactivated || currentStatus == Status.RaisingDeactivated)
+ return;
+ if (currentStatus != Status.Interactive)
+ throw new InvalidOperationException("Cannot call Deactivate() until RaiseInsertionCompleted() has finished.");
+ if (e == null)
+ e = new SnippetEventArgs(DeactivateReason.Unknown);
+
+ TextDocumentWeakEventManager.UpdateFinished.RemoveListener(Document, this);
+ currentStatus = Status.RaisingDeactivated;
+ TextArea.PopStackedInputHandler(myInputHandler);
+ foreach (IActiveElement element in registeredElements) {
+ element.Deactivate(e);
+ }
+ if (Deactivated != null)
+ Deactivated(this, e);
+ currentStatus = Status.Deactivated;
+ }
+
+ /// <summary>
+ /// Occurs when the interactive mode is deactivated.
+ /// </summary>
+ public event EventHandler<SnippetEventArgs> Deactivated;
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ return ReceiveWeakEvent(managerType, sender, e);
+ }
+
+ /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
+ protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(TextDocumentWeakEventManager.UpdateFinished)) {
+ // Deactivate if snippet is deleted. This is necessary for correctly leaving interactive
+ // mode if Undo is pressed after a snippet insertion.
+ if (wholeSnippetAnchor.Length == 0 && deactivateIfSnippetEmpty)
+ Deactivate(new SnippetEventArgs(DeactivateReason.Deleted));
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/Snippet.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/Snippet.cs
new file mode 100644
index 000000000..0006261fb
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/Snippet.cs
@@ -0,0 +1,47 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows.Documents;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// A code snippet that can be inserted into the text editor.
+ /// </summary>
+ [Serializable]
+ public class Snippet : SnippetContainerElement
+ {
+ /// <summary>
+ /// Inserts the snippet into the text area.
+ /// </summary>
+ public void Insert(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+
+ ISegment selection = textArea.Selection.SurroundingSegment;
+ int insertionPosition = textArea.Caret.Offset;
+
+ if (selection != null) // if something is selected
+ // use selection start instead of caret position,
+ // because caret could be at end of selection or anywhere inside.
+ // Removal of the selected text causes the caret position to be invalid.
+ insertionPosition = selection.Offset + TextUtilities.GetWhitespaceAfter(textArea.Document, selection.Offset).Length;
+
+ InsertionContext context = new InsertionContext(textArea, insertionPosition);
+
+ using (context.Document.RunUpdate()) {
+ if (selection != null)
+ textArea.Document.Remove(insertionPosition, selection.EndOffset - insertionPosition);
+ Insert(context);
+ context.RaiseInsertionCompleted(EventArgs.Empty);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetAnchorElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetAnchorElement.cs
new file mode 100644
index 000000000..cfce03b6a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetAnchorElement.cs
@@ -0,0 +1,97 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Creates a named anchor that can be accessed by other SnippetElements.
+ /// </summary>
+ public sealed class SnippetAnchorElement : SnippetElement
+ {
+ /// <summary>
+ /// Gets or sets the name of the anchor.
+ /// </summary>
+ public string Name { get; private set; }
+
+ /// <summary>
+ /// Creates a SnippetAnchorElement with the supplied name.
+ /// </summary>
+ public SnippetAnchorElement(string name)
+ {
+ this.Name = name;
+ }
+
+ /// <inheritdoc />
+ public override void Insert(InsertionContext context)
+ {
+ TextAnchor start = context.Document.CreateAnchor(context.InsertionPosition);
+ start.MovementType = AnchorMovementType.BeforeInsertion;
+ start.SurviveDeletion = true;
+ AnchorSegment segment = new AnchorSegment(start, start);
+ context.RegisterActiveElement(this, new AnchorElement(segment, Name, context));
+ }
+ }
+
+ /// <summary>
+ /// AnchorElement created by SnippetAnchorElement.
+ /// </summary>
+ public sealed class AnchorElement : IActiveElement
+ {
+ /// <inheritdoc />
+ public bool IsEditable {
+ get { return false; }
+ }
+
+ AnchorSegment segment;
+ InsertionContext context;
+
+ /// <inheritdoc />
+ public ISegment Segment {
+ get { return segment; }
+ }
+
+ /// <summary>
+ /// Creates a new AnchorElement.
+ /// </summary>
+ public AnchorElement(AnchorSegment segment, string name, InsertionContext context)
+ {
+ this.segment = segment;
+ this.context = context;
+ this.Name = name;
+ }
+
+ /// <summary>
+ /// Gets or sets the text at the anchor.
+ /// </summary>
+ public string Text {
+ get { return context.Document.GetText(segment); }
+ set {
+ int offset = segment.Offset;
+ int length = segment.Length;
+ context.Document.Replace(offset, length, value);
+ if (length == 0) {
+ // replacing an empty anchor segment with text won't enlarge it, so we have to recreate it
+ segment = new AnchorSegment(context.Document, offset, value.Length);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the name of the anchor.
+ /// </summary>
+ public string Name { get; private set; }
+
+ /// <inheritdoc />
+ public void OnInsertionCompleted()
+ {
+ }
+
+ /// <inheritdoc />
+ public void Deactivate(SnippetEventArgs e)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetBoundElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetBoundElement.cs
new file mode 100644
index 000000000..92f6d4fd4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetBoundElement.cs
@@ -0,0 +1,122 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Documents;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// An element that binds to a <see cref="SnippetReplaceableTextElement"/> and displays the same text.
+ /// </summary>
+ [Serializable]
+ public class SnippetBoundElement : SnippetElement
+ {
+ SnippetReplaceableTextElement targetElement;
+
+ /// <summary>
+ /// Gets/Sets the target element.
+ /// </summary>
+ public SnippetReplaceableTextElement TargetElement {
+ get { return targetElement; }
+ set { targetElement = value; }
+ }
+
+ /// <summary>
+ /// Converts the text before copying it.
+ /// </summary>
+ public virtual string ConvertText(string input)
+ {
+ return input;
+ }
+
+ /// <inheritdoc/>
+ public override void Insert(InsertionContext context)
+ {
+ if (targetElement != null) {
+ TextAnchor start = context.Document.CreateAnchor(context.InsertionPosition);
+ start.MovementType = AnchorMovementType.BeforeInsertion;
+ start.SurviveDeletion = true;
+ string inputText = targetElement.Text;
+ if (inputText != null) {
+ context.InsertText(ConvertText(inputText));
+ }
+ TextAnchor end = context.Document.CreateAnchor(context.InsertionPosition);
+ end.MovementType = AnchorMovementType.BeforeInsertion;
+ end.SurviveDeletion = true;
+ AnchorSegment segment = new AnchorSegment(start, end);
+ context.RegisterActiveElement(this, new BoundActiveElement(context, targetElement, this, segment));
+ }
+ }
+
+ /// <inheritdoc/>
+ public override Inline ToTextRun()
+ {
+ if (targetElement != null) {
+ string inputText = targetElement.Text;
+ if (inputText != null) {
+ return new Italic(new Run(ConvertText(inputText)));
+ }
+ }
+ return base.ToTextRun();
+ }
+ }
+
+ sealed class BoundActiveElement : IActiveElement
+ {
+ InsertionContext context;
+ SnippetReplaceableTextElement targetSnippetElement;
+ SnippetBoundElement boundElement;
+ internal IReplaceableActiveElement targetElement;
+ AnchorSegment segment;
+
+ public BoundActiveElement(InsertionContext context, SnippetReplaceableTextElement targetSnippetElement, SnippetBoundElement boundElement, AnchorSegment segment)
+ {
+ this.context = context;
+ this.targetSnippetElement = targetSnippetElement;
+ this.boundElement = boundElement;
+ this.segment = segment;
+ }
+
+ public void OnInsertionCompleted()
+ {
+ targetElement = context.GetActiveElement(targetSnippetElement) as IReplaceableActiveElement;
+ if (targetElement != null) {
+ targetElement.TextChanged += targetElement_TextChanged;
+ }
+ }
+
+ void targetElement_TextChanged(object sender, EventArgs e)
+ {
+ // Don't copy text if the segments overlap (we would get an endless loop).
+ // This can happen if the user deletes the text between the replaceable element and the bound element.
+ if (segment.GetOverlap(targetElement.Segment) == SimpleSegment.Invalid) {
+ int offset = segment.Offset;
+ int length = segment.Length;
+ string text = boundElement.ConvertText(targetElement.Text);
+ if (length != text.Length || text != context.Document.GetText(offset, length)) {
+ // Call replace only if we're actually changing something.
+ // Without this check, we would generate an empty undo group when the user pressed undo.
+ context.Document.Replace(offset, length, text);
+ if (length == 0) {
+ // replacing an empty anchor segment with text won't enlarge it, so we have to recreate it
+ segment = new AnchorSegment(context.Document, offset, text.Length);
+ }
+ }
+ }
+ }
+
+ public void Deactivate(SnippetEventArgs e)
+ {
+ }
+
+ public bool IsEditable {
+ get { return false; }
+ }
+
+ public ISegment Segment {
+ get { return segment; }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetCaretElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetCaretElement.cs
new file mode 100644
index 000000000..1b33abf66
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetCaretElement.cs
@@ -0,0 +1,58 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Runtime.Serialization;
+using System.Windows.Input;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Sets the caret position after interactive mode has finished.
+ /// </summary>
+ [Serializable]
+ public class SnippetCaretElement : SnippetElement
+ {
+ [OptionalField]
+ bool setCaretOnlyIfTextIsSelected;
+
+ /// <summary>
+ /// Creates a new SnippetCaretElement.
+ /// </summary>
+ public SnippetCaretElement()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new SnippetCaretElement.
+ /// </summary>
+ /// <param name="setCaretOnlyIfTextIsSelected">
+ /// If set to true, the caret is set only when some text was selected.
+ /// This is useful when both SnippetCaretElement and SnippetSelectionElement are used in the same snippet.
+ /// </param>
+ public SnippetCaretElement(bool setCaretOnlyIfTextIsSelected)
+ {
+ this.setCaretOnlyIfTextIsSelected = setCaretOnlyIfTextIsSelected;
+ }
+
+ /// <inheritdoc/>
+ public override void Insert(InsertionContext context)
+ {
+ if (!setCaretOnlyIfTextIsSelected || !string.IsNullOrEmpty(context.SelectedText))
+ SetCaret(context);
+ }
+
+ internal static void SetCaret(InsertionContext context)
+ {
+ TextAnchor pos = context.Document.CreateAnchor(context.InsertionPosition);
+ pos.MovementType = AnchorMovementType.BeforeInsertion;
+ pos.SurviveDeletion = true;
+ context.Deactivated += (sender, e) => {
+ if (e.Reason == DeactivateReason.ReturnPressed || e.Reason == DeactivateReason.NoActiveElements) {
+ context.TextArea.Caret.Offset = pos.Offset;
+ }
+ };
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetContainerElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetContainerElement.cs
new file mode 100644
index 000000000..066f1a870
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetContainerElement.cs
@@ -0,0 +1,49 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Documents;
+
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// A snippet element that has sub-elements.
+ /// </summary>
+ [Serializable]
+ public class SnippetContainerElement : SnippetElement
+ {
+ NullSafeCollection<SnippetElement> elements = new NullSafeCollection<SnippetElement>();
+
+ /// <summary>
+ /// Gets the list of child elements.
+ /// </summary>
+ public IList<SnippetElement> Elements {
+ get { return elements; }
+ }
+
+ /// <inheritdoc/>
+ public override void Insert(InsertionContext context)
+ {
+ foreach (SnippetElement e in this.Elements) {
+ e.Insert(context);
+ }
+ }
+
+ /// <inheritdoc/>
+ public override Inline ToTextRun()
+ {
+ Span span = new Span();
+ foreach (SnippetElement e in this.Elements) {
+ Inline r = e.ToTextRun();
+ if (r != null)
+ span.Inlines.Add(r);
+ }
+ return span;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetElement.cs
new file mode 100644
index 000000000..2e2c4556e
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetElement.cs
@@ -0,0 +1,32 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Windows.Documents;
+
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// An element inside a snippet.
+ /// </summary>
+ [Serializable]
+ public abstract class SnippetElement
+ {
+ /// <summary>
+ /// Performs insertion of the snippet.
+ /// </summary>
+ public abstract void Insert(InsertionContext context);
+
+ /// <summary>
+ /// Converts the snippet to text, with replaceable fields in italic.
+ /// </summary>
+ public virtual Inline ToTextRun()
+ {
+ return null;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetEventArgs.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetEventArgs.cs
new file mode 100644
index 000000000..cfc993485
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetEventArgs.cs
@@ -0,0 +1,57 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Provides information about the event that occured during use of snippets.
+ /// </summary>
+ public class SnippetEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets the reason for deactivation.
+ /// </summary>
+ public DeactivateReason Reason { get; private set; }
+
+ /// <summary>
+ /// Creates a new SnippetEventArgs object, with a DeactivateReason.
+ /// </summary>
+ public SnippetEventArgs(DeactivateReason reason)
+ {
+ this.Reason = reason;
+ }
+ }
+
+ /// <summary>
+ /// Describes the reason for deactivation of a <see cref="SnippetElement" />.
+ /// </summary>
+ public enum DeactivateReason
+ {
+ /// <summary>
+ /// Unknown reason.
+ /// </summary>
+ Unknown,
+ /// <summary>
+ /// Snippet was deleted.
+ /// </summary>
+ Deleted,
+ /// <summary>
+ /// There are no active elements in the snippet.
+ /// </summary>
+ NoActiveElements,
+ /// <summary>
+ /// The SnippetInputHandler was detached.
+ /// </summary>
+ InputHandlerDetached,
+ /// <summary>
+ /// Return was pressed by the user.
+ /// </summary>
+ ReturnPressed,
+ /// <summary>
+ /// Escape was pressed by the user.
+ /// </summary>
+ EscapePressed
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetInputHandler.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetInputHandler.cs
new file mode 100644
index 000000000..6a8de2e88
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetInputHandler.cs
@@ -0,0 +1,80 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Windows.Input;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ sealed class SnippetInputHandler : TextAreaStackedInputHandler
+ {
+ readonly InsertionContext context;
+
+ public SnippetInputHandler(InsertionContext context)
+ : base(context.TextArea)
+ {
+ this.context = context;
+ }
+
+ public override void Attach()
+ {
+ base.Attach();
+
+ SelectElement(FindNextEditableElement(-1, false));
+ }
+
+ public override void Detach()
+ {
+ base.Detach();
+ context.Deactivate(new SnippetEventArgs(DeactivateReason.InputHandlerDetached));
+ }
+
+ public override void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ base.OnPreviewKeyDown(e);
+ if (e.Key == Key.Escape) {
+ context.Deactivate(new SnippetEventArgs(DeactivateReason.EscapePressed));
+ e.Handled = true;
+ } else if (e.Key == Key.Return) {
+ context.Deactivate(new SnippetEventArgs(DeactivateReason.ReturnPressed));
+ e.Handled = true;
+ } else if (e.Key == Key.Tab) {
+ bool backwards = e.KeyboardDevice.Modifiers == ModifierKeys.Shift;
+ SelectElement(FindNextEditableElement(TextArea.Caret.Offset, backwards));
+ e.Handled = true;
+ }
+ }
+
+ void SelectElement(IActiveElement element)
+ {
+ if (element != null) {
+ TextArea.Selection = Selection.Create(TextArea, element.Segment);
+ TextArea.Caret.Offset = element.Segment.EndOffset;
+ }
+ }
+
+ IActiveElement FindNextEditableElement(int offset, bool backwards)
+ {
+ IEnumerable<IActiveElement> elements = context.ActiveElements.Where(e => e.IsEditable && e.Segment != null);
+ if (backwards) {
+ elements = elements.Reverse();
+ foreach (IActiveElement element in elements) {
+ if (offset > element.Segment.EndOffset)
+ return element;
+ }
+ } else {
+ foreach (IActiveElement element in elements) {
+ if (offset < element.Segment.Offset)
+ return element;
+ }
+ }
+ return elements.FirstOrDefault();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs
new file mode 100644
index 000000000..9bf872f11
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs
@@ -0,0 +1,213 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Text element that is supposed to be replaced by the user.
+ /// Will register an <see cref="IReplaceableActiveElement"/>.
+ /// </summary>
+ [Serializable]
+ public class SnippetReplaceableTextElement : SnippetTextElement
+ {
+ /// <inheritdoc/>
+ public override void Insert(InsertionContext context)
+ {
+ int start = context.InsertionPosition;
+ base.Insert(context);
+ int end = context.InsertionPosition;
+ context.RegisterActiveElement(this, new ReplaceableActiveElement(context, start, end));
+ }
+
+ /// <inheritdoc/>
+ public override Inline ToTextRun()
+ {
+ return new Italic(base.ToTextRun());
+ }
+ }
+
+ /// <summary>
+ /// Interface for active element registered by <see cref="SnippetReplaceableTextElement"/>.
+ /// </summary>
+ public interface IReplaceableActiveElement : IActiveElement
+ {
+ /// <summary>
+ /// Gets the current text inside the element.
+ /// </summary>
+ string Text { get; }
+
+ /// <summary>
+ /// Occurs when the text inside the element changes.
+ /// </summary>
+ event EventHandler TextChanged;
+ }
+
+ sealed class ReplaceableActiveElement : IReplaceableActiveElement, IWeakEventListener
+ {
+ readonly InsertionContext context;
+ readonly int startOffset, endOffset;
+ TextAnchor start, end;
+
+ public ReplaceableActiveElement(InsertionContext context, int startOffset, int endOffset)
+ {
+ this.context = context;
+ this.startOffset = startOffset;
+ this.endOffset = endOffset;
+ }
+
+ void AnchorDeleted(object sender, EventArgs e)
+ {
+ context.Deactivate(new SnippetEventArgs(DeactivateReason.Deleted));
+ }
+
+ public void OnInsertionCompleted()
+ {
+ // anchors must be created in OnInsertionCompleted because they should move only
+ // due to user insertions, not due to insertions of other snippet parts
+ start = context.Document.CreateAnchor(startOffset);
+ start.MovementType = AnchorMovementType.BeforeInsertion;
+ end = context.Document.CreateAnchor(endOffset);
+ end.MovementType = AnchorMovementType.AfterInsertion;
+ start.Deleted += AnchorDeleted;
+ end.Deleted += AnchorDeleted;
+
+ // Be careful with references from the document to the editing/snippet layer - use weak events
+ // to prevent memory leaks when the text area control gets dropped from the UI while the snippet is active.
+ // The InsertionContext will keep us alive as long as the snippet is in interactive mode.
+ TextDocumentWeakEventManager.TextChanged.AddListener(context.Document, this);
+
+ background = new Renderer { Layer = KnownLayer.Background, element = this };
+ foreground = new Renderer { Layer = KnownLayer.Text, element = this };
+ context.TextArea.TextView.BackgroundRenderers.Add(background);
+ context.TextArea.TextView.BackgroundRenderers.Add(foreground);
+ context.TextArea.Caret.PositionChanged += Caret_PositionChanged;
+ Caret_PositionChanged(null, null);
+
+ this.Text = GetText();
+ }
+
+ public void Deactivate(SnippetEventArgs e)
+ {
+ TextDocumentWeakEventManager.TextChanged.RemoveListener(context.Document, this);
+ context.TextArea.TextView.BackgroundRenderers.Remove(background);
+ context.TextArea.TextView.BackgroundRenderers.Remove(foreground);
+ context.TextArea.Caret.PositionChanged -= Caret_PositionChanged;
+ }
+
+ bool isCaretInside;
+
+ void Caret_PositionChanged(object sender, EventArgs e)
+ {
+ ISegment s = this.Segment;
+ if (s != null) {
+ bool newIsCaretInside = s.Contains(context.TextArea.Caret.Offset);
+ if (newIsCaretInside != isCaretInside) {
+ isCaretInside = newIsCaretInside;
+ context.TextArea.TextView.InvalidateLayer(foreground.Layer);
+ }
+ }
+ }
+
+ Renderer background, foreground;
+
+ public string Text { get; private set; }
+
+ string GetText()
+ {
+ if (start.IsDeleted || end.IsDeleted)
+ return string.Empty;
+ else
+ return context.Document.GetText(start.Offset, Math.Max(0, end.Offset - start.Offset));
+ }
+
+ public event EventHandler TextChanged;
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(TextDocumentWeakEventManager.TextChanged)) {
+ string newText = GetText();
+ if (this.Text != newText) {
+ this.Text = newText;
+ if (TextChanged != null)
+ TextChanged(this, e);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public bool IsEditable {
+ get { return true; }
+ }
+
+ public ISegment Segment {
+ get {
+ if (start.IsDeleted || end.IsDeleted)
+ return null;
+ else
+ return new SimpleSegment(start.Offset, Math.Max(0, end.Offset - start.Offset));
+ }
+ }
+
+ sealed class Renderer : IBackgroundRenderer
+ {
+ static readonly Brush backgroundBrush = CreateBackgroundBrush();
+ static readonly Pen activeBorderPen = CreateBorderPen();
+
+ static Brush CreateBackgroundBrush()
+ {
+ SolidColorBrush b = new SolidColorBrush(Colors.LimeGreen);
+ b.Opacity = 0.4;
+ b.Freeze();
+ return b;
+ }
+
+ static Pen CreateBorderPen()
+ {
+ Pen p = new Pen(Brushes.Black, 1);
+ p.DashStyle = DashStyles.Dot;
+ p.Freeze();
+ return p;
+ }
+
+ internal ReplaceableActiveElement element;
+
+ public KnownLayer Layer { get; set; }
+
+ public void Draw(TextView textView, System.Windows.Media.DrawingContext drawingContext)
+ {
+ ISegment s = element.Segment;
+ if (s != null) {
+ BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
+ geoBuilder.AlignToMiddleOfPixels = true;
+ if (Layer == KnownLayer.Background) {
+ geoBuilder.AddSegment(textView, s);
+ drawingContext.DrawGeometry(backgroundBrush, null, geoBuilder.CreateGeometry());
+ } else {
+ // draw foreground only if active
+ if (element.isCaretInside) {
+ geoBuilder.AddSegment(textView, s);
+ foreach (BoundActiveElement boundElement in element.context.ActiveElements.OfType<BoundActiveElement>()) {
+ if (boundElement.targetElement == element) {
+ geoBuilder.AddSegment(textView, boundElement.Segment);
+ geoBuilder.CloseFigure();
+ }
+ }
+ drawingContext.DrawGeometry(null, activeBorderPen, geoBuilder.CreateGeometry());
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetSelectionElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetSelectionElement.cs
new file mode 100644
index 000000000..4c073094b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetSelectionElement.cs
@@ -0,0 +1,45 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text;
+using System.Windows.Input;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Inserts the previously selected text at the selection marker.
+ /// </summary>
+ [Serializable]
+ public class SnippetSelectionElement : SnippetElement
+ {
+ /// <summary>
+ /// Gets/Sets the new indentation of the selected text.
+ /// </summary>
+ public int Indentation { get; set; }
+
+ /// <inheritdoc/>
+ public override void Insert(InsertionContext context)
+ {
+ StringBuilder tabString = new StringBuilder();
+
+ for (int i = 0; i < Indentation; i++) {
+ tabString.Append(context.Tab);
+ }
+
+ string indent = tabString.ToString();
+
+ string text = context.SelectedText.TrimStart(' ', '\t');
+
+ text = text.Replace(context.LineTerminator,
+ context.LineTerminator + indent);
+
+ context.Document.Insert(context.InsertionPosition, text);
+ context.InsertionPosition += text.Length;
+
+ if (string.IsNullOrEmpty(context.SelectedText))
+ SnippetCaretElement.SetCaret(context);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetTextElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetTextElement.cs
new file mode 100644
index 000000000..decc18119
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Snippets/SnippetTextElement.cs
@@ -0,0 +1,39 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows.Documents;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Snippets
+{
+ /// <summary>
+ /// Represents a text element in a snippet.
+ /// </summary>
+ [Serializable]
+ public class SnippetTextElement : SnippetElement
+ {
+ string text;
+
+ /// <summary>
+ /// The text to be inserted.
+ /// </summary>
+ public string Text {
+ get { return text; }
+ set { text = value; }
+ }
+
+ /// <inheritdoc/>
+ public override void Insert(InsertionContext context)
+ {
+ if (text != null)
+ context.InsertText(text);
+ }
+
+ /// <inheritdoc/>
+ public override Inline ToTextRun()
+ {
+ return new Run(text ?? string.Empty);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj
new file mode 100644
index 000000000..5dc21efae
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Tango.Scripting.Editors.csproj
@@ -0,0 +1,498 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ProjectGuid>{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}</ProjectGuid>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <OutputType>Library</OutputType>
+ <RootNamespace>Tango.Scripting.Editors</RootNamespace>
+ <AssemblyName>Tango.Scripting.Editors</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <SourceAnalysisOverrideSettingsFile>"C:\Program Files\SharpDevelop\3.0\bin\..\AddIns\AddIns\Misc\SourceAnalysis\Settings.SourceAnalysis"</SourceAnalysisOverrideSettingsFile>
+ <AllowUnsafeBlocks>False</AllowUnsafeBlocks>
+ <NoStdLib>False</NoStdLib>
+ <WarningLevel>4</WarningLevel>
+ <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+ <SignAssembly>false</SignAssembly>
+ <AssemblyOriginatorKeyFile>ICSharpCode.AvalonEdit.snk</AssemblyOriginatorKeyFile>
+ <DelaySign>False</DelaySign>
+ <AssemblyOriginatorKeyMode>File</AssemblyOriginatorKeyMode>
+ <RunCodeAnalysis>False</RunCodeAnalysis>
+ <CodeAnalysisRules>-Microsoft.Design#CA1020;-Microsoft.Design#CA1033;-Microsoft.Performance#CA1805;-Microsoft.Performance#CA1810</CodeAnalysisRules>
+ <OutputPath>..\bin\$(Configuration)</OutputPath>
+ <DocumentationFile>..\bin\$(Configuration)\ICSharpCode.AvalonEdit.xml</DocumentationFile>
+ <NoWarn>1607</NoWarn>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
+ <SccProjectName>SAK</SccProjectName>
+ <SccLocalPath>SAK</SccLocalPath>
+ <SccAuxPath>SAK</SccAuxPath>
+ <SccProvider>SAK</SccProvider>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>Full</DebugType>
+ <Optimize>False</Optimize>
+ <CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
+ <DefineConstants>DEBUG;TRACE;DOTNET4</DefineConstants>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+ <DebugSymbols>false</DebugSymbols>
+ <DebugType>PdbOnly</DebugType>
+ <Optimize>True</Optimize>
+ <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
+ <DefineConstants>TRACE;DOTNET4</DefineConstants>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
+ <RegisterForComInterop>False</RegisterForComInterop>
+ <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
+ <BaseAddress>4194304</BaseAddress>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <FileAlignment>4096</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
+ <OutputPath>bin\Debug\</OutputPath>
+ <DocumentationFile>
+ </DocumentationFile>
+ <Prefer32Bit>false</Prefer32Bit>
+ <DefineConstants>TRACE;DEBUG</DefineConstants>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
+ <Prefer32Bit>false</Prefer32Bit>
+ <OutputPath>bin\Release\</OutputPath>
+ <DocumentationFile />
+ </PropertyGroup>
+ <PropertyGroup>
+ <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ </PropertyGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
+ <ItemGroup>
+ <Reference Include="FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL">
+ <HintPath>..\packages\FontAwesome.WPF.4.7.0.9\lib\net40\FontAwesome.WPF.dll</HintPath>
+ </Reference>
+ <Reference Include="PresentationCore">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="PresentationFramework">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Windows.Forms" />
+ <Reference Include="System.Xaml">
+ <RequiredTargetFramework>4.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="Tango.Core">
+ <HintPath>D:\Development\Tango\Software\Visual_Studio\Build\Core\Debug\Tango.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="UIAutomationProvider">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="UIAutomationTypes">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="WindowsBase">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AvalonEditCommands.cs" />
+ <Compile Include="CodeCompletion\CompletionListBox.cs" />
+ <Compile Include="CodeCompletion\CompletionWindowBase.cs" />
+ <Compile Include="CodeCompletion\CompletionList.cs" />
+ <Compile Include="CodeCompletion\CompletionWindow.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="CodeCompletion\ICompletionData.cs" />
+ <Compile Include="CodeCompletion\InsightWindow.cs" />
+ <Compile Include="CodeCompletion\IOverloadProvider.cs" />
+ <Compile Include="CodeCompletion\OverloadInsightWindow.cs" />
+ <Compile Include="CodeCompletion\OverloadViewer.cs" />
+ <Compile Include="Document\ChangeTrackingCheckpoint.cs" />
+ <Compile Include="Document\DocumentChangeOperation.cs">
+ <DependentUpon>UndoStack.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\ILineTracker.cs" />
+ <Compile Include="Document\ISegment.cs" />
+ <Compile Include="Document\ITextSource.cs" />
+ <Compile Include="Document\IUndoableOperation.cs">
+ <DependentUpon>UndoStack.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\LineNode.cs">
+ <DependentUpon>DocumentLine.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\NewLineFinder.cs" />
+ <Compile Include="Document\OffsetChangeMap.cs" />
+ <Compile Include="Document\TextDocumentWeakEventManager.cs">
+ <DependentUpon>TextDocument.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\TextSegmentCollection.cs" />
+ <Compile Include="Document\TextAnchor.cs" />
+ <Compile Include="Document\TextAnchorNode.cs">
+ <DependentUpon>TextAnchor.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\TextAnchorTree.cs">
+ <DependentUpon>TextAnchor.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\TextLocation.cs" />
+ <Compile Include="Document\TextSegment.cs" />
+ <Compile Include="Document\TextUtilities.cs" />
+ <Compile Include="Document\UndoOperationGroup.cs">
+ <DependentUpon>UndoStack.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\UndoStack.cs">
+ </Compile>
+ <Compile Include="Document\WeakLineTracker.cs">
+ <DependentUpon>ILineTracker.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\AbstractMargin.cs" />
+ <Compile Include="Editing\Caret.cs" />
+ <Compile Include="Editing\CaretLayer.cs">
+ </Compile>
+ <Compile Include="Editing\CaretNavigationCommandHandler.cs">
+ </Compile>
+ <Compile Include="Editing\CaretWeakEventHandler.cs" />
+ <Compile Include="Editing\DottedLineMargin.cs" />
+ <Compile Include="Editing\DragDropException.cs" />
+ <Compile Include="Editing\EditingCommandHandler.cs" />
+ <Compile Include="Editing\EmptySelection.cs">
+ <DependentUpon>Selection.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\ImeNativeWrapper.cs" />
+ <Compile Include="Editing\SelectionSegment.cs" />
+ <Compile Include="Editing\ImeSupport.cs" />
+ <Compile Include="Folding\AbstractFoldingStrategy.cs" />
+ <Compile Include="Folding\BraceFoldingStrategy.cs" />
+ <Compile Include="Folding\FoldingElementGenerator.cs" />
+ <Compile Include="Folding\FoldingManager.cs" />
+ <Compile Include="Folding\FoldingMargin.cs" />
+ <Compile Include="Folding\FoldingMarginMarker.cs" />
+ <Compile Include="Folding\FoldingSection.cs" />
+ <Compile Include="Editing\IReadOnlySectionProvider.cs" />
+ <Compile Include="Editing\LineNumberMargin.cs" />
+ <Compile Include="Editing\NoReadOnlySections.cs">
+ <DependentUpon>IReadOnlySectionProvider.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\RectangleSelection.cs">
+ <DependentUpon>Selection.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\Selection.cs" />
+ <Compile Include="Editing\SelectionColorizer.cs">
+ <DependentUpon>Selection.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\SelectionLayer.cs">
+ <DependentUpon>Selection.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\SelectionMouseHandler.cs">
+ <DependentUpon>Selection.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\SimpleSelection.cs">
+ <DependentUpon>Selection.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Editing\TextArea.cs" />
+ <Compile Include="Editing\TextAreaDefaultInputHandlers.cs" />
+ <Compile Include="Editing\TextAreaInputHandler.cs" />
+ <Compile Include="Editing\TextSegmentReadOnlySectionProvider.cs">
+ <DependentUpon>IReadOnlySectionProvider.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Folding\NewFolding.cs" />
+ <Compile Include="Folding\XmlFoldingStrategy.cs" />
+ <Compile Include="Highlighting\DocumentHighlighter.cs" />
+ <Compile Include="Highlighting\HighlightedInlineBuilder.cs" />
+ <Compile Include="Highlighting\HighlightedLine.cs" />
+ <Compile Include="Highlighting\HighlightedSection.cs" />
+ <Compile Include="Highlighting\HighlightingBrush.cs" />
+ <Compile Include="Highlighting\HighlightingColor.cs" />
+ <Compile Include="Highlighting\HighlightingColorizer.cs" />
+ <Compile Include="Highlighting\HighlightingDefinitionInvalidException.cs" />
+ <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" />
+ <Compile Include="Highlighting\HighlightingRuleSet.cs" />
+ <Compile Include="Highlighting\HighlightingSpan.cs" />
+ <Compile Include="Highlighting\IHighlightingDefinitionReferenceResolver.cs">
+ </Compile>
+ <Compile Include="Highlighting\Xshd\HighlightingLoader.cs" />
+ <Compile Include="Highlighting\Xshd\IXshdVisitor.cs" />
+ <Compile Include="Highlighting\Xshd\SaveXshdVisitor.cs" />
+ <Compile Include="Highlighting\Xshd\V1Loader.cs" />
+ <Compile Include="Highlighting\Xshd\V2Loader.cs" />
+ <Compile Include="Highlighting\Xshd\XmlHighlightingDefinition.cs" />
+ <Compile Include="Highlighting\Xshd\XshdColor.cs" />
+ <Compile Include="Highlighting\Xshd\XshdImport.cs" />
+ <Compile Include="Highlighting\Xshd\XshdProperty.cs" />
+ <Compile Include="Highlighting\Xshd\XshdReference.cs" />
+ <Compile Include="Highlighting\Xshd\XshdElement.cs" />
+ <Compile Include="Highlighting\Xshd\XshdKeywords.cs" />
+ <Compile Include="Highlighting\Xshd\XshdRule.cs" />
+ <Compile Include="Highlighting\Xshd\XshdRuleSet.cs" />
+ <Compile Include="Highlighting\Xshd\XshdSpan.cs" />
+ <Compile Include="Highlighting\Xshd\XshdSyntaxDefinition.cs" />
+ <Compile Include="Indentation\CSharp\CSharpIndentationHelper.cs" />
+ <Compile Include="Indentation\CSharp\IndentationReformatter.cs" />
+ <Compile Include="Indentation\CSharp\CSharpIndentationStrategy.cs" />
+ <Compile Include="Indentation\CSharp\DocumentAccessor.cs" />
+ <Compile Include="Indentation\DefaultIndentationStrategy.cs" />
+ <Compile Include="Indentation\IIndentationStrategy.cs" />
+ <Compile Include="Rendering\BackgroundGeometryBuilder.cs">
+ <DependentUpon>IBackgroundRenderer.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\CollapsedLineSection.cs">
+ <DependentUpon>HeightTree.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\ColorizingTransformer.cs">
+ <DependentUpon>IVisualLineTransformer.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\ColumnRulerRenderer.cs" />
+ <Compile Include="Rendering\DefaultTextRunTypographyProperties.cs" />
+ <Compile Include="Rendering\DocumentColorizingTransformer.cs">
+ <DependentUpon>IVisualLineTransformer.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\FormattedTextElement.cs" />
+ <Compile Include="Rendering\GlobalTextRunProperties.cs">
+ <DependentUpon>TextView.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\HeightTree.cs" />
+ <Compile Include="Rendering\HeightTreeLineNode.cs">
+ <DependentUpon>HeightTree.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\HeightTreeNode.cs">
+ <DependentUpon>HeightTree.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\IBackgroundRenderer.cs" />
+ <Compile Include="Rendering\InlineObjectRun.cs" />
+ <Compile Include="Rendering\ITextRunConstructionContext.cs">
+ <DependentUpon>VisualLineElementGenerator.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\ITextViewConnect.cs">
+ <DependentUpon>TextView.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\IVisualLineTransformer.cs" />
+ <Compile Include="Rendering\Layer.cs">
+ <DependentUpon>TextView.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\LayerPosition.cs">
+ <DependentUpon>TextView.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\LinkElementGenerator.cs" />
+ <Compile Include="Rendering\MouseHoverLogic.cs" />
+ <Compile Include="Rendering\SimpleTextSource.cs">
+ <DependentUpon>FormattedTextElement.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\SingleCharacterElementGenerator.cs" />
+ <Compile Include="Rendering\TextLayer.cs">
+ <DependentUpon>TextView.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\TextView.cs" />
+ <Compile Include="Rendering\TextViewCachedElements.cs" />
+ <Compile Include="Rendering\TextViewWeakEventManager.cs">
+ <DependentUpon>TextView.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\VisualLine.cs" />
+ <Compile Include="Rendering\VisualLineConstructionStartEventArgs.cs" />
+ <Compile Include="Rendering\VisualLineElement.cs" />
+ <Compile Include="Rendering\VisualLineElementGenerator.cs" />
+ <Compile Include="Rendering\VisualLineElementTextRunProperties.cs">
+ <DependentUpon>VisualLine.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\VisualLineLinkText.cs" />
+ <Compile Include="Rendering\VisualLinesInvalidException.cs" />
+ <Compile Include="Rendering\VisualLineText.cs" />
+ <Compile Include="Rendering\VisualLineTextParagraphProperties.cs">
+ <DependentUpon>VisualLine.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\VisualLineTextSource.cs">
+ <DependentUpon>VisualLineElementGenerator.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Rendering\VisualYPosition.cs">
+ <DependentUpon>VisualLine.cs</DependentUpon>
+ </Compile>
+ <Compile Include="ScriptEditor.cs" />
+ <Compile Include="Search\Localization.cs" />
+ <Compile Include="Search\RegexSearchStrategy.cs" />
+ <Compile Include="Search\DropDownButton.cs" />
+ <Compile Include="Search\ISearchStrategy.cs" />
+ <Compile Include="Search\SearchCommands.cs" />
+ <Compile Include="Search\SearchResultBackgroundRenderer.cs" />
+ <Compile Include="Search\SearchPanel.cs">
+ <SubType>Code</SubType>
+ </Compile>
+ <Compile Include="Search\SearchStrategyFactory.cs" />
+ <Compile Include="Snippets\IActiveElement.cs" />
+ <Compile Include="Snippets\SnippetAnchorElement.cs" />
+ <Compile Include="Snippets\SnippetEventArgs.cs" />
+ <Compile Include="Snippets\SnippetInputHandler.cs" />
+ <Compile Include="Snippets\Snippet.cs" />
+ <Compile Include="Snippets\SnippetBoundElement.cs" />
+ <Compile Include="Snippets\SnippetCaretElement.cs" />
+ <Compile Include="Snippets\SnippetContainerElement.cs" />
+ <Compile Include="Snippets\SnippetElement.cs" />
+ <Compile Include="Snippets\InsertionContext.cs" />
+ <Compile Include="Snippets\SnippetReplaceableTextElement.cs" />
+ <Compile Include="Snippets\SnippetSelectionElement.cs" />
+ <Compile Include="Snippets\SnippetTextElement.cs" />
+ <Compile Include="TextEditor.cs" />
+ <Compile Include="TextEditorAutomationPeer.cs" />
+ <Compile Include="TextEditorComponent.cs">
+ </Compile>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Document\DocumentChangeEventArgs.cs" />
+ <Compile Include="Document\GapTextBuffer.cs">
+ <DependentUpon>TextDocument.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\LineManager.cs">
+ <DependentUpon>TextDocument.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\DocumentLine.cs" />
+ <Compile Include="Document\DocumentLineTree.cs">
+ <DependentUpon>DocumentLine.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Document\TextDocument.cs" />
+ <Compile Include="TextEditorOptions.cs" />
+ <Compile Include="TextEditorWeakEventManager.cs">
+ <DependentUpon>TextEditor.cs</DependentUpon>
+ </Compile>
+ <Compile Include="TextViewPosition.cs" />
+ <Compile Include="Utils\Boxes.cs" />
+ <Compile Include="Utils\BusyManager.cs">
+ <DependentUpon>ObserveAddRemoveCollection.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Utils\CharRope.cs" />
+ <Compile Include="Utils\CompressingTreeList.cs" />
+ <Compile Include="Utils\Constants.cs" />
+ <Compile Include="Utils\DelayedEvents.cs" />
+ <Compile Include="Utils\CallbackOnDispose.cs" />
+ <Compile Include="Utils\Deque.cs" />
+ <Compile Include="Utils\Empty.cs" />
+ <Compile Include="Utils\ExtensionMethods.cs" />
+ <Compile Include="Utils\FileReader.cs" />
+ <Compile Include="Utils\ImmutableStack.cs" />
+ <Compile Include="Utils\NullSafeCollection.cs" />
+ <Compile Include="Utils\ObserveAddRemoveCollection.cs" />
+ <Compile Include="Utils\PropertyChangedWeakEventManager.cs" />
+ <Compile Include="Utils\Rope.cs" />
+ <Compile Include="Utils\RopeNode.cs" />
+ <Compile Include="Utils\RopeTextReader.cs" />
+ <Compile Include="Utils\StringSegment.cs" />
+ <Compile Include="Utils\TextFormatterFactory.cs" />
+ <Compile Include="Utils\WeakEventManagerBase.cs" />
+ <Compile Include="Utils\PixelSnapHelpers.cs" />
+ <Compile Include="Utils\ThrowUtil.cs" />
+ <Compile Include="Utils\Win32.cs" />
+ <CodeAnalysisDictionary Include="Properties\CodeAnalysisDictionary.xml" />
+ <Compile Include="Xml\AbstractAXmlVisitor.cs" />
+ <Compile Include="Xml\AXmlAttribute.cs" />
+ <Compile Include="Xml\AXmlAttributeCollection.cs" />
+ <Compile Include="Xml\AXmlContainer.cs" />
+ <Compile Include="Xml\AXmlDocument.cs" />
+ <Compile Include="Xml\AXmlElement.cs" />
+ <Compile Include="Xml\AXmlObject.cs" />
+ <Compile Include="Xml\AXmlObjectCollection.cs" />
+ <Compile Include="Xml\AXmlObjectEventArgs.cs" />
+ <Compile Include="Xml\AXmlParser.cs" />
+ <Compile Include="Xml\AXmlTag.cs" />
+ <Compile Include="Xml\AXmlText.cs" />
+ <Compile Include="Xml\CanonicalPrintAXmlVisitor.cs" />
+ <Compile Include="Xml\InternalException.cs" />
+ <Compile Include="Xml\TrackedSegmentCollection.cs">
+ <DependentUpon>AXmlParser.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Xml\ExtensionMethods.cs" />
+ <Compile Include="Xml\FilteredCollection.cs" />
+ <Compile Include="Xml\IAXmlVisitor.cs" />
+ <Compile Include="Xml\MergedCollection.cs" />
+ <Compile Include="Xml\PrettyPrintAXmlVisitor.cs" />
+ <Compile Include="Xml\SyntaxError.cs" />
+ <Compile Include="Xml\TagMatchingHeuristics.cs">
+ <DependentUpon>AXmlParser.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Xml\TagReader.cs">
+ <DependentUpon>AXmlParser.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Xml\TextType.cs">
+ <DependentUpon>AXmlText.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Xml\TokenReader.cs">
+ <DependentUpon>AXmlParser.cs</DependentUpon>
+ </Compile>
+ <Resource Include="Themes\RightArrow.cur" />
+ <EmbeddedResource Include="Highlighting\Resources\ASPX.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\Boo.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\Coco-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\CPP-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\HTML-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\Java-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\JavaScript-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\Patch-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\PHP-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\Tex-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\VBNET-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\XML-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\ModeV1.xsd" />
+ <EmbeddedResource Include="Highlighting\Resources\ModeV2.xsd" />
+ <EmbeddedResource Include="Highlighting\Resources\CSharp-Mode.xshd" />
+ <EmbeddedResource Include="Highlighting\Resources\XmlDoc.xshd" />
+ </ItemGroup>
+ <ItemGroup>
+ <Page Include="CodeCompletion\CompletionList.xaml" />
+ <Page Include="CodeCompletion\InsightWindow.xaml" />
+ <Page Include="Search\SearchPanel.xaml">
+ <DependentUpon>SearchPanel.cs</DependentUpon>
+ </Page>
+ <Page Include="Search\DropDownButton.xaml">
+ <DependentUpon>DropDownButton.cs</DependentUpon>
+ </Page>
+ <Page Include="TextEditor.xaml" />
+ <Page Include="Theme.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ <Page Include="Themes\Generic.xaml">
+ <SubType>Designer</SubType>
+ </Page>
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Highlighting\Resources\CSS-Mode.xshd" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Tango.Scripting\Tango.Scripting.csproj">
+ <Project>{1e938fd2-c669-4738-98c9-77f96ce4d451}</Project>
+ <Name>Tango.Scripting</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Resource Include="Search\next.png" />
+ <Resource Include="Search\prev.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Highlighting\Resources\PowerShell.xshd" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Highlighting\Resources\MarkDown-Mode.xshd" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="app.config" />
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ProjectExtensions>
+ <VisualStudio>
+ <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UseGlobalSettings="True" />
+ </VisualStudio>
+ </ProjectExtensions>
+</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.cs
new file mode 100644
index 000000000..d2fc9e02b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.cs
@@ -0,0 +1,1144 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Markup;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Highlighting;
+using Tango.Scripting.Editors.Rendering;
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors
+{
+ /// <summary>
+ /// The text editor control.
+ /// Contains a scrollable TextArea.
+ /// </summary>
+ [Localizability(LocalizationCategory.Text), ContentProperty("Text")]
+ public class TextEditor : Control, ITextEditorComponent, IServiceProvider, IWeakEventListener
+ {
+ #region Constructors
+ static TextEditor()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(TextEditor),
+ new FrameworkPropertyMetadata(typeof(TextEditor)));
+ FocusableProperty.OverrideMetadata(typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.True));
+ }
+
+ /// <summary>
+ /// Creates a new TextEditor instance.
+ /// </summary>
+ public TextEditor() : this(new TextArea())
+ {
+ }
+
+ /// <summary>
+ /// Creates a new TextEditor instance.
+ /// </summary>
+ protected TextEditor(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+
+ textArea.TextView.Services.AddService(typeof(TextEditor), this);
+
+ SetCurrentValue(OptionsProperty, textArea.Options);
+ SetCurrentValue(DocumentProperty, new TextDocument());
+ }
+
+ #endregion
+
+ /// <inheritdoc/>
+ protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
+ {
+ return new TextEditorAutomationPeer(this);
+ }
+
+ /// Forward focus to TextArea.
+ /// <inheritdoc/>
+ protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ base.OnGotKeyboardFocus(e);
+ if (e.NewFocus == this) {
+ Keyboard.Focus(this.TextArea);
+ e.Handled = true;
+ }
+ }
+
+ #region Document property
+ /// <summary>
+ /// Document property.
+ /// </summary>
+ public static readonly DependencyProperty DocumentProperty
+ = TextView.DocumentProperty.AddOwner(
+ typeof(TextEditor), new FrameworkPropertyMetadata(OnDocumentChanged));
+
+ /// <summary>
+ /// Gets/Sets the document displayed by the text editor.
+ /// This is a dependency property.
+ /// </summary>
+ public TextDocument Document {
+ get { return (TextDocument)GetValue(DocumentProperty); }
+ set { SetValue(DocumentProperty, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the document property has changed.
+ /// </summary>
+ public event EventHandler DocumentChanged;
+
+ /// <summary>
+ /// Raises the <see cref="DocumentChanged"/> event.
+ /// </summary>
+ protected virtual void OnDocumentChanged(EventArgs e)
+ {
+ if (DocumentChanged != null) {
+ DocumentChanged(this, e);
+ }
+ }
+
+ static void OnDocumentChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextEditor)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue);
+ }
+
+ void OnDocumentChanged(TextDocument oldValue, TextDocument newValue)
+ {
+ if (oldValue != null) {
+ TextDocumentWeakEventManager.TextChanged.RemoveListener(oldValue, this);
+ PropertyChangedEventManager.RemoveListener(oldValue.UndoStack, this, "IsOriginalFile");
+ }
+ textArea.Document = newValue;
+ if (newValue != null) {
+ TextDocumentWeakEventManager.TextChanged.AddListener(newValue, this);
+ PropertyChangedEventManager.AddListener(newValue.UndoStack, this, "IsOriginalFile");
+ }
+ OnDocumentChanged(EventArgs.Empty);
+ OnTextChanged(EventArgs.Empty);
+ }
+ #endregion
+
+ #region Options property
+ /// <summary>
+ /// Options property.
+ /// </summary>
+ public static readonly DependencyProperty OptionsProperty
+ = TextView.OptionsProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(OnOptionsChanged));
+
+ /// <summary>
+ /// Gets/Sets the options currently used by the text editor.
+ /// </summary>
+ public TextEditorOptions Options {
+ get { return (TextEditorOptions)GetValue(OptionsProperty); }
+ set { SetValue(OptionsProperty, value); }
+ }
+
+ /// <summary>
+ /// Occurs when a text editor option has changed.
+ /// </summary>
+ public event PropertyChangedEventHandler OptionChanged;
+
+ /// <summary>
+ /// Raises the <see cref="OptionChanged"/> event.
+ /// </summary>
+ protected virtual void OnOptionChanged(PropertyChangedEventArgs e)
+ {
+ if (OptionChanged != null) {
+ OptionChanged(this, e);
+ }
+ }
+
+ static void OnOptionsChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextEditor)dp).OnOptionsChanged((TextEditorOptions)e.OldValue, (TextEditorOptions)e.NewValue);
+ }
+
+ void OnOptionsChanged(TextEditorOptions oldValue, TextEditorOptions newValue)
+ {
+ if (oldValue != null) {
+ PropertyChangedWeakEventManager.RemoveListener(oldValue, this);
+ }
+ textArea.Options = newValue;
+ if (newValue != null) {
+ PropertyChangedWeakEventManager.AddListener(newValue, this);
+ }
+ OnOptionChanged(new PropertyChangedEventArgs(null));
+ }
+
+ /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
+ protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(PropertyChangedWeakEventManager)) {
+ OnOptionChanged((PropertyChangedEventArgs)e);
+ return true;
+ } else if (managerType == typeof(TextDocumentWeakEventManager.TextChanged)) {
+ OnTextChanged(e);
+ return true;
+ } else if (managerType == typeof(PropertyChangedEventManager)) {
+ return HandleIsOriginalChanged((PropertyChangedEventArgs)e);
+ }
+ return false;
+ }
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ return ReceiveWeakEvent(managerType, sender, e);
+ }
+ #endregion
+
+ #region Text property
+ /// <summary>
+ /// Gets/Sets the text of the current document.
+ /// </summary>
+ [Localizability(LocalizationCategory.Text), DefaultValue("")]
+ public string Text {
+ get {
+ TextDocument document = this.Document;
+ return document != null ? document.Text : string.Empty;
+ }
+ set {
+ TextDocument document = GetDocument();
+ document.Text = value ?? string.Empty;
+ // after replacing the full text, the caret is positioned at the end of the document
+ // - reset it to the beginning.
+ this.CaretOffset = 0;
+ document.UndoStack.ClearAll();
+ }
+ }
+
+ TextDocument GetDocument()
+ {
+ TextDocument document = this.Document;
+ if (document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return document;
+ }
+
+ /// <summary>
+ /// Occurs when the Text property changes.
+ /// </summary>
+ public event EventHandler TextChanged;
+
+ /// <summary>
+ /// Raises the <see cref="TextChanged"/> event.
+ /// </summary>
+ protected virtual void OnTextChanged(EventArgs e)
+ {
+ if (TextChanged != null) {
+ TextChanged(this, e);
+ }
+ }
+ #endregion
+
+ #region TextArea / ScrollViewer properties
+ readonly TextArea textArea;
+ ScrollViewer scrollViewer;
+
+ /// <summary>
+ /// Is called after the template was applied.
+ /// </summary>
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ scrollViewer = (ScrollViewer)Template.FindName("PART_ScrollViewer", this);
+ }
+
+ /// <summary>
+ /// Gets the text area.
+ /// </summary>
+ public TextArea TextArea {
+ get {
+ return textArea;
+ }
+ }
+
+ /// <summary>
+ /// Gets the scroll viewer used by the text editor.
+ /// This property can return null if the template has not been applied / does not contain a scroll viewer.
+ /// </summary>
+ internal ScrollViewer ScrollViewer {
+ get { return scrollViewer; }
+ }
+
+ bool CanExecute(RoutedUICommand command)
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea == null)
+ return false;
+ else
+ return command.CanExecute(null, textArea);
+ }
+
+ void Execute(RoutedUICommand command)
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea != null)
+ command.Execute(null, textArea);
+ }
+ #endregion
+
+ #region Syntax highlighting
+ /// <summary>
+ /// The <see cref="SyntaxHighlighting"/> property.
+ /// </summary>
+ public static readonly DependencyProperty SyntaxHighlightingProperty =
+ DependencyProperty.Register("SyntaxHighlighting", typeof(IHighlightingDefinition), typeof(TextEditor),
+ new FrameworkPropertyMetadata(OnSyntaxHighlightingChanged));
+
+
+ /// <summary>
+ /// Gets/sets the syntax highlighting definition used to colorize the text.
+ /// </summary>
+ public IHighlightingDefinition SyntaxHighlighting {
+ get { return (IHighlightingDefinition)GetValue(SyntaxHighlightingProperty); }
+ set { SetValue(SyntaxHighlightingProperty, value); }
+ }
+
+ IVisualLineTransformer colorizer;
+
+ static void OnSyntaxHighlightingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextEditor)d).OnSyntaxHighlightingChanged(e.NewValue as IHighlightingDefinition);
+ }
+
+ void OnSyntaxHighlightingChanged(IHighlightingDefinition newValue)
+ {
+ if (colorizer != null) {
+ this.TextArea.TextView.LineTransformers.Remove(colorizer);
+ colorizer = null;
+ }
+ if (newValue != null) {
+ colorizer = CreateColorizer(newValue);
+ this.TextArea.TextView.LineTransformers.Insert(0, colorizer);
+ }
+ }
+
+ /// <summary>
+ /// Creates the highlighting colorizer for the specified highlighting definition.
+ /// Allows derived classes to provide custom colorizer implementations for special highlighting definitions.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual IVisualLineTransformer CreateColorizer(IHighlightingDefinition highlightingDefinition)
+ {
+ if (highlightingDefinition == null)
+ throw new ArgumentNullException("highlightingDefinition");
+ return new HighlightingColorizer(highlightingDefinition.MainRuleSet);
+ }
+ #endregion
+
+ #region WordWrap
+ /// <summary>
+ /// Word wrap dependency property.
+ /// </summary>
+ public static readonly DependencyProperty WordWrapProperty =
+ DependencyProperty.Register("WordWrap", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False));
+
+ /// <summary>
+ /// Specifies whether the text editor uses word wrapping.
+ /// </summary>
+ /// <remarks>
+ /// Setting WordWrap=true has the same effect as setting HorizontalScrollBarVisibility=Disabled and will override the
+ /// HorizontalScrollBarVisibility setting.
+ /// </remarks>
+ public bool WordWrap {
+ get { return (bool)GetValue(WordWrapProperty); }
+ set { SetValue(WordWrapProperty, Boxes.Box(value)); }
+ }
+ #endregion
+
+ #region IsReadOnly
+ /// <summary>
+ /// IsReadOnly dependency property.
+ /// </summary>
+ public static readonly DependencyProperty IsReadOnlyProperty =
+ DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False, OnIsReadOnlyChanged));
+
+ /// <summary>
+ /// Specifies whether the user can change the text editor content.
+ /// Setting this property will replace the
+ /// <see cref="Editing.TextArea.ReadOnlySectionProvider">TextArea.ReadOnlySectionProvider</see>.
+ /// </summary>
+ public bool IsReadOnly {
+ get { return (bool)GetValue(IsReadOnlyProperty); }
+ set { SetValue(IsReadOnlyProperty, Boxes.Box(value)); }
+ }
+
+ static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = d as TextEditor;
+ if (editor != null) {
+ if ((bool)e.NewValue)
+ editor.TextArea.ReadOnlySectionProvider = ReadOnlyDocument.Instance;
+ else
+ editor.TextArea.ReadOnlySectionProvider = NoReadOnlySections.Instance;
+
+ TextEditorAutomationPeer peer = TextEditorAutomationPeer.FromElement(editor) as TextEditorAutomationPeer;
+ if (peer != null) {
+ peer.RaiseIsReadOnlyChanged((bool)e.OldValue, (bool)e.NewValue);
+ }
+ }
+ }
+ #endregion
+
+ #region IsModified
+ /// <summary>
+ /// Dependency property for <see cref="IsModified"/>
+ /// </summary>
+ public static readonly DependencyProperty IsModifiedProperty =
+ DependencyProperty.Register("IsModified", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False, OnIsModifiedChanged));
+
+ /// <summary>
+ /// Gets/Sets the 'modified' flag.
+ /// </summary>
+ public bool IsModified {
+ get { return (bool)GetValue(IsModifiedProperty); }
+ set { SetValue(IsModifiedProperty, Boxes.Box(value)); }
+ }
+
+ static void OnIsModifiedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = d as TextEditor;
+ if (editor != null) {
+ TextDocument document = editor.Document;
+ if (document != null) {
+ UndoStack undoStack = document.UndoStack;
+ if ((bool)e.NewValue) {
+ if (undoStack.IsOriginalFile)
+ undoStack.DiscardOriginalFileMarker();
+ } else {
+ undoStack.MarkAsOriginalFile();
+ }
+ }
+ }
+ }
+
+ bool HandleIsOriginalChanged(PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "IsOriginalFile") {
+ TextDocument document = this.Document;
+ if (document != null) {
+ SetCurrentValue(IsModifiedProperty, Boxes.Box(!document.UndoStack.IsOriginalFile));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ #endregion
+
+ #region ShowLineNumbers
+ /// <summary>
+ /// ShowLineNumbers dependency property.
+ /// </summary>
+ public static readonly DependencyProperty ShowLineNumbersProperty =
+ DependencyProperty.Register("ShowLineNumbers", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False, OnShowLineNumbersChanged));
+
+ /// <summary>
+ /// Specifies whether line numbers are shown on the left to the text view.
+ /// </summary>
+ public bool ShowLineNumbers {
+ get { return (bool)GetValue(ShowLineNumbersProperty); }
+ set { SetValue(ShowLineNumbersProperty, Boxes.Box(value)); }
+ }
+
+ static void OnShowLineNumbersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = (TextEditor)d;
+ var leftMargins = editor.TextArea.LeftMargins;
+ if ((bool)e.NewValue) {
+ LineNumberMargin lineNumbers = new LineNumberMargin();
+ Line line = (Line)DottedLineMargin.Create();
+ leftMargins.Insert(0, lineNumbers);
+ leftMargins.Insert(1, line);
+ var lineNumbersForeground = new Binding("LineNumbersForeground") { Source = editor };
+ line.SetBinding(Line.StrokeProperty, lineNumbersForeground);
+ lineNumbers.SetBinding(Control.ForegroundProperty, lineNumbersForeground);
+ } else {
+ for (int i = 0; i < leftMargins.Count; i++) {
+ if (leftMargins[i] is LineNumberMargin) {
+ leftMargins.RemoveAt(i);
+ if (i < leftMargins.Count && DottedLineMargin.IsDottedLineMargin(leftMargins[i])) {
+ leftMargins.RemoveAt(i);
+ }
+ break;
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region LineNumbersForeground
+ /// <summary>
+ /// LineNumbersForeground dependency property.
+ /// </summary>
+ public static readonly DependencyProperty LineNumbersForegroundProperty =
+ DependencyProperty.Register("LineNumbersForeground", typeof(Brush), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Brushes.Gray, OnLineNumbersForegroundChanged));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying the foreground color of line numbers.
+ /// </summary>
+ public Brush LineNumbersForeground {
+ get { return (Brush)GetValue(LineNumbersForegroundProperty); }
+ set { SetValue(LineNumbersForegroundProperty, value); }
+ }
+
+ static void OnLineNumbersForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = (TextEditor)d;
+ var lineNumberMargin = editor.TextArea.LeftMargins.FirstOrDefault(margin => margin is LineNumberMargin) as LineNumberMargin;;
+
+ if (lineNumberMargin != null) {
+ lineNumberMargin.SetValue(Control.ForegroundProperty, e.NewValue);
+ }
+ }
+ #endregion
+
+ #region TextBoxBase-like methods
+ /// <summary>
+ /// Appends text to the end of the document.
+ /// </summary>
+ public void AppendText(string textData)
+ {
+ var document = GetDocument();
+ document.Insert(document.TextLength, textData);
+ }
+
+ /// <summary>
+ /// Begins a group of document changes.
+ /// </summary>
+ public void BeginChange()
+ {
+ GetDocument().BeginUpdate();
+ }
+
+ /// <summary>
+ /// Copies the current selection to the clipboard.
+ /// </summary>
+ public void Copy()
+ {
+ Execute(ApplicationCommands.Copy);
+ }
+
+ /// <summary>
+ /// Removes the current selection and copies it to the clipboard.
+ /// </summary>
+ public void Cut()
+ {
+ Execute(ApplicationCommands.Cut);
+ }
+
+ /// <summary>
+ /// Begins a group of document changes and returns an object that ends the group of document
+ /// changes when it is disposed.
+ /// </summary>
+ public IDisposable DeclareChangeBlock()
+ {
+ return GetDocument().RunUpdate();
+ }
+
+ /// <summary>
+ /// Ends the current group of document changes.
+ /// </summary>
+ public void EndChange()
+ {
+ GetDocument().EndUpdate();
+ }
+
+ /// <summary>
+ /// Scrolls one line down.
+ /// </summary>
+ public void LineDown()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineDown();
+ }
+
+ /// <summary>
+ /// Scrolls to the left.
+ /// </summary>
+ public void LineLeft()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineLeft();
+ }
+
+ /// <summary>
+ /// Scrolls to the right.
+ /// </summary>
+ public void LineRight()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineRight();
+ }
+
+ /// <summary>
+ /// Scrolls one line up.
+ /// </summary>
+ public void LineUp()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineUp();
+ }
+
+ /// <summary>
+ /// Scrolls one page down.
+ /// </summary>
+ public void PageDown()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageDown();
+ }
+
+ /// <summary>
+ /// Scrolls one page up.
+ /// </summary>
+ public void PageUp()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageUp();
+ }
+
+ /// <summary>
+ /// Scrolls one page left.
+ /// </summary>
+ public void PageLeft()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageLeft();
+ }
+
+ /// <summary>
+ /// Scrolls one page right.
+ /// </summary>
+ public void PageRight()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageRight();
+ }
+
+ /// <summary>
+ /// Pastes the clipboard content.
+ /// </summary>
+ public void Paste()
+ {
+ Execute(ApplicationCommands.Paste);
+ }
+
+ /// <summary>
+ /// Redoes the most recent undone command.
+ /// </summary>
+ /// <returns>True is the redo operation was successful, false is the redo stack is empty.</returns>
+ public bool Redo()
+ {
+ if (CanExecute(ApplicationCommands.Redo)) {
+ Execute(ApplicationCommands.Redo);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Scrolls to the end of the document.
+ /// </summary>
+ public void ScrollToEnd()
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToEnd();
+ }
+
+ /// <summary>
+ /// Scrolls to the start of the document.
+ /// </summary>
+ public void ScrollToHome()
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToHome();
+ }
+
+ /// <summary>
+ /// Scrolls to the specified position in the document.
+ /// </summary>
+ public void ScrollToHorizontalOffset(double offset)
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToHorizontalOffset(offset);
+ }
+
+ /// <summary>
+ /// Scrolls to the specified position in the document.
+ /// </summary>
+ public void ScrollToVerticalOffset(double offset)
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToVerticalOffset(offset);
+ }
+
+ /// <summary>
+ /// Selects the entire text.
+ /// </summary>
+ public void SelectAll()
+ {
+ Execute(ApplicationCommands.SelectAll);
+ }
+
+ /// <summary>
+ /// Undoes the most recent command.
+ /// </summary>
+ /// <returns>True is the undo operation was successful, false is the undo stack is empty.</returns>
+ public bool Undo()
+ {
+ if (CanExecute(ApplicationCommands.Undo)) {
+ Execute(ApplicationCommands.Undo);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Gets if the most recent undone command can be redone.
+ /// </summary>
+ public bool CanRedo {
+ get { return CanExecute(ApplicationCommands.Redo); }
+ }
+
+ /// <summary>
+ /// Gets if the most recent command can be undone.
+ /// </summary>
+ public bool CanUndo {
+ get { return CanExecute(ApplicationCommands.Undo); }
+ }
+
+ /// <summary>
+ /// Gets the vertical size of the document.
+ /// </summary>
+ public double ExtentHeight {
+ get {
+ return scrollViewer != null ? scrollViewer.ExtentHeight : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal size of the current document region.
+ /// </summary>
+ public double ExtentWidth {
+ get {
+ return scrollViewer != null ? scrollViewer.ExtentWidth : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal size of the viewport.
+ /// </summary>
+ public double ViewportHeight {
+ get {
+ return scrollViewer != null ? scrollViewer.ViewportHeight : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal size of the viewport.
+ /// </summary>
+ public double ViewportWidth {
+ get {
+ return scrollViewer != null ? scrollViewer.ViewportWidth : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the vertical scroll position.
+ /// </summary>
+ public double VerticalOffset {
+ get {
+ return scrollViewer != null ? scrollViewer.VerticalOffset : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal scroll position.
+ /// </summary>
+ public double HorizontalOffset {
+ get {
+ return scrollViewer != null ? scrollViewer.HorizontalOffset : 0;
+ }
+ }
+ #endregion
+
+ #region TextBox methods
+ /// <summary>
+ /// Gets/Sets the selected text.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string SelectedText {
+ get {
+ TextArea textArea = this.TextArea;
+ // We'll get the text from the whole surrounding segment.
+ // This is done to ensure that SelectedText.Length == SelectionLength.
+ if (textArea != null && textArea.Document != null && !textArea.Selection.IsEmpty)
+ return textArea.Document.GetText(textArea.Selection.SurroundingSegment);
+ else
+ return string.Empty;
+ }
+ set {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ TextArea textArea = this.TextArea;
+ if (textArea != null && textArea.Document != null) {
+ int offset = this.SelectionStart;
+ int length = this.SelectionLength;
+ textArea.Document.Replace(offset, length, value);
+ // keep inserted text selected
+ textArea.Selection = SimpleSelection.Create(textArea, offset, offset + value.Length);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the caret position.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int CaretOffset {
+ get {
+ TextArea textArea = this.TextArea;
+ if (textArea != null)
+ return textArea.Caret.Offset;
+ else
+ return 0;
+ }
+ set {
+ TextArea textArea = this.TextArea;
+ if (textArea != null)
+ textArea.Caret.Offset = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the start position of the selection.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int SelectionStart {
+ get {
+ TextArea textArea = this.TextArea;
+ if (textArea != null) {
+ if (textArea.Selection.IsEmpty)
+ return textArea.Caret.Offset;
+ else
+ return textArea.Selection.SurroundingSegment.Offset;
+ } else {
+ return 0;
+ }
+ }
+ set {
+ Select(value, SelectionLength);
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the length of the selection.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int SelectionLength {
+ get {
+ TextArea textArea = this.TextArea;
+ if (textArea != null && !textArea.Selection.IsEmpty)
+ return textArea.Selection.SurroundingSegment.Length;
+ else
+ return 0;
+ }
+ set {
+ Select(SelectionStart, value);
+ }
+ }
+
+ /// <summary>
+ /// Selects the specified text section.
+ /// </summary>
+ public void Select(int start, int length)
+ {
+ int documentLength = Document != null ? Document.TextLength : 0;
+ if (start < 0 || start > documentLength)
+ throw new ArgumentOutOfRangeException("start", start, "Value must be between 0 and " + documentLength);
+ if (length < 0 || start + length > documentLength)
+ throw new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (documentLength - length));
+ textArea.Selection = SimpleSelection.Create(textArea, start, start + length);
+ textArea.Caret.Offset = start + length;
+ }
+
+ /// <summary>
+ /// Gets the number of lines in the document.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int LineCount {
+ get {
+ TextDocument document = this.Document;
+ if (document != null)
+ return document.LineCount;
+ else
+ return 1;
+ }
+ }
+
+ /// <summary>
+ /// Clears the text.
+ /// </summary>
+ public void Clear()
+ {
+ this.Text = string.Empty;
+ }
+ #endregion
+
+ #region Loading from stream
+ /// <summary>
+ /// Loads the text from the stream, auto-detecting the encoding.
+ /// </summary>
+ /// <remarks>
+ /// This method sets <see cref="IsModified"/> to false.
+ /// </remarks>
+ public void Load(Stream stream)
+ {
+ using (StreamReader reader = FileReader.OpenStream(stream, this.Encoding ?? Encoding.UTF8)) {
+ this.Text = reader.ReadToEnd();
+ this.Encoding = reader.CurrentEncoding; // assign encoding after ReadToEnd() so that the StreamReader can autodetect the encoding
+ }
+ this.IsModified = false;
+ }
+
+ /// <summary>
+ /// Loads the text from the stream, auto-detecting the encoding.
+ /// </summary>
+ public void Load(string fileName)
+ {
+ if (fileName == null)
+ throw new ArgumentNullException("fileName");
+ using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
+ Load(fs);
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the encoding used when the file is saved.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Encoding Encoding { get; set; }
+
+ /// <summary>
+ /// Saves the text to the stream.
+ /// </summary>
+ /// <remarks>
+ /// This method sets <see cref="IsModified"/> to false.
+ /// </remarks>
+ public void Save(Stream stream)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+ StreamWriter writer = new StreamWriter(stream, this.Encoding ?? Encoding.UTF8);
+ writer.Write(this.Text);
+ writer.Flush();
+ // do not close the stream
+ this.IsModified = false;
+ }
+
+ /// <summary>
+ /// Saves the text to the file.
+ /// </summary>
+ public void Save(string fileName)
+ {
+ if (fileName == null)
+ throw new ArgumentNullException("fileName");
+ using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) {
+ Save(fs);
+ }
+ }
+ #endregion
+
+ #region MouseHover events
+ /// <summary>
+ /// The PreviewMouseHover event.
+ /// </summary>
+ public static readonly RoutedEvent PreviewMouseHoverEvent =
+ TextView.PreviewMouseHoverEvent.AddOwner(typeof(TextEditor));
+
+ /// <summary>
+ /// The MouseHover event.
+ /// </summary>
+ public static readonly RoutedEvent MouseHoverEvent =
+ TextView.MouseHoverEvent.AddOwner(typeof(TextEditor));
+
+
+ /// <summary>
+ /// The PreviewMouseHoverStopped event.
+ /// </summary>
+ public static readonly RoutedEvent PreviewMouseHoverStoppedEvent =
+ TextView.PreviewMouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
+
+ /// <summary>
+ /// The MouseHoverStopped event.
+ /// </summary>
+ public static readonly RoutedEvent MouseHoverStoppedEvent =
+ TextView.MouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
+
+
+ /// <summary>
+ /// Occurs when the mouse has hovered over a fixed location for some time.
+ /// </summary>
+ public event MouseEventHandler PreviewMouseHover {
+ add { AddHandler(PreviewMouseHoverEvent, value); }
+ remove { RemoveHandler(PreviewMouseHoverEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse has hovered over a fixed location for some time.
+ /// </summary>
+ public event MouseEventHandler MouseHover {
+ add { AddHandler(MouseHoverEvent, value); }
+ remove { RemoveHandler(MouseHoverEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse had previously hovered but now started moving again.
+ /// </summary>
+ public event MouseEventHandler PreviewMouseHoverStopped {
+ add { AddHandler(PreviewMouseHoverStoppedEvent, value); }
+ remove { RemoveHandler(PreviewMouseHoverStoppedEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse had previously hovered but now started moving again.
+ /// </summary>
+ public event MouseEventHandler MouseHoverStopped {
+ add { AddHandler(MouseHoverStoppedEvent, value); }
+ remove { RemoveHandler(MouseHoverStoppedEvent, value); }
+ }
+ #endregion
+
+ #region ScrollBarVisibility
+ /// <summary>
+ /// Dependency property for <see cref="HorizontalScrollBarVisibility"/>
+ /// </summary>
+ public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = ScrollViewer.HorizontalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
+
+ /// <summary>
+ /// Gets/Sets the horizontal scroll bar visibility.
+ /// </summary>
+ public ScrollBarVisibility HorizontalScrollBarVisibility {
+ get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); }
+ set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="VerticalScrollBarVisibility"/>
+ /// </summary>
+ public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
+
+ /// <summary>
+ /// Gets/Sets the vertical scroll bar visibility.
+ /// </summary>
+ public ScrollBarVisibility VerticalScrollBarVisibility {
+ get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); }
+ set { SetValue(VerticalScrollBarVisibilityProperty, value); }
+ }
+ #endregion
+
+ object IServiceProvider.GetService(Type serviceType)
+ {
+ return textArea.GetService(serviceType);
+ }
+
+ /// <summary>
+ /// Gets the text view position from a point inside the editor.
+ /// </summary>
+ /// <param name="point">The position, relative to top left
+ /// corner of TextEditor control</param>
+ /// <returns>The text view position, or null if the point is outside the document.</returns>
+ public TextViewPosition? GetPositionFromPoint(Point point)
+ {
+ if (this.Document == null)
+ return null;
+ TextView textView = this.TextArea.TextView;
+ return textView.GetPosition(TranslatePoint(point, textView) + textView.ScrollOffset);
+ }
+
+ /// <summary>
+ /// Scrolls to the specified line.
+ /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
+ /// </summary>
+ public void ScrollToLine(int line)
+ {
+ ScrollTo(line, -1);
+ }
+
+ /// <summary>
+ /// Scrolls to the specified line/column.
+ /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
+ /// </summary>
+ public void ScrollTo(int line, int column)
+ {
+ const double MinimumScrollPercentage = 0.3;
+
+ TextView textView = textArea.TextView;
+ TextDocument document = textView.Document;
+ if (scrollViewer != null && document != null) {
+ if (line < 1)
+ line = 1;
+ if (line > document.LineCount)
+ line = document.LineCount;
+
+ IScrollInfo scrollInfo = textView;
+ if (!scrollInfo.CanHorizontallyScroll) {
+ // Word wrap is enabled. Ensure that we have up-to-date info about line height so that we scroll
+ // to the correct position.
+ // This avoids that the user has to repeat the ScrollTo() call several times when there are very long lines.
+ VisualLine vl = textView.GetOrConstructVisualLine(document.GetLineByNumber(line));
+ double remainingHeight = scrollViewer.ViewportHeight / 2;
+ while (remainingHeight > 0) {
+ DocumentLine prevLine = vl.FirstDocumentLine.PreviousLine;
+ if (prevLine == null)
+ break;
+ vl = textView.GetOrConstructVisualLine(prevLine);
+ remainingHeight -= vl.Height;
+ }
+ }
+
+ Point p = textArea.TextView.GetVisualPosition(new TextViewPosition(line, Math.Max(1, column)), VisualYPosition.LineMiddle);
+ double verticalPos = p.Y - scrollViewer.ViewportHeight / 2;
+ if (Math.Abs(verticalPos - scrollViewer.VerticalOffset) > MinimumScrollPercentage * scrollViewer.ViewportHeight) {
+ scrollViewer.ScrollToVerticalOffset(Math.Max(0, verticalPos));
+ }
+ if (column > 0) {
+ if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2) {
+ double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2);
+ if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth) {
+ scrollViewer.ScrollToHorizontalOffset(horizontalPos);
+ }
+ } else {
+ scrollViewer.ScrollToHorizontalOffset(0);
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.xaml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.xaml
new file mode 100644
index 000000000..c7d29b4e1
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditor.xaml
@@ -0,0 +1,79 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:AvalonEdit="clr-namespace:Tango.Scripting.Editors"
+ xmlns:editing="clr-namespace:Tango.Scripting.Editors.Editing"
+>
+ <Style TargetType="{x:Type AvalonEdit:TextEditor}">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
+ <Setter Property="FlowDirection" Value="LeftToRight"/> <!-- AvalonEdit does not support RTL, so ensure we use LTR by default -->
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type AvalonEdit:TextEditor}">
+ <ScrollViewer
+ Focusable="False"
+ Name="PART_ScrollViewer"
+ CanContentScroll="True"
+ VerticalScrollBarVisibility="{TemplateBinding VerticalScrollBarVisibility}"
+ HorizontalScrollBarVisibility="{TemplateBinding HorizontalScrollBarVisibility}"
+ Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TextArea}"
+ VerticalContentAlignment="Top"
+ HorizontalContentAlignment="Left"
+ Background="{TemplateBinding Background}"
+ Padding="{TemplateBinding Padding}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ />
+ <ControlTemplate.Triggers>
+ <Trigger Property="WordWrap"
+ Value="True">
+ <Setter TargetName="PART_ScrollViewer"
+ Property="HorizontalScrollBarVisibility"
+ Value="Disabled" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style TargetType="{x:Type editing:TextArea}">
+ <Setter Property="SelectionCornerRadius" Value="0"></Setter>
+ <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
+ <Setter Property="SelectionBrush">
+ <Setter.Value>
+ <SolidColorBrush
+ Color="#3F8FD6"
+ Opacity="0.5"/>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="SelectionBorder">
+ <Setter.Value>
+ <Pen Brush="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"
+ Thickness="0"/>
+ </Setter.Value>
+ </Setter>
+ <!--<Setter Property="SelectionForeground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>-->
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type editing:TextArea}">
+ <DockPanel Focusable="False">
+ <ItemsControl DockPanel.Dock="Left"
+ Focusable="False"
+ ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=LeftMargins}">
+ <ItemsControl.ItemsPanel>
+ <ItemsPanelTemplate>
+ <StackPanel Orientation="Horizontal" />
+ </ItemsPanelTemplate>
+ </ItemsControl.ItemsPanel>
+ </ItemsControl>
+ <ContentPresenter
+ Panel.ZIndex="-1"
+ Focusable="False"
+ Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TextView}"/>
+ </DockPanel>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+</ResourceDictionary> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorAutomationPeer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorAutomationPeer.cs
new file mode 100644
index 000000000..b32ebf224
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorAutomationPeer.cs
@@ -0,0 +1,65 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows.Automation;
+using System.Windows.Automation.Peers;
+using System.Windows.Automation.Provider;
+using System.Windows.Controls;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors
+{
+ /// <summary>
+ /// Exposes <see cref="TextEditor"/> to automation.
+ /// </summary>
+ public class TextEditorAutomationPeer : FrameworkElementAutomationPeer, IValueProvider
+ {
+ /// <summary>
+ /// Creates a new TextEditorAutomationPeer instance.
+ /// </summary>
+ public TextEditorAutomationPeer(TextEditor owner) : base(owner)
+ {
+ Debug.WriteLine("TextEditorAutomationPeer was created");
+ }
+
+ private TextEditor TextEditor {
+ get { return (TextEditor)base.Owner; }
+ }
+
+ void IValueProvider.SetValue(string value)
+ {
+ this.TextEditor.Text = value;
+ }
+
+ string IValueProvider.Value {
+ get { return this.TextEditor.Text; }
+ }
+
+ bool IValueProvider.IsReadOnly {
+ get { return this.TextEditor.IsReadOnly; }
+ }
+
+ /// <inheritdoc/>
+ public override object GetPattern(PatternInterface patternInterface)
+ {
+ if (patternInterface == PatternInterface.Value)
+ return this;
+
+ if (patternInterface == PatternInterface.Scroll) {
+ ScrollViewer scrollViewer = this.TextEditor.ScrollViewer;
+ if (scrollViewer != null)
+ return UIElementAutomationPeer.CreatePeerForElement(scrollViewer);
+ }
+
+ return base.GetPattern(patternInterface);
+ }
+
+ internal void RaiseIsReadOnlyChanged(bool oldValue, bool newValue)
+ {
+ RaisePropertyChangedEvent(ValuePatternIdentifiers.IsReadOnlyProperty, Boxes.Box(oldValue), Boxes.Box(newValue));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorComponent.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorComponent.cs
new file mode 100644
index 000000000..0d333c1f2
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorComponent.cs
@@ -0,0 +1,40 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using Tango.Scripting.Editors.Document;
+using Tango.Scripting.Editors.Editing;
+using Tango.Scripting.Editors.Rendering;
+
+namespace Tango.Scripting.Editors
+{
+ /// <summary>
+ /// Represents a text editor control (<see cref="TextEditor"/>, <see cref="TextArea"/>
+ /// or <see cref="TextView"/>).
+ /// </summary>
+ public interface ITextEditorComponent : IServiceProvider
+ {
+ /// <summary>
+ /// Gets the document being edited.
+ /// </summary>
+ TextDocument Document { get; }
+
+ /// <summary>
+ /// Occurs when the Document property changes (when the text editor is connected to another
+ /// document - not when the document content changes).
+ /// </summary>
+ event EventHandler DocumentChanged;
+
+ /// <summary>
+ /// Gets the options of the text editor.
+ /// </summary>
+ TextEditorOptions Options { get; }
+
+ /// <summary>
+ /// Occurs when the Options property changes, or when an option inside the current option list
+ /// changes.
+ /// </summary>
+ event PropertyChangedEventHandler OptionChanged;
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorOptions.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorOptions.cs
new file mode 100644
index 000000000..3dcc99eab
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorOptions.cs
@@ -0,0 +1,434 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+using System.Reflection;
+using System.Text;
+
+namespace Tango.Scripting.Editors
+{
+ /// <summary>
+ /// A container for the text editor options.
+ /// </summary>
+ [Serializable]
+ public class TextEditorOptions : INotifyPropertyChanged
+ {
+ #region ctor
+ /// <summary>
+ /// Initializes an empty instance of TextEditorOptions.
+ /// </summary>
+ public TextEditorOptions()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of TextEditorOptions by copying all values
+ /// from <paramref name="options"/> to the new instance.
+ /// </summary>
+ public TextEditorOptions(TextEditorOptions options)
+ {
+ // get all the fields in the class
+ FieldInfo[] fields = typeof(TextEditorOptions).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+
+ // copy each value over to 'this'
+ foreach(FieldInfo fi in fields) {
+ if (!fi.IsNotSerialized)
+ fi.SetValue(this, fi.GetValue(options));
+ }
+ }
+ #endregion
+
+ #region PropertyChanged handling
+ /// <inheritdoc/>
+ [field: NonSerialized]
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ /// <summary>
+ /// Raises the PropertyChanged event.
+ /// </summary>
+ /// <param name="propertyName">The name of the changed property.</param>
+ protected void OnPropertyChanged(string propertyName)
+ {
+ OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
+ }
+
+ /// <summary>
+ /// Raises the PropertyChanged event.
+ /// </summary>
+ protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (PropertyChanged != null) {
+ PropertyChanged(this, e);
+ }
+ }
+ #endregion
+
+ #region ShowSpaces / ShowTabs / ShowEndOfLine / ShowBoxForControlCharacters
+ bool showSpaces;
+
+ /// <summary>
+ /// Gets/Sets whether to show · for spaces.
+ /// </summary>
+ /// <remarks>The default value is <c>false</c>.</remarks>
+ [DefaultValue(false)]
+ public virtual bool ShowSpaces {
+ get { return showSpaces; }
+ set {
+ if (showSpaces != value) {
+ showSpaces = value;
+ OnPropertyChanged("ShowSpaces");
+ }
+ }
+ }
+
+ bool showTabs;
+
+ /// <summary>
+ /// Gets/Sets whether to show » for tabs.
+ /// </summary>
+ /// <remarks>The default value is <c>false</c>.</remarks>
+ [DefaultValue(false)]
+ public virtual bool ShowTabs {
+ get { return showTabs; }
+ set {
+ if (showTabs != value) {
+ showTabs = value;
+ OnPropertyChanged("ShowTabs");
+ }
+ }
+ }
+
+ bool showEndOfLine;
+
+ /// <summary>
+ /// Gets/Sets whether to show ¶ at the end of lines.
+ /// </summary>
+ /// <remarks>The default value is <c>false</c>.</remarks>
+ [DefaultValue(false)]
+ public virtual bool ShowEndOfLine {
+ get { return showEndOfLine; }
+ set {
+ if (showEndOfLine != value) {
+ showEndOfLine = value;
+ OnPropertyChanged("ShowEndOfLine");
+ }
+ }
+ }
+
+ bool showBoxForControlCharacters = true;
+
+ /// <summary>
+ /// Gets/Sets whether to show a box with the hex code for control characters.
+ /// </summary>
+ /// <remarks>The default value is <c>true</c>.</remarks>
+ [DefaultValue(true)]
+ public virtual bool ShowBoxForControlCharacters {
+ get { return showBoxForControlCharacters; }
+ set {
+ if (showBoxForControlCharacters != value) {
+ showBoxForControlCharacters = value;
+ OnPropertyChanged("ShowBoxForControlCharacters");
+ }
+ }
+ }
+ #endregion
+
+ #region EnableHyperlinks
+ bool enableHyperlinks = true;
+
+ /// <summary>
+ /// Gets/Sets whether to enable clickable hyperlinks in the editor.
+ /// </summary>
+ /// <remarks>The default value is <c>true</c>.</remarks>
+ [DefaultValue(true)]
+ public virtual bool EnableHyperlinks {
+ get { return enableHyperlinks; }
+ set {
+ if (enableHyperlinks != value) {
+ enableHyperlinks = value;
+ OnPropertyChanged("EnableHyperlinks");
+ }
+ }
+ }
+
+ bool enableEmailHyperlinks = true;
+
+ /// <summary>
+ /// Gets/Sets whether to enable clickable hyperlinks for e-mail addresses in the editor.
+ /// </summary>
+ /// <remarks>The default value is <c>true</c>.</remarks>
+ [DefaultValue(true)]
+ public virtual bool EnableEmailHyperlinks {
+ get { return enableEmailHyperlinks; }
+ set {
+ if (enableEmailHyperlinks != value) {
+ enableEmailHyperlinks = value;
+ OnPropertyChanged("EnableEMailHyperlinks");
+ }
+ }
+ }
+
+ bool requireControlModifierForHyperlinkClick = true;
+
+ /// <summary>
+ /// Gets/Sets whether the user needs to press Control to click hyperlinks.
+ /// The default value is true.
+ /// </summary>
+ /// <remarks>The default value is <c>true</c>.</remarks>
+ [DefaultValue(true)]
+ public virtual bool RequireControlModifierForHyperlinkClick {
+ get { return requireControlModifierForHyperlinkClick; }
+ set {
+ if (requireControlModifierForHyperlinkClick != value) {
+ requireControlModifierForHyperlinkClick = value;
+ OnPropertyChanged("RequireControlModifierForHyperlinkClick");
+ }
+ }
+ }
+ #endregion
+
+ #region TabSize / IndentationSize / ConvertTabsToSpaces / GetIndentationString
+ // I'm using '_' prefixes for the fields here to avoid confusion with the local variables
+ // in the methods below.
+ // The fields should be accessed only by their property - the fields might not be used
+ // if someone overrides the property.
+
+ int _indentationSize = 4;
+
+ /// <summary>
+ /// Gets/Sets the width of one indentation unit.
+ /// </summary>
+ /// <remarks>The default value is 4.</remarks>
+ [DefaultValue(4)]
+ public virtual int IndentationSize {
+ get { return _indentationSize; }
+ set {
+ if (value < 1)
+ throw new ArgumentOutOfRangeException("value", value, "value must be positive");
+ // sanity check; a too large value might cause WPF to crash internally much later
+ // (it only crashed in the hundred thousands for me; but might crash earlier with larger fonts)
+ if (value > 1000)
+ throw new ArgumentOutOfRangeException("value", value, "indentation size is too large");
+ if (_indentationSize != value) {
+ _indentationSize = value;
+ OnPropertyChanged("IndentationSize");
+ OnPropertyChanged("IndentationString");
+ }
+ }
+ }
+
+ bool _convertTabsToSpaces;
+
+ /// <summary>
+ /// Gets/Sets whether to use spaces for indentation instead of tabs.
+ /// </summary>
+ /// <remarks>The default value is <c>false</c>.</remarks>
+ [DefaultValue(false)]
+ public virtual bool ConvertTabsToSpaces {
+ get { return _convertTabsToSpaces; }
+ set {
+ if (_convertTabsToSpaces != value) {
+ _convertTabsToSpaces = value;
+ OnPropertyChanged("ConvertTabsToSpaces");
+ OnPropertyChanged("IndentationString");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the text used for indentation.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
+ [Browsable(false)]
+ public string IndentationString {
+ get { return GetIndentationString(1); }
+ }
+
+ /// <summary>
+ /// Gets text required to indent from the specified <paramref name="column"/> to the next indentation level.
+ /// </summary>
+ public virtual string GetIndentationString(int column)
+ {
+ if (column < 1)
+ throw new ArgumentOutOfRangeException("column", column, "Value must be at least 1.");
+ int indentationSize = this.IndentationSize;
+ if (ConvertTabsToSpaces) {
+ return new string(' ', indentationSize - ((column - 1) % indentationSize));
+ } else {
+ return "\t";
+ }
+ }
+ #endregion
+
+ bool cutCopyWholeLine = true;
+
+ /// <summary>
+ /// Gets/Sets whether copying without a selection copies the whole current line.
+ /// </summary>
+ [DefaultValue(true)]
+ public virtual bool CutCopyWholeLine {
+ get { return cutCopyWholeLine; }
+ set {
+ if (cutCopyWholeLine != value) {
+ cutCopyWholeLine = value;
+ OnPropertyChanged("CutCopyWholeLine");
+ }
+ }
+ }
+
+ bool allowScrollBelowDocument;
+
+ /// <summary>
+ /// Gets/Sets whether the user can scroll below the bottom of the document.
+ /// The default value is false; but it a good idea to set this property to true when using folding.
+ /// </summary>
+ [DefaultValue(false)]
+ public virtual bool AllowScrollBelowDocument {
+ get { return allowScrollBelowDocument; }
+ set {
+ if (allowScrollBelowDocument != value) {
+ allowScrollBelowDocument = value;
+ OnPropertyChanged("AllowScrollBelowDocument");
+ }
+ }
+ }
+
+ double wordWrapIndentation = 0;
+
+ /// <summary>
+ /// Gets/Sets the indentation used for all lines except the first when word-wrapping.
+ /// The default value is 0.
+ /// </summary>
+ [DefaultValue(0.0)]
+ public virtual double WordWrapIndentation {
+ get { return wordWrapIndentation; }
+ set {
+ if (double.IsNaN(value) || double.IsInfinity(value))
+ throw new ArgumentOutOfRangeException("value", value, "value must not be NaN/infinity");
+ if (value != wordWrapIndentation) {
+ wordWrapIndentation = value;
+ OnPropertyChanged("WordWrapIndentation");
+ }
+ }
+ }
+
+ bool inheritWordWrapIndentation = true;
+
+ /// <summary>
+ /// Gets/Sets whether the indentation is inherited from the first line when word-wrapping.
+ /// The default value is true.
+ /// </summary>
+ /// <remarks>When combined with <see cref="WordWrapIndentation"/>, the inherited indentation is added to the word wrap indentation.</remarks>
+ [DefaultValue(true)]
+ public virtual bool InheritWordWrapIndentation {
+ get { return inheritWordWrapIndentation; }
+ set {
+ if (value != inheritWordWrapIndentation) {
+ inheritWordWrapIndentation = value;
+ OnPropertyChanged("InheritWordWrapIndentation");
+ }
+ }
+ }
+
+ bool enableRectangularSelection = true;
+
+ /// <summary>
+ /// Enables rectangular selection (press ALT and select a rectangle)
+ /// </summary>
+ [DefaultValue(true)]
+ public bool EnableRectangularSelection {
+ get { return enableRectangularSelection; }
+ set {
+ if (enableRectangularSelection != value) {
+ enableRectangularSelection = value;
+ OnPropertyChanged("EnableRectangularSelection");
+ }
+ }
+ }
+
+ bool enableTextDragDrop = true;
+
+ /// <summary>
+ /// Enable dragging text within the text area.
+ /// </summary>
+ [DefaultValue(true)]
+ public bool EnableTextDragDrop {
+ get { return enableTextDragDrop; }
+ set {
+ if (enableTextDragDrop != value) {
+ enableTextDragDrop = value;
+ OnPropertyChanged("EnableTextDragDrop");
+ }
+ }
+ }
+
+ bool enableVirtualSpace;
+
+ /// <summary>
+ /// Gets/Sets whether the user can set the caret behind the line ending
+ /// (into "virtual space").
+ /// Note that virtual space is always used (independent from this setting)
+ /// when doing rectangle selections.
+ /// </summary>
+ [DefaultValue(false)]
+ public virtual bool EnableVirtualSpace {
+ get { return enableVirtualSpace; }
+ set {
+ if (enableVirtualSpace != value) {
+ enableVirtualSpace = value;
+ OnPropertyChanged("EnableVirtualSpace");
+ }
+ }
+ }
+
+ bool enableImeSupport = true;
+
+ /// <summary>
+ /// Gets/Sets whether the support for Input Method Editors (IME)
+ /// for non-alphanumeric scripts (Chinese, Japanese, Korean, ...) is enabled.
+ /// </summary>
+ [DefaultValue(true)]
+ public virtual bool EnableImeSupport {
+ get { return enableImeSupport; }
+ set {
+ if (enableImeSupport != value) {
+ enableImeSupport = value;
+ OnPropertyChanged("EnableImeSupport");
+ }
+ }
+ }
+
+ bool showColumnRuler = false;
+
+ /// <summary>
+ /// Gets/Sets whether the column ruler should be shown.
+ /// </summary>
+ [DefaultValue(false)]
+ public virtual bool ShowColumnRuler {
+ get { return showColumnRuler; }
+ set {
+ if (showColumnRuler != value) {
+ showColumnRuler = value;
+ OnPropertyChanged("ShowColumnRuler");
+ }
+ }
+ }
+
+ int columnRulerPosition = 80;
+
+ /// <summary>
+ /// Gets/Sets where the column ruler should be shown.
+ /// </summary>
+ [DefaultValue(80)]
+ public virtual int ColumnRulerPosition {
+ get { return columnRulerPosition; }
+ set {
+ if (columnRulerPosition != value) {
+ columnRulerPosition = value;
+ OnPropertyChanged("ColumnRulerPosition");
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorWeakEventManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorWeakEventManager.cs
new file mode 100644
index 000000000..87057ead4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextEditorWeakEventManager.cs
@@ -0,0 +1,52 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using Tango.Scripting.Editors.Utils;
+using System;
+
+namespace Tango.Scripting.Editors
+{
+ /// <summary>
+ /// Contains weak event managers for <see cref="ITextEditorComponent"/>.
+ /// </summary>
+ public static class TextEditorWeakEventManager
+ {
+ /// <summary>
+ /// Weak event manager for the <see cref="ITextEditorComponent.DocumentChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class DocumentChanged : WeakEventManagerBase<DocumentChanged, ITextEditorComponent>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(ITextEditorComponent source)
+ {
+ source.DocumentChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(ITextEditorComponent source)
+ {
+ source.DocumentChanged -= DeliverEvent;
+ }
+ }
+
+ /// <summary>
+ /// Weak event manager for the <see cref="ITextEditorComponent.OptionChanged"/> event.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public sealed class OptionChanged : WeakEventManagerBase<OptionChanged, ITextEditorComponent>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(ITextEditorComponent source)
+ {
+ source.OptionChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(ITextEditorComponent source)
+ {
+ source.OptionChanged -= DeliverEvent;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextViewPosition.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextViewPosition.cs
new file mode 100644
index 000000000..ff7a78e31
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/TextViewPosition.cs
@@ -0,0 +1,158 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors
+{
+ /// <summary>
+ /// Represents a text location with a visual column.
+ /// </summary>
+ public struct TextViewPosition : IEquatable<TextViewPosition>
+ {
+ int line, column, visualColumn;
+
+ /// <summary>
+ /// Gets/Sets Location.
+ /// </summary>
+ public TextLocation Location {
+ get {
+ return new TextLocation(line, column);
+ }
+ set {
+ line = value.Line;
+ column = value.Column;
+ }
+ }
+
+ /// <summary>
+ /// Gets/Sets the line number.
+ /// </summary>
+ public int Line {
+ get { return line; }
+ set { line = value; }
+ }
+
+ /// <summary>
+ /// Gets/Sets the (text) column number.
+ /// </summary>
+ public int Column {
+ get { return column; }
+ set { column = value; }
+ }
+
+ /// <summary>
+ /// Gets/Sets the visual column number.
+ /// Can be -1 (meaning unknown visual column).
+ /// </summary>
+ public int VisualColumn {
+ get { return visualColumn; }
+ set { visualColumn = value; }
+ }
+
+ /// <summary>
+ /// Creates a new TextViewPosition instance.
+ /// </summary>
+ public TextViewPosition(int line, int column, int visualColumn)
+ {
+ this.line = line;
+ this.column = column;
+ this.visualColumn = visualColumn;
+ }
+
+ /// <summary>
+ /// Creates a new TextViewPosition instance.
+ /// </summary>
+ public TextViewPosition(int line, int column)
+ : this(line, column, -1)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new TextViewPosition instance.
+ /// </summary>
+ public TextViewPosition(TextLocation location, int visualColumn)
+ {
+ this.line = location.Line;
+ this.column = location.Column;
+ this.visualColumn = visualColumn;
+ }
+
+ /// <summary>
+ /// Creates a new TextViewPosition instance.
+ /// </summary>
+ public TextViewPosition(TextLocation location)
+ : this(location, -1)
+ {
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture,
+ "[TextViewPosition Line={0} Column={1} VisualColumn={2}]",
+ this.line, this.column, this.visualColumn);
+ }
+
+ /// <summary>
+ /// Implicit conversion to <see cref="TextLocation"/>.
+ /// </summary>
+ [Obsolete("Use the Location property instead of the implicit conversion to TextLocation")]
+ public static implicit operator TextLocation(TextViewPosition position)
+ {
+ return new TextLocation(position.Line, position.Column);
+ }
+
+ #region Equals and GetHashCode implementation
+ // The code in this region is useful if you want to use this structure in collections.
+ // If you don't need it, you can just remove the region and the ": IEquatable<Struct1>" declaration.
+
+ /// <inheritdoc/>
+ public override bool Equals(object obj)
+ {
+ if (obj is TextViewPosition)
+ return Equals((TextViewPosition)obj); // use Equals method below
+ else
+ return false;
+ }
+
+ /// <inheritdoc/>
+ public override int GetHashCode()
+ {
+ int hashCode = 0;
+ unchecked {
+ hashCode += 1000000007 * Line.GetHashCode();
+ hashCode += 1000000009 * Column.GetHashCode();
+ hashCode += 1000000021 * VisualColumn.GetHashCode();
+ }
+ return hashCode;
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public bool Equals(TextViewPosition other)
+ {
+ return this.Line == other.Line && this.Column == other.Column && this.VisualColumn == other.VisualColumn;
+ }
+
+ /// <summary>
+ /// Equality test.
+ /// </summary>
+ public static bool operator ==(TextViewPosition left, TextViewPosition right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Inequality test.
+ /// </summary>
+ public static bool operator !=(TextViewPosition left, TextViewPosition right)
+ {
+ return !(left.Equals(right)); // use operator == and negate result
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Theme.xaml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Theme.xaml
new file mode 100644
index 000000000..4847b6a10
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Theme.xaml
@@ -0,0 +1,3066 @@
+<!--
+// (c) Copyright Microsoft Corporation.
+// This source is subject to Microsoft Public License (Ms-PL).
+// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
+// All other rights reserved.
+-->
+
+<ResourceDictionary
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
+
+ <!-- Main Colors -->
+
+ <Color x:Key="MainColor">#FF282828</Color>
+ <Color x:Key="HoverColor">#FF202020</Color>
+ <Color x:Key="DefaultColor">#FF9BB1C5</Color>
+ <SolidColorBrush x:Key="LightBrush" Color="#FF1B1B1B" />
+ <SolidColorBrush x:Key="ShadeBrush" Color="#FF282828" />
+
+ <Color x:Key="TopGradientSelectColor">#FF777777</Color>
+ <Color x:Key="BottomGradientSelectColor">#FF2E2E2E</Color>
+
+ <Color x:Key="BlackColor">#FF000000</Color>
+ <Color x:Key="WhiteColor">#FFFFFFFF</Color>
+
+ <SolidColorBrush x:Key="TextBrush" Color="#FFFFFFFF" />
+ <SolidColorBrush x:Key="NormalBrush" Color="{StaticResource MainColor}" />
+ <SolidColorBrush x:Key="NormalBorderBrush" Color="#FF333333" />
+ <SolidColorBrush x:Key="HoverBrush" Color="{StaticResource HoverColor}" />
+ <SolidColorBrush x:Key="GlyphBrush" Color="#FFD1D1D1" />
+
+ <LinearGradientBrush x:Key="PressedBrush" EndPoint="0.5,0.971" StartPoint="0.5,0.042">
+ <GradientStop Color="#4C000000" Offset="0" />
+ <GradientStop Color="#26FFFFFF" Offset="1" />
+ <GradientStop Color="#4C000000" Offset="0.467" />
+ <GradientStop Color="#26FFFFFF" Offset="0.479" />
+ </LinearGradientBrush>
+
+ <SolidColorBrush x:Key="ControlBackgroundBrush" Color="#FF333333" />
+
+ <LinearGradientBrush x:Key="SelectedBackgroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
+ <GradientStop Color="{StaticResource TopGradientSelectColor}" Offset="0" />
+ <GradientStop Color="{StaticResource BottomGradientSelectColor}" Offset="1" />
+ </LinearGradientBrush>
+
+ <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
+ <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#FF787878" />
+ <SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />
+ <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FF595959" />
+
+ <!-- EXPRESSION COMMON VALUES -->
+
+ <LinearGradientBrush x:Key="ShineBrush" EndPoint="0.5,0.971" StartPoint="0.5,0.042">
+ <GradientStop Color="#59FFFFFF" Offset="0" />
+ <GradientStop Color="#00FFFFFF" Offset="1" />
+ <GradientStop Color="#26FFFFFF" Offset="0.467" />
+ <GradientStop Color="#00FFFFFF" Offset="0.475" />
+ </LinearGradientBrush>
+
+ <LinearGradientBrush x:Key="HoverShineBrush" EndPoint="0.5,0.971" StartPoint="0.5,0.042">
+ <GradientStop Color="#4CFFFFFF" Offset="0" />
+ <GradientStop Color="#26FFFFFF" Offset="1" />
+ <GradientStop Color="#26FFFFFF" Offset="0.467" />
+ <GradientStop Color="#00FFFFFF" Offset="0.475" />
+ <GradientStop Color="#00FFFFFF" Offset="0.856" />
+ </LinearGradientBrush>
+
+ <LinearGradientBrush x:Key="PressedBorderBrush" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#444" Offset="0.0" />
+ <GradientStop Color="#888" Offset="1.0" />
+ </LinearGradientBrush>
+
+ <SolidColorBrush x:Key="DefaultedBorderBrush" Color="{StaticResource DefaultColor}" />
+ <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
+
+
+ <Style x:Key="NuclearButtonFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Border>
+ <Rectangle Margin="2" Stroke="#60000000" StrokeThickness="1" StrokeDashArray="1 2" />
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type Button}" BasedOn="{x:Null}">
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource NuclearButtonFocusVisual}" />
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template" Value="{DynamicResource ButtonTemplate}" />
+ </Style>
+
+ <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="FocusedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocussedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+
+ </ControlTemplate.Resources>
+ <Grid x:Name="Grid">
+ <Border x:Name="Background" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" />
+ <Border x:Name="HoverBorder" Opacity="0" Background="{StaticResource HoverBrush}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" />
+ <Border x:Name="HoverShineBorder" Opacity="0" Background="{StaticResource HoverShineBrush}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" />
+ <Border x:Name="PressedBorder" Opacity="0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" Background="{StaticResource PressedBrush}" />
+ <Border x:Name="ShineBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" Background="{StaticResource ShineBrush}" Opacity="1" />
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" />
+ <Border x:Name="FocusVisualElement" IsHitTestVisible="false" BorderBrush="{StaticResource HoverShineBrush}" BorderThickness="1" CornerRadius="3,3,3,3" Margin="1,1,1,1" Opacity="0" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsDefault" Value="True">
+ <Setter Property="BorderBrush" TargetName="ShineBorder" Value="{DynamicResource DefaultedBorderBrush}" />
+ </Trigger>
+ <Trigger Property="IsKeyboardFocused" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource FocussedOff}" x:Name="FocussedOff_BeginStoryboard" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource FocusedOn}" x:Name="FocusedOn_BeginStoryboard" />
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}" />
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" />
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="true" />
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Background" />
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="ShineBorder" />
+ <Setter Property="Opacity" TargetName="Grid" Value="0.5" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <Style x:Key="RadioButtonFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Border>
+ <Rectangle Margin="15,0,0,0" Stroke="#60000000" StrokeThickness="1" StrokeDashArray="1 2" />
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="CheckBoxFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Border>
+ <Rectangle Margin="15,0,0,0" Stroke="#60000000" StrokeThickness="1" StrokeDashArray="1 2" />
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type CheckBox}">
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource CheckBoxFocusVisual}" />
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template" Value="{DynamicResource CheckBoxTemplate}" />
+ </Style>
+
+ <ControlTemplate x:Key="CheckBoxTemplate" TargetType="{x:Type CheckBox}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="BackgroundOverlay" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="BackgroundOverlay" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="CheckedTrue">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="CheckIcon" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="CheckedFalse">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="CheckIcon" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="IndeterminateOn">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="IndeterminateIcon" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="IndeterminateOff">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="IndeterminateIcon" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocussedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentFocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocussedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentFocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <BulletDecorator Background="Transparent" x:Name="bulletDecorator">
+ <BulletDecorator.Bullet>
+ <Grid Width="16" Height="16">
+ <Rectangle Height="14" Margin="1" x:Name="Background" Width="14" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" />
+ <Rectangle Height="14" Margin="1" x:Name="BackgroundOverlay" Width="14" Opacity="0" Fill="{x:Null}" Stroke="{StaticResource HoverBrush}" StrokeThickness="2" />
+ <Rectangle Height="16" x:Name="PressedRectangle" Width="16" IsHitTestVisible="false" Opacity="0" Stroke="{StaticResource HoverBrush}" StrokeThickness="1" />
+ <Path Height="10" Margin="1,1,0,1.5" x:Name="CheckIcon" Width="10.5" Opacity="0" Fill="{StaticResource GlyphBrush}" Stretch="Fill" Data="M102.03442,598.79645 L105.22962,597.78918 L106.78825,600.42358 C106.78825,600.42358 108.51028,595.74304 110.21724,593.60419 C112.00967,591.35822 114.89314,591.42316 114.89314,591.42316 C114.89314,591.42316 112.67844,593.42645 111.93174,594.44464 C110.7449,596.06293 107.15683,604.13837 107.15683,604.13837 z" />
+ <Rectangle Height="8" x:Name="IndeterminateIcon" Width="10" Opacity="0" Fill="{StaticResource GlyphBrush}" />
+ <Rectangle Height="14" x:Name="DisabledVisualElement" Width="14" Opacity="0" Fill="{StaticResource DisabledBackgroundBrush}" />
+ <Rectangle Height="16" x:Name="ContentFocusVisualElement" Width="16" IsHitTestVisible="false" Opacity="0" Stroke="{StaticResource HoverShineBrush}" StrokeThickness="1" />
+ <Rectangle Height="12" x:Name="ShineRectangle" Width="14" IsHitTestVisible="false" Opacity="1" Stroke="{x:Null}" StrokeThickness="1" Fill="{StaticResource ShineBrush}" Margin="2,2,2,2" />
+ </Grid>
+ </BulletDecorator.Bullet>
+ <ContentPresenter RecognizesAccessKey="True" Margin="5,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" />
+ </BulletDecorator>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsFocused" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource FocussedOff}" x:Name="FocussedOff_BeginStoryboard" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource FocussedOn}" x:Name="FocussedOn_BeginStoryboard" />
+ </Trigger.EnterActions>
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsChecked" Value="True" />
+ <Condition Property="IsThreeState" Value="True" />
+ </MultiTrigger.Conditions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard x:Name="ThreeStateOn_BeginStoryboard" Storyboard="{StaticResource CheckedFalse}" />
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard x:Name="ThreeStateOff_BeginStoryboard" Storyboard="{StaticResource CheckedTrue}" />
+ </MultiTrigger.ExitActions>
+ </MultiTrigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsChecked" Value="{x:Null}" />
+ <Condition Property="IsThreeState" Value="True" />
+ </MultiTrigger.Conditions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard x:Name="ThreeStateOn1_BeginStoryboard" Storyboard="{StaticResource IndeterminateOn}" />
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard x:Name="ThreeStateOff1_BeginStoryboard" Storyboard="{StaticResource IndeterminateOff}" />
+ </MultiTrigger.ExitActions>
+ <Setter Property="Visibility" TargetName="CheckIcon" Value="Hidden" />
+ </MultiTrigger>
+ <Trigger Property="IsChecked" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource CheckedFalse}" x:Name="CheckedTrue_BeginStoryboard" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard x:Name="CheckedTrue_BeginStoryboard1" Storyboard="{StaticResource CheckedTrue}" />
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}" />
+ </Trigger.EnterActions>
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" />
+ </Trigger.ExitActions>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Fill" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Background" />
+ <Setter Property="Stroke" Value="{DynamicResource DisabledBorderBrush}" TargetName="Background" />
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ <Setter Property="Opacity" TargetName="bulletDecorator" Value="0.5" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <Style TargetType="{x:Type RadioButton}">
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource RadioButtonFocusVisual}" />
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template" Value="{DynamicResource RadioButtonTemplate}" />
+ <Style.Triggers>
+ </Style.Triggers>
+ </Style>
+
+ <ControlTemplate x:Key="RadioButtonTemplate" TargetType="{x:Type RadioButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="BackgroundOverlay" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="BackgroundOverlay" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedEllipse" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedEllipse" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="CheckedOn">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="CheckIcon" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="CheckedOff">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="CheckIcon" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocussedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentFocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocussedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentFocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <BulletDecorator Background="Transparent" x:Name="bulletDecorator">
+ <BulletDecorator.Bullet>
+ <Grid Width="16" Height="16">
+ <Ellipse Height="14" Margin="1" x:Name="Background" Width="14" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" />
+ <Ellipse Height="14" Margin="1" x:Name="BackgroundOverlay" Width="14" StrokeThickness="2" Stroke="{StaticResource HoverBrush}" Opacity="0" />
+ <Ellipse Height="14" Margin="1" x:Name="PressedEllipse" Width="14" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{StaticResource HoverBrush}" Opacity="0" />
+ <Ellipse Height="6" x:Name="CheckIcon" Width="6" Opacity="0" Fill="{StaticResource GlyphBrush}" />
+ <Ellipse Height="14" x:Name="DisabledVisualElement" Width="14" Opacity="0" Fill="{StaticResource DisabledBackgroundBrush}" />
+ <Ellipse Height="16" x:Name="ContentFocusVisualElement" Width="16" IsHitTestVisible="false" Opacity="0" Stroke="{StaticResource HoverShineBrush}" StrokeThickness="1" />
+ <Ellipse Height="12" Margin="2,2,2,2" x:Name="ShineEllipse" Width="12" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{x:Null}" Fill="{StaticResource ShineBrush}" />
+ </Grid>
+ </BulletDecorator.Bullet>
+ <ContentPresenter RecognizesAccessKey="True" Margin="5,0,0,0" />
+ </BulletDecorator>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsFocused" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource FocussedOff}" x:Name="FocussedOff_BeginStoryboard" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource FocussedOn}" x:Name="FocussedOn_BeginStoryboard" />
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsChecked" Value="false">
+
+ <Trigger.ExitActions>
+ <BeginStoryboard x:Name="CheckedOn_BeginStoryboard" Storyboard="{StaticResource CheckedOn}" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard x:Name="CheckedOff_BeginStoryboard" Storyboard="{StaticResource CheckedOff}" />
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsChecked" Value="True" />
+ <Trigger Property="IsMouseOver" Value="true">
+
+ <Trigger.ExitActions>
+ <BeginStoryboard x:Name="HoverOff_BeginStoryboard" Storyboard="{StaticResource HoverOff}" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}" />
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+
+ <Trigger.ExitActions>
+ <BeginStoryboard x:Name="PressedOff_BeginStoryboard" Storyboard="{StaticResource PressedOff}" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard x:Name="PressedOn_BeginStoryboard" Storyboard="{StaticResource PressedOn}" />
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Fill" TargetName="Background" Value="{DynamicResource DisabledBackgroundBrush}" />
+ <Setter Property="Stroke" TargetName="Background" Value="{DynamicResource DisabledBorderBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ <Setter Property="Opacity" TargetName="bulletDecorator" Value="0.5" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <Style d:IsControlPart="True" TargetType="{x:Type RepeatButton}" BasedOn="{x:Null}">
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type RepeatButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.8"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.3"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.3"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid>
+ <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" Opacity="1" />
+ <ContentPresenter HorizontalAlignment="Center" x:Name="ContentPresenter" VerticalAlignment="Center" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}" Opacity="0.3" Height="Auto" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsKeyboardFocused" Value="true" />
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Opacity" TargetName="ContentPresenter" Value="0.1"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="NuclearThumbStyle" d:IsControlPart="True" TargetType="{x:Type Thumb}" BasedOn="{x:Null}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Thumb}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.8"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.3"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.3"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid Margin="0,0,0,0" x:Name="Grid">
+ <Rectangle HorizontalAlignment="Stretch" x:Name="HoverRectangle" VerticalAlignment="Stretch" Width="Auto" Height="Auto" RadiusX="3" RadiusY="4" Stroke="{x:Null}" Margin="4.5,-2,4.5,-2" Opacity="0.3" MinHeight="10">
+ <Rectangle.Fill>
+ <SolidColorBrush Color="{DynamicResource WhiteColor}" />
+ </Rectangle.Fill>
+ </Rectangle>
+ <Rectangle HorizontalAlignment="Stretch" x:Name="PressedRectangle" VerticalAlignment="Stretch" Width="Auto" Height="Auto" RadiusX="3" RadiusY="4" Stroke="{x:Null}" Margin="4.5,-2,4.5,-2" Opacity="0.3" MinHeight="10">
+ <Rectangle.Fill>
+ <SolidColorBrush Color="{DynamicResource WhiteColor}" />
+ </Rectangle.Fill>
+ </Rectangle>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsFocused" Value="True" />
+ <Trigger Property="IsMouseOver" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="False" >
+ <Setter Property="Opacity" TargetName="Grid" Value="0.1"/>
+ </Trigger>
+ <Trigger Property="IsDragging" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="NuclearScrollRepeatButtonStyle" d:IsControlPart="True" TargetType="{x:Type RepeatButton}">
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="BorderBrush" Value="Transparent" />
+ <Setter Property="IsTabStop" Value="false" />
+ <Setter Property="Focusable" Value="false" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type RepeatButton}">
+ <Grid>
+ <Rectangle Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type ScrollBar}">
+ <Setter Property="Stylus.IsFlicksEnabled" Value="false" />
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ScrollBar}">
+ <Grid x:Name="GridRoot" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Background="{DynamicResource NormalBrush}">
+ <Grid.RowDefinitions>
+ <RowDefinition MaxHeight="18" />
+ <RowDefinition Height="0.00001*" />
+ <RowDefinition MaxHeight="18" />
+ </Grid.RowDefinitions>
+ <RepeatButton x:Name="DecreaseRepeat" Command="ScrollBar.LineUpCommand" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}">
+ <Grid Margin="0,0,0,0">
+ <Path Margin="4.742,3.997,4.946,5.327" VerticalAlignment="Stretch" Height="Auto" Fill="{DynamicResource GlyphBrush}" Stretch="Fill" Stroke="{DynamicResource GlyphBrush}" StrokeThickness="1" Data="M5.2422477,11.132184 L11.5544,11.132184 8.6412958,4.4969033 z" x:Name="DecreaseArrow" />
+ </Grid>
+ </RepeatButton>
+ <Track Grid.Row="1" x:Name="PART_Track" Orientation="Vertical" IsDirectionReversed="true">
+ <Track.Thumb>
+ <Thumb Style="{DynamicResource NuclearThumbStyle}" Background="{x:Null}" Foreground="{x:Null}" />
+ </Track.Thumb>
+ <Track.IncreaseRepeatButton>
+ <RepeatButton x:Name="PageUp" Style="{DynamicResource NuclearScrollRepeatButtonStyle}" Command="ScrollBar.PageDownCommand" />
+ </Track.IncreaseRepeatButton>
+ <Track.DecreaseRepeatButton>
+ <RepeatButton x:Name="PageDown" Style="{DynamicResource NuclearScrollRepeatButtonStyle}" Command="ScrollBar.PageUpCommand" />
+ </Track.DecreaseRepeatButton>
+ </Track>
+ <RepeatButton Grid.Row="2" x:Name="IncreaseRepeat" Command="ScrollBar.LineDownCommand">
+ <Grid>
+ <Path Margin="4.742,3.997,4.946,5.327" x:Name="IncreaseArrow" VerticalAlignment="Stretch" Height="Auto" Fill="{DynamicResource GlyphBrush}" Stretch="Fill" Stroke="{DynamicResource GlyphBrush}" StrokeThickness="1" Data="M5.2422477,11.132184 L11.5544,11.132184 8.6412958,4.4969033 z" RenderTransformOrigin="0.5,0.5">
+ <Path.RenderTransform>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1" />
+ <SkewTransform AngleX="0" AngleY="0" />
+ <RotateTransform Angle="180" />
+ <TranslateTransform X="0" Y="0" />
+ </TransformGroup>
+ </Path.RenderTransform>
+ </Path>
+ </Grid>
+ </RepeatButton>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="Orientation" Value="Horizontal">
+ <Setter Property="LayoutTransform" TargetName="GridRoot">
+ <Setter.Value>
+ <RotateTransform Angle="-90" />
+ </Setter.Value>
+ </Setter>
+ <Setter TargetName="PART_Track" Property="Orientation" Value="Vertical" />
+ <Setter Property="Command" Value="ScrollBar.LineLeftCommand" TargetName="DecreaseRepeat" />
+ <Setter Property="Command" Value="ScrollBar.LineRightCommand" TargetName="IncreaseRepeat" />
+ <Setter Property="Command" Value="ScrollBar.PageLeftCommand" TargetName="PageDown" />
+ <Setter Property="Command" Value="ScrollBar.PageRightCommand" TargetName="PageUp" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="NuclearScrollViewer" TargetType="{x:Type ScrollViewer}" BasedOn="{x:Null}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ScrollViewer}">
+ <Grid Background="{TemplateBinding Background}">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <ScrollContentPresenter Grid.Column="0" Grid.Row="0" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" />
+ <ScrollBar Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Grid.Column="0" Grid.Row="1" x:Name="PART_HorizontalScrollBar" Orientation="Horizontal" Value="{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Minimum="0" Maximum="{TemplateBinding ScrollableWidth}" AutomationProperties.AutomationId="HorizontalScrollBar" />
+ <ScrollBar Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Grid.Column="1" Grid.Row="0" x:Name="PART_VerticalScrollBar" Orientation="Vertical" Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Minimum="0" Maximum="{TemplateBinding ScrollableHeight}" AutomationProperties.AutomationId="VerticalScrollBar" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+
+
+
+
+ <Style x:Key="ExpanderHeaderFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Border>
+ <Rectangle SnapsToDevicePixels="true" Margin="0" Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1" />
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style x:Key="ExpanderDownHeaderStyle" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Border Padding="{TemplateBinding Padding}" Background="{DynamicResource NormalBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" BorderThickness="1,1,1,1" CornerRadius="2,2,2,2">
+ <Grid SnapsToDevicePixels="False" Background="Transparent">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="19" />
+ <ColumnDefinition Width="*" />
+ </Grid.ColumnDefinitions>
+ <Rectangle Margin="2,2,2,2" x:Name="Rectangle" Fill="{DynamicResource NormalBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="ShineRectangle" Fill="{DynamicResource ShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverRectangle" Fill="{DynamicResource HoverBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverShineRectangle" Fill="{DynamicResource HoverShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="PressedRectangle" Fill="{DynamicResource PressedBrush}" Stroke="{DynamicResource PressedBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Path HorizontalAlignment="Center" x:Name="Up_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" />
+ <Path Visibility="Collapsed" HorizontalAlignment="Center" x:Name="Down_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 4 L 4 0 L 8 4 Z" />
+ <ContentPresenter SnapsToDevicePixels="True" HorizontalAlignment="Left" Margin="4,2,0,2" VerticalAlignment="Center" Grid.Column="1" RecognizesAccessKey="True" />
+ </Grid>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Visibility" TargetName="Down_Arrow" Value="Visible" />
+ <Setter Property="Visibility" TargetName="Up_Arrow" Value="Collapsed" />
+
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="ExpanderRightHeaderStyle" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Border Padding="{TemplateBinding Padding}" Background="{DynamicResource NormalBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" BorderThickness="1,1,1,1" CornerRadius="2,2,2,2">
+ <Grid SnapsToDevicePixels="False" Background="Transparent">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="19" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Grid>
+ <Grid.LayoutTransform>
+ <TransformGroup>
+ <TransformGroup.Children>
+ <TransformCollection>
+ <RotateTransform Angle="-90" />
+ </TransformCollection>
+ </TransformGroup.Children>
+ </TransformGroup>
+ </Grid.LayoutTransform>
+ <Rectangle Margin="2,2,2,2" x:Name="Rectangle" Fill="{DynamicResource NormalBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="ShineRectangle" Fill="{DynamicResource ShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverRectangle" Fill="{DynamicResource HoverBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverShineRectangle" Fill="{DynamicResource HoverShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="PressedRectangle" Fill="{DynamicResource PressedBrush}" Stroke="{DynamicResource PressedBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Path HorizontalAlignment="Center" x:Name="Up_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" />
+ <Path Visibility="Collapsed" HorizontalAlignment="Center" x:Name="Down_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 4 L 4 0 L 8 4 Z" />
+ </Grid>
+ <ContentPresenter SnapsToDevicePixels="True" HorizontalAlignment="Center" Margin="2,4,4,0" VerticalAlignment="Top" Grid.Row="1" RecognizesAccessKey="True">
+ <ContentPresenter.LayoutTransform>
+ <TransformGroup>
+ <TransformGroup.Children>
+ <TransformCollection>
+ <RotateTransform Angle="-90" />
+ </TransformCollection>
+ </TransformGroup.Children>
+ </TransformGroup>
+ </ContentPresenter.LayoutTransform>
+ </ContentPresenter>
+ </Grid>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Visibility" TargetName="Up_Arrow" Value="Collapsed" />
+ <Setter Property="Visibility" TargetName="Down_Arrow" Value="Visible" />
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="ExpanderUpHeaderStyle" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Border Padding="{TemplateBinding Padding}" CornerRadius="2,2,2,2" BorderThickness="1,1,1,1" Background="{DynamicResource NormalBrush}" BorderBrush="{DynamicResource NormalBorderBrush}">
+ <Grid SnapsToDevicePixels="False" Background="Transparent">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="19" />
+ <ColumnDefinition Width="*" />
+ </Grid.ColumnDefinitions>
+ <Grid>
+ <Grid.LayoutTransform>
+ <TransformGroup>
+ <TransformGroup.Children>
+ <TransformCollection>
+ <RotateTransform Angle="180" />
+ </TransformCollection>
+ </TransformGroup.Children>
+ </TransformGroup>
+ </Grid.LayoutTransform>
+ <Rectangle Margin="2,2,2,2" x:Name="Rectangle" Fill="{DynamicResource NormalBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="ShineRectangle" Fill="{DynamicResource ShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverRectangle" Fill="{DynamicResource HoverBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverShineRectangle" Fill="{DynamicResource HoverShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="PressedRectangle" Fill="{DynamicResource PressedBrush}" Stroke="{DynamicResource PressedBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Path HorizontalAlignment="Center" x:Name="Up_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" />
+ <Path Visibility="Collapsed" HorizontalAlignment="Center" x:Name="Down_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 4 L 4 0 L 8 4 Z" />
+ </Grid>
+ <ContentPresenter SnapsToDevicePixels="True" HorizontalAlignment="Left" Margin="4,2,0,2" VerticalAlignment="Center" Grid.Column="1" RecognizesAccessKey="True" />
+ </Grid>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Visibility" TargetName="Up_Arrow" Value="Collapsed" />
+ <Setter Property="Visibility" TargetName="Down_Arrow" Value="Visible" />
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="ExpanderLeftHeaderStyle" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Border Padding="{TemplateBinding Padding}" CornerRadius="2,2,2,2" Background="{DynamicResource NormalBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" BorderThickness="1,1,1,1">
+ <Grid SnapsToDevicePixels="False" Background="Transparent">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="19" />
+ <RowDefinition Height="*" />
+ </Grid.RowDefinitions>
+ <Grid>
+ <Grid.LayoutTransform>
+ <TransformGroup>
+ <TransformGroup.Children>
+ <TransformCollection>
+ <RotateTransform Angle="90" />
+ </TransformCollection>
+ </TransformGroup.Children>
+ </TransformGroup>
+ </Grid.LayoutTransform>
+ <Rectangle Margin="2,2,2,2" x:Name="Rectangle" Fill="{DynamicResource NormalBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="ShineRectangle" Fill="{DynamicResource ShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverRectangle" Fill="{DynamicResource HoverBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="HoverShineRectangle" Fill="{DynamicResource HoverShineBrush}" Stroke="{DynamicResource NormalBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Rectangle Margin="2,2,2,2" x:Name="PressedRectangle" Fill="{DynamicResource PressedBrush}" Stroke="{DynamicResource PressedBorderBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" Opacity="0" />
+ <Path HorizontalAlignment="Center" x:Name="Up_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 0 L 4 4 L 8 0 Z" />
+ <Path Visibility="Collapsed" HorizontalAlignment="Center" x:Name="Down_Arrow" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 4 L 4 0 L 8 4 Z" />
+ </Grid>
+ <ContentPresenter SnapsToDevicePixels="True" HorizontalAlignment="Center" Margin="4,4,2,0" VerticalAlignment="Top" Grid.Row="1" RecognizesAccessKey="True">
+ <ContentPresenter.LayoutTransform>
+ <TransformGroup>
+ <TransformGroup.Children>
+ <TransformCollection>
+ <RotateTransform Angle="90" />
+ </TransformCollection>
+ </TransformGroup.Children>
+ </TransformGroup>
+ </ContentPresenter.LayoutTransform>
+ </ContentPresenter>
+ </Grid>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Visibility" TargetName="Up_Arrow" Value="Collapsed" />
+ <Setter Property="Visibility" TargetName="Down_Arrow" Value="Visible" />
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type Expander}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="HorizontalContentAlignment" Value="Stretch" />
+ <Setter Property="VerticalContentAlignment" Value="Stretch" />
+ <Setter Property="BorderBrush" Value="Transparent" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Expander}">
+ <Border SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,1" CornerRadius="3">
+ <DockPanel x:Name="dockPanel">
+ <ToggleButton FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}" FontStretch="{TemplateBinding FontStretch}" FontStyle="{TemplateBinding FontStyle}" FontWeight="{TemplateBinding FontWeight}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}" Margin="1,1,1,0" MinHeight="0" MinWidth="0" x:Name="HeaderSite" Style="{StaticResource ExpanderDownHeaderStyle}" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" IsChecked="{Binding Path=IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" DockPanel.Dock="Top" />
+ <Border Visibility="Collapsed" Background="{DynamicResource ShadeBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" BorderThickness="1,0,1,1" CornerRadius="3,3,3,3" x:Name="border" Margin="1,1,1,1">
+ <ContentPresenter Focusable="false" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1,1,1,1" x:Name="ExpandSite" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" DockPanel.Dock="Bottom" />
+ </Border>
+ </DockPanel>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsExpanded" Value="true">
+ <Setter Property="Visibility" TargetName="border" Value="Visible" />
+ </Trigger>
+ <Trigger Property="ExpandDirection" Value="Down" />
+ <Trigger Property="ExpandDirection" Value="Right">
+ <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Right" />
+ <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Left" />
+ <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderRightHeaderStyle}" />
+ </Trigger>
+ <Trigger Property="ExpandDirection" Value="Up">
+ <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Top" />
+ <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Bottom" />
+ <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderUpHeaderStyle}" />
+ </Trigger>
+ <Trigger Property="ExpandDirection" Value="Left">
+ <Setter Property="DockPanel.Dock" TargetName="ExpandSite" Value="Left" />
+ <Setter Property="DockPanel.Dock" TargetName="HeaderSite" Value="Right" />
+ <Setter Property="Style" TargetName="HeaderSite" Value="{StaticResource ExpanderLeftHeaderStyle}" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid x:Name="grid">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition />
+ <ColumnDefinition Width="20" />
+ </Grid.ColumnDefinitions>
+ <Rectangle Grid.ColumnSpan="2" HorizontalAlignment="Stretch" x:Name="Rectangle" VerticalAlignment="Stretch" Width="Auto" Height="Auto" RadiusX="3" RadiusY="3" Fill="{DynamicResource LightBrush}" Stroke="{DynamicResource NormalBorderBrush}" />
+ <Rectangle Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" RadiusX="3" RadiusY="3" Fill="#252525" Stroke="{DynamicResource NormalBorderBrush}" />
+ <Border Margin="2,2,2,2" Grid.Column="1" Background="{DynamicResource NormalBrush}" CornerRadius="3,3,3,3" x:Name="border" />
+ <Border Margin="2,2,2,2" Background="{DynamicResource HoverBrush}" CornerRadius="3,3,3,3" x:Name="HoverBorder" Grid.Column="1" Opacity="0" />
+ <Border Margin="2,2,2,2" Background="{DynamicResource HoverShineBrush}" CornerRadius="3,3,3,3" x:Name="HoverShineBorder" Grid.Column="1" Opacity="0" />
+ <Path Grid.Column="1" HorizontalAlignment="Center" x:Name="Arrow" VerticalAlignment="Center" Fill="{x:Null}" Data="M0.5,0.5 L3,6.5 5.5,0.5" Stroke="{DynamicResource GlyphBrush}" Margin="5,0,5,0" Height="7" StrokeThickness="2" Stretch="Fill" />
+ <Border Margin="2,2,2,2" Background="{DynamicResource ShineBrush}" CornerRadius="3,3,3,3" x:Name="ShineBorder" Grid.Column="1" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsChecked" Value="true" />
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ <Setter Property="Stroke" TargetName="Arrow" Value="{DynamicResource DisabledForegroundBrush}"/>
+ <Setter Property="Background" TargetName="border" Value="{DynamicResource DisabledBorderBrush}"/>
+ <Setter Property="Opacity" TargetName="grid" Value="0.8"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ <ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
+ <Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
+ <ControlTemplate.Triggers>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <Style TargetType="{x:Type ComboBox}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="Template" Value="{DynamicResource ComboBoxTemplate}" />
+ </Style>
+
+ <ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
+ <Grid x:Name="grid">
+ <ToggleButton Grid.Column="2" Template="{DynamicResource ComboBoxToggleButton}" x:Name="ToggleButton" Focusable="false" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" />
+ <ContentPresenter HorizontalAlignment="Left" Margin="3,3,23,3" x:Name="ContentSite" VerticalAlignment="Center" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" IsHitTestVisible="False" />
+ <TextBox Visibility="Hidden" Template="{DynamicResource ComboBoxTextBox}" HorizontalAlignment="Left" Margin="3,3,23,3" x:Name="PART_EditableTextBox" Style="{x:Null}" VerticalAlignment="Center" Focusable="True" Background="{DynamicResource LightBrush}" IsReadOnly="{TemplateBinding IsReadOnly}" BorderBrush="{DynamicResource NormalBorderBrush}" Foreground="{DynamicResource TextBrush}" />
+ <Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide">
+ <Grid MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}" x:Name="DropDown" SnapsToDevicePixels="True">
+ <Border x:Name="DropDownBorder" Background="{DynamicResource ControlBackgroundBrush}" CornerRadius="3,3,3,3" />
+ <ScrollViewer Margin="4,6,4,6" Style="{DynamicResource NuclearScrollViewer}" SnapsToDevicePixels="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="True" Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}">
+ <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
+ </ScrollViewer>
+ </Grid>
+ </Popup>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="HasItems" Value="false">
+ <Setter Property="MinHeight" Value="95" TargetName="DropDownBorder" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ <Setter Property="Opacity" TargetName="grid" Value="0.5" />
+ </Trigger>
+ <Trigger Property="IsGrouping" Value="true">
+ <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
+ </Trigger>
+ <Trigger Property="AllowsTransparency" SourceName="Popup" Value="true">
+ <Setter Property="Margin" Value="0,2,0,0" TargetName="DropDownBorder" />
+ </Trigger>
+ <Trigger Property="IsEditable" Value="true">
+ <Setter Property="IsTabStop" Value="false" />
+ <Setter Property="Visibility" Value="Visible" TargetName="PART_EditableTextBox" />
+ <Setter Property="Visibility" Value="Hidden" TargetName="ContentSite" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <Style d:IsControlPart="True" TargetType="{x:Type ComboBoxItem}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ComboBoxItem}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="SelectedOn">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="SelectedOff">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+
+ </ControlTemplate.Resources>
+ <Grid SnapsToDevicePixels="true" Margin="1,1,1,1">
+ <Rectangle x:Name="Background" IsHitTestVisible="False" Opacity="0.25" Fill="{StaticResource NormalBrush}" RadiusX="1" RadiusY="1" />
+ <Rectangle x:Name="HoverRectangle" IsHitTestVisible="False" Opacity="0" Fill="{StaticResource NormalBrush}" RadiusX="1" RadiusY="1" />
+ <Rectangle x:Name="SelectedRectangle" IsHitTestVisible="False" Opacity="0" Fill="{StaticResource SelectedBackgroundBrush}" RadiusX="1" RadiusY="1" />
+ <ContentPresenter Margin="5,2,0,2" x:Name="contentPresenter" VerticalAlignment="Center" />
+ <Rectangle x:Name="FocusVisualElement" Visibility="Collapsed" Stroke="{StaticResource HoverShineBrush}" StrokeThickness="1" RadiusX="1" RadiusY="1" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsHighlighted" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOff}" x:Name="SelectedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOn}" x:Name="SelectedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type ProgressBar}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ProgressBar}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="ValueChangedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PART_Track" Storyboard.TargetProperty="(UIElement.BitmapEffect).(OuterGlowBitmapEffect.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0" />
+ <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1" />
+ <SplineDoubleKeyFrame KeyTime="00:00:02" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="IndeterminateOn">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="IndeterminateGradientFill" Storyboard.TargetProperty="(Shape.Fill).(LinearGradientBrush.Transform).(TransformGroup.Children)[0].X" RepeatBehavior="Forever">
+ <SplineDoubleKeyFrame KeyTime="0" Value="0" />
+ <SplineDoubleKeyFrame KeyTime="00:00:2.4" Value="145" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid>
+ <Border x:Name="PART_Track" BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1" CornerRadius="0,0,0,0" Background="{DynamicResource LightBrush}">
+ <Border.BitmapEffect>
+ <OuterGlowBitmapEffect Opacity="0" GlowColor="{DynamicResource WhiteColor}" GlowSize="3" />
+ </Border.BitmapEffect>
+ </Border>
+ <Border HorizontalAlignment="Left" x:Name="PART_Indicator" BorderBrush="{DynamicResource NormalBorderBrush}" BorderThickness="1" CornerRadius="0,0,0,0">
+ <Border.Background>
+ <LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">
+ <GradientStop Color="#B2FFFFFF" Offset="0" />
+ <GradientStop Color="#C6FFFFFF" Offset="0.15" />
+ <GradientStop Color="#D1FFFFFF" Offset="0.275" />
+ <GradientStop Color="#C6FFFFFF" Offset="0.4" />
+ <GradientStop Color="#BFFFFFFF" Offset="0.65" />
+ <GradientStop Color="#A5FFFFFF" Offset="0.75" />
+ <GradientStop Color="#91FFFFFF" Offset="0.85" />
+ <GradientStop Color="#72FFFFFF" Offset="1" />
+ </LinearGradientBrush>
+ </Border.Background>
+ </Border>
+ <Grid x:Name="IndeterminateRoot" Visibility="Collapsed">
+ <Rectangle Margin="{TemplateBinding BorderThickness}" x:Name="IndeterminateSolidFill" Opacity="1" RenderTransformOrigin="0.5,0.5" RadiusX="2" RadiusY="2" Fill="{DynamicResource ControlBackgroundBrush}" Stroke="#FF448DCA" StrokeThickness="0" />
+ <Rectangle Margin="{TemplateBinding BorderThickness}" x:Name="IndeterminateGradientFill" RadiusX="2" RadiusY="2" StrokeThickness="1">
+ <Rectangle.Fill>
+ <LinearGradientBrush EndPoint="0.508,1.394" StartPoint="0.401,-0.394" SpreadMethod="Repeat">
+ <GradientStop Color="#00FFFFFF" />
+ <GradientStop Color="#FEA1A1A1" Offset="1" />
+ <GradientStop Color="#00FEFEFE" Offset="0.517" />
+ <GradientStop Color="#FEA1A1A1" Offset="0.517" />
+ <LinearGradientBrush.Transform>
+ <TransformGroup>
+ <TranslateTransform X="0" />
+ </TransformGroup>
+ </LinearGradientBrush.Transform>
+ </LinearGradientBrush>
+ </Rectangle.Fill>
+ </Rectangle>
+ </Grid>
+ <Border Background="{DynamicResource DisabledBackgroundBrush}" Opacity="0" BorderThickness="1,1,1,1" BorderBrush="{DynamicResource DisabledBorderBrush}" x:Name="border"/>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Opacity" TargetName="border" Value="0.8"/>
+ </Trigger>
+ <EventTrigger RoutedEvent="RangeBase.ValueChanged">
+ <BeginStoryboard Storyboard="{StaticResource ValueChangedOn}" />
+ </EventTrigger>
+ <Trigger Property="IsIndeterminate" Value="True">
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource IndeterminateOn}" />
+ </Trigger.EnterActions>
+ <Setter Property="Visibility" TargetName="IndeterminateRoot" Value="Visible"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type TextBox}">
+ <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
+ <Setter Property="FocusVisualStyle" Value="{x:Null}" />
+ <Setter Property="AllowDrop" Value="true" />
+ <Setter Property="Template" Value="{DynamicResource TextBoxTemplate}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}"/>
+ <Setter Property="BorderBrush" Value="#FF000000"/>
+ </Style>
+
+ <ControlTemplate x:Key="TextBoxTemplate" TargetType="{x:Type TextBox}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.5" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocusedOn">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocusedOff">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid>
+ <Border x:Name="Border" Opacity="1" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" Background="{TemplateBinding Background}">
+ <Grid>
+ <Border BorderThickness="1">
+ <ScrollViewer Margin="0" x:Name="PART_ContentHost" Style="{DynamicResource NuclearScrollViewer}" />
+ </Border>
+ </Grid>
+ </Border>
+ <Border x:Name="HoverBorder" IsHitTestVisible="False" Opacity="0" BorderBrush="{StaticResource GlyphBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" />
+ <Border x:Name="FocusVisualElement" IsHitTestVisible="False" Opacity="0" BorderBrush="{StaticResource HoverShineBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" />
+
+
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsFocused" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource FocusedOff}" x:Name="FocusedOff_BeginStoryboard" />
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource FocusedOn}" x:Name="FocusedOn_BeginStoryboard" />
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard" />
+ </MultiTrigger.ExitActions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}" />
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsMouseOver" Value="True" />
+ <Condition Property="IsFocused" Value="False" />
+ </MultiTrigger.Conditions>
+
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" TargetName="Border" Value="{DynamicResource DisabledBackgroundBrush}" />
+ <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource DisabledBorderBrush}" />
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+
+ <Style TargetType="{x:Type PasswordBox}">
+ <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
+ <Setter Property="FocusVisualStyle" Value="{x:Null}" />
+ <Setter Property="AllowDrop" Value="true" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type PasswordBox}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.5" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocusedOn">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocusedOff">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid>
+ <Border x:Name="Border" Opacity="1" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" Background="{TemplateBinding Background}">
+ <Grid>
+ <Border BorderThickness="1">
+ <ScrollViewer Margin="0" x:Name="PART_ContentHost" Style="{DynamicResource NuclearScrollViewer}" />
+ </Border>
+ </Grid>
+ </Border>
+ <Border x:Name="HoverBorder" IsHitTestVisible="False" Opacity="0" BorderBrush="{StaticResource GlyphBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" />
+ <Border x:Name="FocusVisualElement" IsHitTestVisible="False" Opacity="0" BorderBrush="{StaticResource HoverShineBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" />
+
+
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsFocused" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource FocusedOff}" x:Name="FocusedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource FocusedOn}" x:Name="FocusedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </MultiTrigger.ExitActions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsMouseOver" Value="True" />
+ <Condition Property="IsFocused" Value="False" />
+ </MultiTrigger.Conditions>
+
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" TargetName="Border" Value="{DynamicResource DisabledBackgroundBrush}"/>
+ <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource DisabledBorderBrush}"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}"/>
+ <Setter Property="BorderBrush" Value="#FF000000"/>
+ </Style>
+
+ <Style TargetType="{x:Type RichTextBox}">
+ <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
+ <Setter Property="FocusVisualStyle" Value="{x:Null}" />
+ <Setter Property="AllowDrop" Value="true" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type RichTextBox}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.5" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocusedOn">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocusedOff">
+ <DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid>
+ <Border x:Name="Border" Opacity="1" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" Background="{TemplateBinding Background}">
+ <Grid>
+ <Border BorderThickness="1">
+ <ScrollViewer Margin="0" x:Name="PART_ContentHost" Style="{DynamicResource NuclearScrollViewer}" />
+ </Border>
+ </Grid>
+ </Border>
+ <Border x:Name="HoverBorder" IsHitTestVisible="False" Opacity="0" BorderBrush="{StaticResource GlyphBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" />
+ <Border x:Name="FocusVisualElement" IsHitTestVisible="False" Opacity="0" BorderBrush="{StaticResource HoverShineBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2,2,2,2" />
+
+
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsFocused" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource FocusedOff}" x:Name="FocusedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource FocusedOn}" x:Name="FocusedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </MultiTrigger.ExitActions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsMouseOver" Value="True" />
+ <Condition Property="IsFocused" Value="False" />
+ </MultiTrigger.Conditions>
+
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" TargetName="Border" Value="{DynamicResource DisabledBackgroundBrush}"/>
+ <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource DisabledBorderBrush}"/>
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}"/>
+ <Setter Property="BorderBrush" Value="#FF000000"/>
+ </Style>
+ <Style TargetType="{x:Type Label}">
+ <Setter Property="HorizontalContentAlignment" Value="Left" />
+ <Setter Property="VerticalContentAlignment" Value="Top" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Label}">
+ <Grid>
+ <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="false" />
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ </Style>
+
+ <Style TargetType="{x:Type Menu}">
+ <Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="SnapsToDevicePixels" Value="True" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Menu}">
+ <Grid>
+ <Border Margin="1" x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" />
+ <StackPanel Background="{TemplateBinding Background}" IsItemsHost="True" ClipToBounds="True" Orientation="Horizontal" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border" />
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <DropShadowBitmapEffect x:Key="PopupDropShadow" ShadowDepth="1.5" Softness="0.15" />
+ <Style TargetType="{x:Type MenuItem}">
+ <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
+ <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type MenuItem}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HighlightedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HightlightedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="0,0,0,0" Margin="3,2,3,2">
+ <Grid Margin="0,0,0,0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition MinWidth="17" Width="Auto" SharedSizeGroup="MenuItemIconColumnGroup" />
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIGTColumnGroup" />
+ <ColumnDefinition Width="14" />
+ </Grid.ColumnDefinitions>
+ <Border Grid.Column="0" Grid.ColumnSpan="4" Background="{DynamicResource HoverBrush}" Opacity="0" x:Name="HoverBorder" CornerRadius="1,1,1,1"/>
+ <Border Grid.ColumnSpan="4" Background="{DynamicResource HoverShineBrush}" Opacity="0" x:Name="HoverShineBorder" CornerRadius="1,1,1,1"/>
+ <ContentPresenter Margin="4,0,6,0" x:Name="Icon" VerticalAlignment="Center" ContentSource="Icon" />
+ <Grid Visibility="Hidden" Margin="4,0,6,0" x:Name="GlyphPanel" VerticalAlignment="Center">
+ <Path x:Name="GlyphPanelpath" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Data="M0,2 L0,4.8 L2.5,7.4 L7.1,2.8 L7.1,0 L2.5,4.6 z" FlowDirection="LeftToRight" />
+ </Grid>
+ <ContentPresenter Grid.Column="1" x:Name="HeaderHost" RecognizesAccessKey="True" ContentSource="Header" Margin="2,1,2,1" />
+ <Grid Grid.Column="3" Margin="4,0,6,0" x:Name="ArrowPanel" VerticalAlignment="Center">
+ <Path x:Name="ArrowPanelPath" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Data="M0,0 L0,8 L4,4 z" />
+ </Grid>
+ <Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Right" x:Name="SubMenuPopup" Focusable="false" AllowsTransparency="true" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
+ <Grid x:Name="SubMenu">
+ <Border x:Name="SubMenuBorder" Background="{DynamicResource LightBrush}" BorderThickness="1" Opacity="0.8">
+ <Border.BorderBrush>
+ <SolidColorBrush Color="{DynamicResource WhiteColor}" />
+ </Border.BorderBrush>
+ </Border>
+ <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
+ </Grid>
+ </Popup>
+ </Grid>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="Role" Value="TopLevelHeader">
+ <Setter Property="Margin" Value="0,1,0,1" />
+ <Setter Property="Padding" Value="6,3,6,3" />
+ <Setter Property="Grid.IsSharedSizeScope" Value="true" />
+ <Setter Property="Placement" Value="Bottom" TargetName="SubMenuPopup" />
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel" />
+ </Trigger>
+ <Trigger Property="Role" Value="TopLevelItem">
+ <Setter Property="Margin" Value="0,1,0,1" />
+ <Setter Property="Padding" Value="6,3,6,3" />
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel" />
+ </Trigger>
+ <Trigger Property="Role" Value="SubmenuHeader">
+ <Setter Property="DockPanel.Dock" Value="Top" />
+ <Setter Property="Padding" Value="0,2,0,2" />
+ <Setter Property="Grid.IsSharedSizeScope" Value="true" />
+ </Trigger>
+ <Trigger Property="Role" Value="SubmenuItem">
+ <Setter Property="DockPanel.Dock" Value="Top" />
+ <Setter Property="Padding" Value="0,2,0,2" />
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ArrowPanel" />
+ </Trigger>
+ <Trigger Property="IsSuspendingPopupAnimation" Value="true">
+ <Setter Property="PopupAnimation" Value="None" TargetName="SubMenuPopup" />
+ </Trigger>
+ <Trigger Property="Icon" Value="{x:Null}">
+ <Setter Property="Visibility" Value="Collapsed" TargetName="Icon" />
+ </Trigger>
+ <Trigger Property="IsChecked" Value="true">
+ <Setter Property="Visibility" Value="Visible" TargetName="GlyphPanel" />
+ <Setter Property="Visibility" Value="Collapsed" TargetName="Icon" />
+ </Trigger>
+ <Trigger Property="AllowsTransparency" SourceName="SubMenuPopup" Value="true">
+ <Setter Property="Margin" Value="0,0,3,3" TargetName="SubMenu" />
+ <Setter Property="SnapsToDevicePixels" Value="true" TargetName="SubMenu" />
+ <Setter Property="BitmapEffect" Value="{DynamicResource PopupDropShadow}" TargetName="SubMenuBorder" />
+ </Trigger>
+ <Trigger Property="IsHighlighted" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HightlightedOff}" x:Name="HightlightedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HighlightedOn}"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type Separator}">
+ <Setter Property="Height" Value="1" />
+ <Setter Property="Margin" Value="0,2,0,2" />
+ <Setter Property="Focusable" Value="false" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Separator}">
+ <Border BorderBrush="{DynamicResource SolidBorderBrush}" BorderThickness="1" />
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type TabControl}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TabControl}">
+ <Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition x:Name="ColumnDefinition0" />
+ <ColumnDefinition x:Name="ColumnDefinition1" Width="0" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" x:Name="RowDefinition0" />
+ <RowDefinition Height="*" x:Name="RowDefinition1" />
+ </Grid.RowDefinitions>
+ <Border CornerRadius="2,2,0,0" Background="{DynamicResource LightBrush}" x:Name="border">
+ <TabPanel x:Name="HeaderPanel" IsItemsHost="true" Panel.ZIndex="1" KeyboardNavigation.TabIndex="1" RenderTransformOrigin="0.5,0.5" Width="Auto" Height="Auto">
+ <TabPanel.LayoutTransform>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1" />
+ <SkewTransform AngleX="0" AngleY="0" />
+ <RotateTransform Angle="0" />
+ <TranslateTransform X="0" Y="0" />
+ </TransformGroup>
+ </TabPanel.LayoutTransform>
+ <TabPanel.RenderTransform>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1" />
+ <SkewTransform AngleX="0" AngleY="0" />
+ <RotateTransform />
+ <TranslateTransform X="0" Y="0" />
+ </TransformGroup>
+ </TabPanel.RenderTransform>
+
+ </TabPanel>
+ </Border>
+ <Border x:Name="ContentPanel" Grid.Column="0" Grid.Row="1" KeyboardNavigation.DirectionalNavigation="Contained" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local" Background="{DynamicResource ShadeBrush}" CornerRadius="0,0,2,2">
+ <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
+ </Border>
+ <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" BorderThickness="0,0,0,1" CornerRadius="10,0,0,0" Opacity="0.5" Margin="2,0,2,0" x:Name="border1">
+ <Border.LayoutTransform>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1"/>
+ <SkewTransform AngleX="0" AngleY="0"/>
+ <RotateTransform Angle="0"/>
+ <TranslateTransform X="0" Y="0"/>
+ </TransformGroup>
+ </Border.LayoutTransform>
+ <Border.BorderBrush>
+ <SolidColorBrush Color="{DynamicResource BlackColor}"/>
+ </Border.BorderBrush>
+ </Border>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="TabStripPlacement" Value="Bottom">
+ <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0" />
+ <Setter Property="Height" TargetName="RowDefinition0" Value="*" />
+ <Setter Property="Height" TargetName="RowDefinition1" Value="Auto" />
+ <Setter Property="Grid.Row" TargetName="border" Value="1"/>
+ <Setter Property="CornerRadius" TargetName="ContentPanel" Value="2,2,0,0"/>
+ <Setter Property="CornerRadius" TargetName="border" Value="0,0,2,2"/>
+ </Trigger>
+ <Trigger Property="TabStripPlacement" Value="Left">
+
+ <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0" />
+ <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0" />
+ <Setter Property="Grid.Column" TargetName="ContentPanel" Value="1" />
+ <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto" />
+ <Setter Property="Width" TargetName="ColumnDefinition1" Value="*" />
+ <Setter Property="Height" TargetName="RowDefinition0" Value="*" />
+ <Setter Property="Height" TargetName="RowDefinition1" Value="0" />
+ <Setter Property="Grid.Column" TargetName="border" Value="0"/>
+ <Setter Property="Margin" TargetName="border" Value="0,0,0,0"/>
+ <Setter Property="Margin" TargetName="ContentPanel" Value="0,0,0,0"/>
+ <Setter Property="LayoutTransform" TargetName="border1">
+ <Setter.Value>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1"/>
+ <SkewTransform AngleX="0" AngleY="0"/>
+ <RotateTransform Angle="-90"/>
+ <TranslateTransform X="0" Y="0"/>
+ </TransformGroup>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Margin" TargetName="border1" Value="0,0,0,0"/>
+ <Setter Property="CornerRadius" TargetName="border" Value="0,2,2,0"/>
+ <Setter Property="CornerRadius" TargetName="ContentPanel" Value="0,2,2,0"/>
+
+ </Trigger>
+ <Trigger Property="TabStripPlacement" Value="Right">
+ <Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0" />
+ <Setter Property="Grid.Row" TargetName="ContentPanel" Value="0" />
+ <Setter Property="Grid.Column" TargetName="ContentPanel" Value="0" />
+ <Setter Property="Width" TargetName="ColumnDefinition0" Value="*" />
+ <Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto" />
+ <Setter Property="Height" TargetName="RowDefinition0" Value="*" />
+ <Setter Property="Height" TargetName="RowDefinition1" Value="0" />
+ <Setter Property="Grid.Column" TargetName="border" Value="1"/>
+ <Setter Property="LayoutTransform" TargetName="border1">
+ <Setter.Value>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1"/>
+ <SkewTransform AngleX="0" AngleY="0"/>
+ <RotateTransform Angle="90"/>
+ <TranslateTransform X="0" Y="0"/>
+ </TransformGroup>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Grid.Column" TargetName="border1" Value="1"/>
+ <Setter Property="CornerRadius" TargetName="ContentPanel" Value="2,0,0,2"/>
+ <Setter Property="CornerRadius" TargetName="border" Value="0,2,2,0"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style d:IsControlPart="True" TargetType="{x:Type TabItem}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TabItem}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="SelectedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="SelectedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid x:Name="grid" Margin="2,1,2,3">
+ <Grid.LayoutTransform>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1"/>
+ <SkewTransform AngleX="0" AngleY="0"/>
+ <RotateTransform Angle="0"/>
+ <TranslateTransform X="0" Y="0"/>
+ </TransformGroup>
+ </Grid.LayoutTransform>
+ <Border x:Name="border" BorderBrush="{x:Null}" CornerRadius="2,2,2,2" Opacity="0.5">
+ <Border.Background>
+ <LinearGradientBrush EndPoint="0.5,0.976" StartPoint="0.5,0.039">
+ <GradientStop Color="#7F595959" Offset="0" />
+ <GradientStop Color="#19FFFFFF" Offset="1" />
+ </LinearGradientBrush>
+ </Border.Background>
+ </Border>
+ <Border x:Name="SelectedBorder" BorderBrush="{x:Null}" CornerRadius="2,2,2,2" Opacity="0" Background="{DynamicResource SelectedBackgroundBrush}"/>
+ <Border x:Name="HoverBorder" BorderBrush="{x:Null}" CornerRadius="2,2,2,2" Opacity="0">
+ <Border.Background>
+ <LinearGradientBrush EndPoint="0.5,0.976" StartPoint="0.5,0.039">
+ <GradientStop Color="#7F595959" Offset="0" />
+ <GradientStop Color="#19FFFFFF" Offset="1" />
+ </LinearGradientBrush>
+ </Border.Background>
+ </Border>
+ <Grid Margin="14 5">
+ <ContentPresenter x:Name="ContentSite" RecognizesAccessKey="True" ContentSource="Header" d:LayoutOverrides="Width, Height" HorizontalAlignment="Center" Margin="6,1,6,1" VerticalAlignment="Center" />
+ </Grid>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="TabStripPlacement" Value="Left">
+ <Setter Property="LayoutTransform" TargetName="grid">
+ <Setter.Value>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1" />
+ <SkewTransform AngleX="0" AngleY="0" />
+ <RotateTransform Angle="-90" />
+ <TranslateTransform X="0" Y="0" />
+ </TransformGroup>
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ <Trigger Property="TabStripPlacement" Value="Right">
+ <Setter Property="LayoutTransform" TargetName="grid">
+ <Setter.Value>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1" />
+ <SkewTransform AngleX="0" AngleY="0" />
+ <RotateTransform Angle="90" />
+ <TranslateTransform X="0" Y="0" />
+ </TransformGroup>
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ <Trigger Property="TabStripPlacement" Value="Bottom">
+ <Setter Property="LayoutTransform" TargetName="ContentSite">
+ <Setter.Value>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1" />
+ <SkewTransform AngleX="0" AngleY="0" />
+ <RotateTransform Angle="180" />
+ <TranslateTransform X="0" Y="0" />
+ </TransformGroup>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="LayoutTransform" TargetName="grid">
+ <Setter.Value>
+ <TransformGroup>
+ <ScaleTransform ScaleX="1" ScaleY="1"/>
+ <SkewTransform AngleX="0" AngleY="0"/>
+ <RotateTransform Angle="180"/>
+ <TranslateTransform X="0" Y="0"/>
+ </TransformGroup>
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </MultiTrigger.ExitActions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsMouseOver" Value="True" />
+ <Condition Property="Selector.IsSelected" Value="False" />
+ </MultiTrigger.Conditions>
+ </MultiTrigger>
+ <Trigger Property="IsSelected" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOff}" x:Name="SelectedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOn}" x:Name="SelectedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ <Setter Property="Panel.ZIndex" Value="100" />
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsMouseOver" Value="True" SourceName="grid" />
+ <Condition Property="Selector.IsSelected" Value="True" />
+ </MultiTrigger.Conditions>
+ <Setter Property="Background" TargetName="border" Value="{DynamicResource SelectedBackgroundBrush}" />
+ <Setter Property="Opacity" TargetName="border" Value="0.9" />
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ <Setter Property="Background" TargetName="border" Value="{DynamicResource DisabledBackgroundBrush}"/>
+ <Setter Property="Opacity" TargetName="grid" Value="0.3"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <Trigger Property="Selector.IsSelected" Value="True">
+ <Setter Property="Foreground" >
+ <Setter.Value>
+ <SolidColorBrush Color="{DynamicResource BlackColor}"/>
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+ <Style x:Key="NuclearSliderThumb" d:IsControlPart="True" TargetType="{x:Type Thumb}">
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="Height" Value="14" />
+ <Setter Property="Width" Value="14" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Thumb}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" />
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid>
+ <Border x:Name="Background" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" />
+ <Border x:Name="HoverBorder" Opacity="0" Background="{StaticResource HoverBrush}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" />
+ <Border x:Name="HoverShineBorder" Opacity="0" Background="{StaticResource HoverShineBrush}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" />
+ <Border x:Name="PressedBorder" Opacity="0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" Background="{StaticResource PressedBrush}" />
+ <Border x:Name="ShineBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3" Background="{StaticResource ShineBrush}" Opacity="1" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsDragging" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" TargetName="Background" Value="{DynamicResource DisabledBackgroundBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
+ <Setter Property="BorderThickness" Value="1,1,1,1"/>
+ </Style>
+ <Style TargetType="{x:Type Slider}">
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Slider}">
+ <Grid x:Name="GridRoot">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <TickBar Visibility="Collapsed" x:Name="TopTick" Height="4" SnapsToDevicePixels="True" Placement="Top" Fill="{DynamicResource GlyphBrush}" />
+ <Border Grid.Row="1" Margin="0" x:Name="Border" Height="4" CornerRadius="2" Background="{DynamicResource LightBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" BorderThickness="1,1,1,1" />
+ <Track Grid.Row="1" x:Name="PART_Track">
+ <Track.Thumb>
+ <Thumb Style="{DynamicResource NuclearSliderThumb}" Background="{DynamicResource NormalBrush}" Foreground="{x:Null}" Width="20" />
+ </Track.Thumb>
+ <Track.IncreaseRepeatButton>
+ <RepeatButton Style="{DynamicResource NuclearScrollRepeatButtonStyle}" Command="Slider.IncreaseLarge" />
+ </Track.IncreaseRepeatButton>
+ <Track.DecreaseRepeatButton>
+ <RepeatButton Style="{DynamicResource NuclearScrollRepeatButtonStyle}" Command="Slider.DecreaseLarge" />
+ </Track.DecreaseRepeatButton>
+ </Track>
+ <TickBar Visibility="Collapsed" Grid.Row="2" x:Name="BottomTick" Height="4" SnapsToDevicePixels="True" Placement="Bottom" Fill="{TemplateBinding Foreground}" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="TickPlacement" Value="TopLeft">
+ <Setter Property="Visibility" Value="Visible" TargetName="TopTick" />
+ </Trigger>
+ <Trigger Property="TickPlacement" Value="BottomRight">
+ <Setter Property="Visibility" Value="Visible" TargetName="BottomTick" />
+ </Trigger>
+ <Trigger Property="TickPlacement" Value="Both">
+ <Setter Property="Visibility" Value="Visible" TargetName="TopTick" />
+ <Setter Property="Visibility" Value="Visible" TargetName="BottomTick" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border" />
+ <Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border" />
+ </Trigger>
+ <Trigger Property="Orientation" Value="Vertical">
+ <Setter Property="LayoutTransform" TargetName="GridRoot">
+ <Setter.Value>
+ <RotateTransform Angle="-90" />
+ </Setter.Value>
+ </Setter>
+ <Setter TargetName="PART_Track" Property="Orientation" Value="Horizontal" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type TreeView}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TreeView}">
+ <Grid>
+ <Border x:Name="Border" Background="{DynamicResource ControlBackgroundBrush}" BorderBrush="{x:Null}" BorderThickness="1" CornerRadius="3,3,3,3">
+ <ScrollViewer Style="{DynamicResource NuclearScrollViewer}" Focusable="False" Padding="4" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="False" Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}">
+ <ItemsPresenter />
+ </ScrollViewer>
+ </Border>
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="NuclearTreeViewItemToggleButton" d:IsControlPart="True" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Focusable" Value="False" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <Grid Width="15" Height="13">
+ <Path Visibility="Collapsed" HorizontalAlignment="Left" Margin="1,1,1,1" x:Name="IsExpandedPath" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 0 4 L 8 4 L 4 8 Z" />
+ <Path HorizontalAlignment="Left" Margin="1,1,1,1" x:Name="ExpandPath" VerticalAlignment="Center" Fill="{DynamicResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsChecked" Value="True">
+ <Setter Property="Visibility" Value="Visible" TargetName="IsExpandedPath" />
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ExpandPath" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style d:IsControlPart="True" TargetType="{x:Type TreeViewItem}">
+ <Style.Triggers>
+ <Trigger Property="IsSelected" Value="True">
+ <Setter Property="Foreground">
+ <Setter.Value>
+ <SolidColorBrush Color="{DynamicResource BlackColor}"/>
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ </Style.Triggers>
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
+ <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
+ <Setter Property="Padding" Value="1,0,0,0" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type TreeViewItem}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="SelectedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectionBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="SelectedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectionBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition MinWidth="19" Width="Auto" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="*" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition />
+ </Grid.RowDefinitions>
+ <ToggleButton x:Name="Expander" Style="{DynamicResource NuclearTreeViewItemToggleButton}" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" />
+ <Border Grid.Column="1" x:Name="Selection_Border" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3">
+ <Grid Width="Auto" Height="Auto">
+ <Border x:Name="MainBorder" Opacity="0.25" Background="{DynamicResource NormalBrush}" CornerRadius="2,2,2,2"/>
+ <Border x:Name="SelectionBorder" Opacity="0" Background="{DynamicResource SelectedBackgroundBrush}" CornerRadius="2,2,2,2"/>
+ <Border x:Name="HoverBorder" Opacity="0" Background="{DynamicResource NormalBrush}" CornerRadius="2,2,2,2"/>
+ <ContentPresenter x:Name="PART_Header" ContentSource="Header" d:LayoutOverrides="Width" Height="Auto" Margin="2,0,2,0" />
+ </Grid>
+ </Border>
+ <ItemsPresenter Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1" x:Name="ItemsHost" />
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsExpanded" Value="false">
+ <Setter Property="Visibility" Value="Collapsed" TargetName="ItemsHost" />
+ </Trigger>
+ <Trigger Property="HasItems" Value="false">
+ <Setter Property="Visibility" Value="Hidden" TargetName="Expander" />
+ </Trigger>
+ <Trigger Property="IsSelected" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOff}" x:Name="SelectedOn_BeginStoryboard1"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOn}" x:Name="SelectedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsSelected" Value="true" />
+ <Condition Property="IsSelectionActive" Value="false" />
+ </MultiTrigger.Conditions>
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
+ <Setter Property="Background" TargetName="Selection_Border" Value="{DynamicResource SelectedBackgroundBrush}" />
+ <Setter Property="Opacity" TargetName="Selection_Border" Value="0.5" />
+ <Setter Property="BorderBrush" TargetName="Selection_Border" Value="{DynamicResource NormalBorderBrush}" />
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </MultiTrigger.ExitActions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsMouseOver" SourceName="Selection_Border" Value="True" />
+ <Condition Property="IsSelected" Value="False" />
+ </MultiTrigger.Conditions>
+ </MultiTrigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsSelected" Value="true" />
+ <Condition Property="IsMouseOver" Value="true" SourceName="Selection_Border" />
+ </MultiTrigger.Conditions>
+ <Setter Property="Background" TargetName="Selection_Border" Value="{DynamicResource SelectedBackgroundBrush}" />
+ <Setter Property="Opacity" TargetName="Selection_Border" Value="0.8" />
+ <Setter Property="BorderBrush" TargetName="Selection_Border" Value="{DynamicResource NormalBorderBrush}" />
+ </MultiTrigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style x:Key="ButtonFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Rectangle SnapsToDevicePixels="true" Margin="2" Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1" />
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#F3F3F3" Offset="0" />
+ <GradientStop Color="#EBEBEB" Offset="0.5" />
+ <GradientStop Color="#DDDDDD" Offset="0.5" />
+ <GradientStop Color="#CDCDCD" Offset="1" />
+ </LinearGradientBrush>
+ <SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070" />
+
+ <Style TargetType="{x:Type ToggleButton}">
+ <Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}" />
+ <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}" />
+ <Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="HorizontalContentAlignment" Value="Center" />
+ <Setter Property="VerticalContentAlignment" Value="Center" />
+ <Setter Property="Padding" Value="1" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.5"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="CheckedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="CheckedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.5"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="CheckedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="CheckedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Pressed" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Pressed" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid x:Name="grid">
+
+ <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{DynamicResource NormalBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" CornerRadius="3,3,3,3" />
+ <Border x:Name="CheckedBorder" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{DynamicResource SelectedBackgroundBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" CornerRadius="3,3,3,3" Opacity="0" />
+ <Border x:Name="HoverBorder" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" BorderBrush="{DynamicResource NormalBorderBrush}" CornerRadius="3,3,3,3" Opacity="0" Background="{DynamicResource HoverBrush}" />
+ <Border x:Name="HoverShineBorder" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" BorderBrush="{DynamicResource NormalBorderBrush}" CornerRadius="3,3,3,3" Opacity="0" Background="{DynamicResource HoverShineBrush}" />
+ <Border x:Name="Pressed" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" BorderBrush="{DynamicResource PressedBorderBrush}" CornerRadius="3,3,3,3" Opacity="0" Background="{DynamicResource PressedBrush}" />
+ <Rectangle Opacity="1" Stroke="{x:Null}" RadiusX="3" RadiusY="3" Margin="2,2,2,2" VerticalAlignment="Stretch" Height="Auto" x:Name="Shine">
+ <Rectangle.Fill>
+ <LinearGradientBrush EndPoint="0.5,0.971" StartPoint="0.5,0.042">
+ <GradientStop Color="#26FFFFFF" Offset="0" />
+ <GradientStop Color="#00FFFFFF" Offset="1" />
+ <GradientStop Color="#26FFFFFF" Offset="0.467" />
+ <GradientStop Color="#00FFFFFF" Offset="0.475" />
+ </LinearGradientBrush>
+ </Rectangle.Fill>
+ </Rectangle>
+
+ <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="4,4,4,4" />
+ </Grid>
+
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="True">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsKeyboardFocused" Value="true"/>
+ <Trigger Property="IsChecked" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource CheckedOff}" x:Name="CheckedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource CheckedOn}" x:Name="CheckedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="#ADADAD" />
+ <Setter Property="Background" TargetName="Border" Value="{DynamicResource DisabledBackgroundBrush}"/>
+ <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource DisabledBorderBrush}"/>
+ <Setter Property="Opacity" TargetName="grid" Value="0.5"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <Trigger Property="IsChecked" Value="True">
+ <Setter Property="Foreground">
+ <Setter.Value>
+ <SolidColorBrush Color="{DynamicResource BlackColor}" />
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+
+ <BorderGapMaskConverter x:Key="BorderGapMaskConverter" />
+ <Style TargetType="{x:Type GroupBox}">
+ <Setter Property="BorderBrush" Value="#D5DFE5" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type GroupBox}">
+ <Grid SnapsToDevicePixels="true">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="6" />
+ <ColumnDefinition Width="Auto" />
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="6" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="Auto" />
+ <RowDefinition Height="*" />
+ <RowDefinition Height="6" />
+ </Grid.RowDefinitions>
+ <Border Grid.ColumnSpan="4" Grid.RowSpan="4" Background="{DynamicResource LightBrush}" CornerRadius="4,4,4,4" BorderThickness="1,1,1,1" />
+ <Border Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3" BorderThickness="1,1,1,1" CornerRadius="4" BorderBrush="{DynamicResource NormalBorderBrush}">
+ <Border.OpacityMask>
+ <MultiBinding Converter="{StaticResource BorderGapMaskConverter}" ConverterParameter="7">
+ <Binding Path="ActualWidth" ElementName="Header" />
+ <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}" />
+ <Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}" />
+ </MultiBinding>
+ </Border.OpacityMask>
+ <Border BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3">
+ <Border.BorderBrush>
+ <SolidColorBrush Color="{DynamicResource MainColor}" />
+ </Border.BorderBrush>
+ <Border BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2">
+ <Border.BorderBrush>
+ <SolidColorBrush Color="{DynamicResource MainColor}" />
+ </Border.BorderBrush>
+ </Border>
+ </Border>
+ </Border>
+ <Border Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="1" Grid.RowSpan="3" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4" />
+ <Border Grid.ColumnSpan="4" Grid.RowSpan="2" Background="{DynamicResource NormalBrush}" BorderThickness="1,1,1,0" CornerRadius="2,2,0,0" x:Name="Main" BorderBrush="{DynamicResource NormalBorderBrush}" />
+ <Rectangle Opacity="1" Stroke="{x:Null}" RadiusX="3" RadiusY="3" VerticalAlignment="Stretch" Height="Auto" x:Name="Shine" Grid.ColumnSpan="4" Grid.RowSpan="2" Margin="1,1,1,1">
+ <Rectangle.Fill>
+ <LinearGradientBrush EndPoint="0.5,0.971" StartPoint="0.5,0.042">
+ <GradientStop Color="#26FFFFFF" Offset="0" />
+ <GradientStop Color="#00FFFFFF" Offset="1" />
+ <GradientStop Color="#26FFFFFF" Offset="0.467" />
+ <GradientStop Color="#00FFFFFF" Offset="0.475" />
+ </LinearGradientBrush>
+ </Rectangle.Fill>
+ </Rectangle>
+
+ <Border x:Name="Header" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Padding="3,1,3,0">
+ <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True" Margin="0,4,0,4" />
+ </Border>
+
+ <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="{TemplateBinding Padding}" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Foreground">
+ <Setter.Value>
+ <SolidColorBrush Color="{DynamicResource WhiteColor}" />
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <SolidColorBrush x:Key="ListBorder" Color="#828790" />
+ <Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}" TargetType="{x:Type ScrollViewer}">
+ <Setter Property="Focusable" Value="false" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ScrollViewer}">
+ <Grid SnapsToDevicePixels="true" Background="{TemplateBinding Background}">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*" />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <DockPanel Margin="{TemplateBinding Padding}">
+ <ScrollViewer Focusable="false" DockPanel.Dock="Top" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
+ <GridViewHeaderRowPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="2,0,2,0" AllowsColumnReorder="{Binding Path=TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderContainerStyle="{Binding Path=TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderContextMenu="{Binding Path=TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderTemplate="{Binding Path=TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderTemplateSelector="{Binding Path=TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderToolTip="{Binding Path=TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}" Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}" />
+
+ </ScrollViewer>
+ <ScrollContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ScrollContentPresenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" KeyboardNavigation.DirectionalNavigation="Local" />
+ </DockPanel>
+ <ScrollBar Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Cursor="Arrow" x:Name="PART_HorizontalScrollBar" Grid.Row="1" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0.0" Value="{Binding Path=HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
+ <ScrollBar Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Cursor="Arrow" x:Name="PART_VerticalScrollBar" Grid.Column="1" Orientation="Vertical" ViewportSize="{TemplateBinding ViewportHeight}" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0.0" Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
+ <DockPanel Grid.Column="1" Grid.Row="1" Background="{Binding Path=Background, ElementName=PART_VerticalScrollBar}" LastChildFill="false">
+ <Rectangle Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Width="1" Fill="White" DockPanel.Dock="Left" />
+ <Rectangle Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Height="1" Fill="White" DockPanel.Dock="Top" />
+ </DockPanel>
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style TargetType="{x:Type ListView}">
+ <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
+ <Setter Property="BorderBrush" Value="{StaticResource ListBorder}" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="Foreground" Value="#FF042271" />
+ <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
+ <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
+ <Setter Property="ScrollViewer.CanContentScroll" Value="true" />
+ <Setter Property="VerticalContentAlignment" Value="Center" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ListView}">
+ <Border x:Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="0,0,0,0" BorderBrush="{DynamicResource DefaultedBorderBrush}" Background="{DynamicResource ControlBackgroundBrush}">
+
+ <ScrollViewer Padding="{TemplateBinding Padding}" Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
+ <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
+ </ScrollViewer>
+ </Border>
+
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsGrouping" Value="true">
+ <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="ListViewItemFocusVisual">
+ <Setter Property="Control.Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Rectangle Stroke="#8E6EA6F5" StrokeThickness="1" RadiusX="2" RadiusY="2" />
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style TargetType="{x:Type ListViewItem}">
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="FocusVisualStyle" Value="{StaticResource ListViewItemFocusVisual}" />
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="BorderBrush" Value="Transparent" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="Margin" Value="0,0,0,1" />
+ <Setter Property="Padding" Value="5,2,5,2" />
+ <Setter Property="VerticalContentAlignment" Value="Center" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ListViewItem}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="SelectedOn">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="SelectedOff">
+
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="SelectedRectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+
+ </Storyboard>
+ <Storyboard x:Key="FocussedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="FocussedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Border SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" x:Name="border">
+
+ <Grid Margin="2,0,2,0">
+ <Rectangle x:Name="Background" IsHitTestVisible="False" Opacity="0.25" Fill="{StaticResource NormalBrush}" RadiusX="1" RadiusY="1"/>
+ <Rectangle x:Name="HoverRectangle" IsHitTestVisible="False" Opacity="0" Fill="{StaticResource NormalBrush}" RadiusX="1" RadiusY="1"/>
+ <Rectangle x:Name="SelectedRectangle" IsHitTestVisible="False" Opacity="0" Fill="{StaticResource SelectedBackgroundBrush}" RadiusX="1" RadiusY="1"/>
+ <GridViewRowPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="0,2,0,2" VerticalAlignment="Stretch" />
+ </Grid>
+
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsSelected" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOff}" x:Name="SelectedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource SelectedOn}" x:Name="SelectedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </MultiTrigger.ExitActions>
+ <MultiTrigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </MultiTrigger.EnterActions>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsMouseOver" Value="True" />
+ <Condition Property="Selector.IsSelected" Value="False" />
+ </MultiTrigger.Conditions>
+
+ </MultiTrigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="Selector.IsSelected" Value="True" />
+ <Condition Property="IsMouseOver" Value="True" />
+ </MultiTrigger.Conditions>
+
+ </MultiTrigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource DisabledForegroundBrush}" />
+ <Setter Property="Fill" TargetName="Background" Value="{DynamicResource DisabledBackgroundBrush}"/>
+ </Trigger>
+ <MultiTrigger>
+ <MultiTrigger.Conditions>
+ <Condition Property="IsSelected" Value="true" />
+ <Condition Property="Selector.IsSelectionActive" Value="false" />
+ </MultiTrigger.Conditions>
+
+
+ </MultiTrigger>
+
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Style.Triggers>
+ <Trigger Property="Selector.IsSelected" Value="True">
+ <Setter Property="Foreground">
+ <Setter.Value>
+ <SolidColorBrush Color="{DynamicResource BlackColor}" />
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+
+
+ <Style x:Key="GridViewColumnHeaderGripper" TargetType="{x:Type Thumb}">
+ <Setter Property="Canvas.Right" Value="-8.5" />
+ <Setter Property="Width" Value="18" />
+ <Setter Property="Height" Value="{Binding Path=ActualHeight, RelativeSource={RelativeSource TemplatedParent}}" />
+ <Setter Property="Padding" Value="0,3,0,4" />
+ <Setter Property="Background" Value="{DynamicResource LightBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Thumb}">
+ <Border Padding="{TemplateBinding Padding}">
+ <Rectangle HorizontalAlignment="Center" Width="0.5">
+ <Rectangle.Fill>
+ <SolidColorBrush Color="{DynamicResource WhiteColor}" />
+ </Rectangle.Fill>
+ </Rectangle>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <Style x:Key="{x:Type GridViewColumnHeader}" TargetType="{x:Type GridViewColumnHeader}">
+ <Setter Property="HorizontalContentAlignment" Value="Center" />
+ <Setter Property="VerticalContentAlignment" Value="Center" />
+ <Setter Property="Background" Value="{DynamicResource LightBrush}" />
+ <Setter Property="Padding" Value="2,0,2,0" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
+ <ControlTemplate.Resources>
+ <Storyboard x:Key="HoverOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="HoverOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="HoverShineBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOn">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ <Storyboard x:Key="PressedOff">
+ <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PressedBorder" Storyboard.TargetProperty="(UIElement.Opacity)">
+ <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </ControlTemplate.Resources>
+ <Grid Margin="0,1,0,1">
+ <Grid>
+
+ <Border x:Name="Main" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{DynamicResource NormalBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" CornerRadius="0,0,0,0" />
+ <Rectangle Opacity="1" Stroke="{x:Null}" RadiusX="0" RadiusY="0" VerticalAlignment="Stretch" Height="Auto" x:Name="Shine" Fill="{DynamicResource ShineBrush}"/>
+ <Border x:Name="HoverBorder" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{DynamicResource HoverBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" CornerRadius="0,0,0,0" Opacity="0" />
+ <Border x:Name="HoverShineBorder" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{DynamicResource HoverShineBrush}" BorderBrush="{DynamicResource NormalBorderBrush}" CornerRadius="0,0,0,0" Opacity="0" />
+ <Border x:Name="PressedBorder" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Background="{DynamicResource PressedBrush}" BorderBrush="{DynamicResource PressedBorderBrush}" CornerRadius="0,0,0,0" Opacity="0" />
+
+ <Border BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
+ <Border.BorderBrush>
+ <SolidColorBrush Color="{DynamicResource WhiteColor}"/>
+ </Border.BorderBrush>
+ <ContentPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" Margin="2,2,2,2" />
+ </Border>
+ </Grid>
+ <Canvas>
+ <Thumb x:Name="PART_HeaderGripper" Style="{StaticResource GridViewColumnHeaderGripper}" Margin="0,0,7,0" Width="Auto" HorizontalAlignment="Stretch" />
+ </Canvas>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOff}" x:Name="HoverOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource HoverOn}"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="IsPressed" Value="true">
+ <Trigger.ExitActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOff}" x:Name="PressedOff_BeginStoryboard"/>
+ </Trigger.ExitActions>
+ <Trigger.EnterActions>
+ <BeginStoryboard Storyboard="{StaticResource PressedOn}" x:Name="PressedOn_BeginStoryboard"/>
+ </Trigger.EnterActions>
+ </Trigger>
+ <Trigger Property="Height" Value="Auto">
+ <Setter Property="MinHeight" Value="20" />
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
+ </Trigger>
+ <Trigger Property="Role" Value="Padding">
+ <Setter TargetName="PART_HeaderGripper" Property="Visibility" Value="Collapsed" />
+ </Trigger>
+ <Trigger Property="Role" Value="Floating">
+ <Setter TargetName="PART_HeaderGripper" Property="Visibility" Value="Collapsed" />
+ <Setter Property="Background" Value="Yellow" />
+ <Setter Property="Foreground" Value="{DynamicResource MouseOverBrush}" />
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style x:Key="{x:Static ToolBar.ButtonStyleKey}" TargetType="{x:Type Button}" BasedOn="{x:Null}">
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource NuclearButtonFocusVisual}" />
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template" Value="{DynamicResource ButtonTemplate}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="FontSize" Value="10"/>
+ <Setter Property="MinHeight" Value="18"/>
+ <Setter Property="MinWidth" Value="50"/>
+ </Style>
+
+ <Style x:Key="{x:Static ToolBar.CheckBoxStyleKey}" TargetType="{x:Type CheckBox}">
+ <Setter Property="FontSize" Value="10"/>
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource CheckBoxFocusVisual}" />
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template" Value="{DynamicResource CheckBoxTemplate}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ </Style>
+
+ <Style x:Key="{x:Static ToolBar.RadioButtonStyleKey}" TargetType="{x:Type RadioButton}">
+ <Setter Property="FontSize" Value="10"/>
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="FocusVisualStyle" Value="{DynamicResource RadioButtonFocusVisual}" />
+ <Setter Property="Background" Value="{DynamicResource NormalBrush}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}" />
+ <Setter Property="Template" Value="{DynamicResource RadioButtonTemplate}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Style.Triggers>
+ <Trigger Property="IsEnabled" Value="False">
+ <Setter Property="Foreground">
+ <Setter.Value>
+ <SolidColorBrush Color="{DynamicResource TopGradientSelectColor}" />
+ </Setter.Value>
+ </Setter>
+ </Trigger>
+ </Style.Triggers>
+ </Style>
+
+ <Style x:Key="{x:Static ToolBar.ComboBoxStyleKey}" TargetType="{x:Type ComboBox}">
+ <Setter Property="FontSize" Value="10"/>
+ <Setter Property="SnapsToDevicePixels" Value="true" />
+ <Setter Property="Template" Value="{DynamicResource ComboBoxTemplate}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ </Style>
+
+ <Style x:Key="{x:Static ToolBar.TextBoxStyleKey}" TargetType="{x:Type TextBox}">
+ <Setter Property="FontSize" Value="10"/>
+ <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
+ <Setter Property="FocusVisualStyle" Value="{x:Null}" />
+ <Setter Property="AllowDrop" Value="true" />
+ <Setter Property="Template" Value="{DynamicResource TextBoxTemplate}" />
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ <Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}"/>
+ <Setter Property="BorderBrush" Value="#FF000000"/>
+ </Style>
+
+
+ <LinearGradientBrush x:Key="ToolBarHorizontalBackground" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#FFFFFF" Offset="0"/>
+ <GradientStop Color="#FFFBFF" Offset="0.5"/>
+ <GradientStop Color="#F7F7F7" Offset="1"/>
+ </LinearGradientBrush>
+ <LinearGradientBrush x:Key="ToolBarToggleButtonHorizontalBackground" EndPoint="0,1" StartPoint="0,0">
+ <GradientStop Color="#ECECEC" Offset="0"/>
+ <GradientStop Color="#DDDDDD" Offset="0.5"/>
+ <GradientStop Color="#A0A0A0" Offset="1"/>
+ </LinearGradientBrush>
+ <SolidColorBrush x:Key="ToolBarButtonHover" Color="{StaticResource MainColor}" />
+ <SolidColorBrush x:Key="ToolBarGripper" Color="#C6C3C6"/>
+ <Style x:Key="ToolBarHorizontalOverflowButtonStyle" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Background" Value="{StaticResource ToolBarToggleButtonHorizontalBackground}"/>
+ <Setter Property="MinHeight" Value="0"/>
+ <Setter Property="MinWidth" Value="0"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <Border SnapsToDevicePixels="true" x:Name="Bd" CornerRadius="0,0,0,0" Background="{DynamicResource LightBrush}">
+ <Canvas SnapsToDevicePixels="true" HorizontalAlignment="Right" Margin="7,2,2,2" VerticalAlignment="Bottom" Width="6" Height="7">
+ <Path Stroke="White" Data="M 1 1.5 L 6 1.5"/>
+ <Path Stroke="{TemplateBinding Foreground}" Data="M 0 0.5 L 5 0.5"/>
+ <Path Fill="White" Data="M 0.5 4 L 6.5 4 L 3.5 7 Z"/>
+ <Path Fill="{TemplateBinding Foreground}" Data="M -0.5 3 L 5.5 3 L 2.5 6 Z"/>
+ </Canvas>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Background" TargetName="Bd" Value="{DynamicResource MouseOverBrush}"/>
+ </Trigger>
+ <Trigger Property="IsKeyboardFocused" Value="true">
+ <Setter Property="Background" TargetName="Bd" Value="{StaticResource ToolBarButtonHover}"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{StaticResource ToolBarGripper}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <SolidColorBrush x:Key="ToolBarSubMenuBackground" Color="#FFFDFDFD"/>
+ <SolidColorBrush x:Key="ToolBarMenuBorder" Color="#FFFFFFFF"/>
+ <Style x:Key="ToolBarThumbStyle" TargetType="{x:Type Thumb}">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Thumb}">
+ <Border SnapsToDevicePixels="True" Background="Transparent" Padding="{TemplateBinding Padding}" CornerRadius="0,0,0,0">
+ <Rectangle>
+ <Rectangle.Fill>
+ <DrawingBrush TileMode="Tile" Viewbox="0,0,4,4" ViewboxUnits="Absolute" Viewport="0,0,4,4" ViewportUnits="Absolute">
+ <DrawingBrush.Drawing>
+ <DrawingGroup>
+ <GeometryDrawing Brush="White" Geometry="M 1 1 L 1 3 L 3 3 L 3 1 z"/>
+ <GeometryDrawing Brush="{StaticResource ToolBarGripper}" Geometry="M 0 0 L 0 2 L 2 2 L 2 0 z"/>
+ </DrawingGroup>
+ </DrawingBrush.Drawing>
+ </DrawingBrush>
+ </Rectangle.Fill>
+ </Rectangle>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Cursor" Value="SizeAll"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <LinearGradientBrush x:Key="ToolBarToggleButtonVerticalBackground" EndPoint="1,0" StartPoint="0,0">
+ <GradientStop Color="#ECECEC" Offset="0"/>
+ <GradientStop Color="#DDDDDD" Offset="0.5"/>
+ <GradientStop Color="#A0A0A0" Offset="1"/>
+ </LinearGradientBrush>
+ <Style x:Key="ToolBarVerticalOverflowButtonStyle" TargetType="{x:Type ToggleButton}">
+ <Setter Property="Background" Value="{StaticResource ToolBarToggleButtonVerticalBackground}"/>
+ <Setter Property="MinHeight" Value="0"/>
+ <Setter Property="MinWidth" Value="0"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToggleButton}">
+ <Border SnapsToDevicePixels="true" x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="0,0,3,3">
+ <Canvas SnapsToDevicePixels="true" HorizontalAlignment="Right" Margin="2,7,2,2" VerticalAlignment="Bottom" Width="7" Height="6">
+ <Path Stroke="White" Data="M 1.5 1 L 1.5 6"/>
+ <Path Stroke="{TemplateBinding Foreground}" Data="M 0.5 0 L 0.5 5"/>
+ <Path Fill="White" Data="M 3.5 0.5 L 7 3.5 L 4 6.5 Z"/>
+ <Path Fill="{TemplateBinding Foreground}" Data="M 3 -0.5 L 6 2.5 L 3 5.5 Z"/>
+ </Canvas>
+ </Border>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsMouseOver" Value="true">
+ <Setter Property="Background" TargetName="Bd" Value="{StaticResource ToolBarButtonHover}"/>
+ </Trigger>
+ <Trigger Property="IsKeyboardFocused" Value="true">
+ <Setter Property="Background" TargetName="Bd" Value="{StaticResource ToolBarButtonHover}"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{StaticResource ToolBarGripper}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ <LinearGradientBrush x:Key="ToolBarVerticalBackground" EndPoint="1,0" StartPoint="0,0">
+ <GradientStop Color="#FFFFFF" Offset="0"/>
+ <GradientStop Color="#FFFBFF" Offset="0.5"/>
+ <GradientStop Color="#F7F7F7" Offset="1"/>
+ </LinearGradientBrush>
+ <Style TargetType="{x:Type ToolBar}">
+ <Setter Property="Background" Value="{StaticResource ToolBarHorizontalBackground}"/>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type ToolBar}">
+ <Grid SnapsToDevicePixels="true" Margin="3,1,1,1" x:Name="Grid">
+ <Grid HorizontalAlignment="Right" x:Name="OverflowGrid">
+ <ToggleButton IsEnabled="{TemplateBinding HasOverflowItems}" FocusVisualStyle="{x:Null}" x:Name="OverflowButton" Style="{StaticResource ToolBarHorizontalOverflowButtonStyle}" ClickMode="Press" IsChecked="{Binding Path=IsOverflowOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
+ <Popup Focusable="false" AllowsTransparency="true" IsOpen="{Binding Path=IsOverflowOpen, RelativeSource={RelativeSource TemplatedParent}}" Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" StaysOpen="False" x:Name="OverflowPopup">
+ <Border x:Name="Shdw">
+ <Border BorderThickness="1,1,1,1" Background="{DynamicResource LightBrush}" BorderBrush="{DynamicResource LightBrush}">
+ <ToolBarOverflowPanel Focusable="true" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" FocusVisualStyle="{x:Null}" Margin="2" x:Name="PART_ToolBarOverflowPanel" WrapWidth="200" KeyboardNavigation.DirectionalNavigation="Cycle" KeyboardNavigation.TabNavigation="Cycle"/>
+ </Border>
+ </Border>
+ </Popup>
+ </Grid>
+ <Border Margin="0,0,11,0" x:Name="MainPanelBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="0,0,0,0" Padding="{TemplateBinding Padding}" Background="{DynamicResource NormalBorderBrush}">
+ <Grid>
+
+ <DockPanel KeyboardNavigation.TabIndex="1" KeyboardNavigation.TabNavigation="Local">
+ <Thumb Padding="6,5,1,6" Margin="-3,-1,0,0" x:Name="ToolBarThumb" Style="{StaticResource ToolBarThumbStyle}" Width="10"/>
+ <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="Center" Margin="4,0,4,0" x:Name="ToolBarHeader" VerticalAlignment="Center" ContentSource="Header"/>
+ <ToolBarPanel SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="0,1,2,2" x:Name="PART_ToolBarPanel" IsItemsHost="true" Background="{DynamicResource NormalBrush}"/>
+ </DockPanel>
+ <Rectangle IsHitTestVisible="False" Opacity="0.15" VerticalAlignment="Top" Height="13.995" Grid.ColumnSpan="1" Margin="0,0,2,0">
+ <Rectangle.Fill>
+ <SolidColorBrush Color="{DynamicResource WhiteColor}"/>
+ </Rectangle.Fill>
+ </Rectangle>
+ </Grid>
+ </Border>
+ </Grid>
+ <ControlTemplate.Triggers>
+ <Trigger Property="IsOverflowOpen" Value="true">
+ <Setter Property="IsEnabled" TargetName="ToolBarThumb" Value="false"/>
+ </Trigger>
+ <Trigger Property="Header" Value="{x:Null}">
+ <Setter Property="Visibility" TargetName="ToolBarHeader" Value="Collapsed"/>
+ </Trigger>
+ <Trigger Property="ToolBarTray.IsLocked" Value="true">
+ <Setter Property="Visibility" TargetName="ToolBarThumb" Value="Collapsed"/>
+ </Trigger>
+ <Trigger Property="HasDropShadow" SourceName="OverflowPopup" Value="true">
+ <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
+ <Setter Property="SnapsToDevicePixels" TargetName="Shdw" Value="true"/>
+ <Setter Property="Background" TargetName="Shdw" Value="#71000000"/>
+ </Trigger>
+ <Trigger Property="Orientation" Value="Vertical">
+ <Setter Property="Margin" TargetName="Grid" Value="1,3,1,1"/>
+ <Setter Property="Style" TargetName="OverflowButton" Value="{StaticResource ToolBarVerticalOverflowButtonStyle}"/>
+ <Setter Property="Height" TargetName="ToolBarThumb" Value="10"/>
+ <Setter Property="Width" TargetName="ToolBarThumb" Value="Auto"/>
+ <Setter Property="Margin" TargetName="ToolBarThumb" Value="-1,-3,0,0"/>
+ <Setter Property="Padding" TargetName="ToolBarThumb" Value="5,6,6,1"/>
+ <Setter Property="Margin" TargetName="ToolBarHeader" Value="0,0,0,4"/>
+ <Setter Property="Margin" TargetName="PART_ToolBarPanel" Value="1,0,2,2"/>
+ <Setter Property="DockPanel.Dock" TargetName="ToolBarThumb" Value="Top"/>
+ <Setter Property="DockPanel.Dock" TargetName="ToolBarHeader" Value="Top"/>
+ <Setter Property="HorizontalAlignment" TargetName="OverflowGrid" Value="Stretch"/>
+ <Setter Property="VerticalAlignment" TargetName="OverflowGrid" Value="Bottom"/>
+ <Setter Property="Placement" TargetName="OverflowPopup" Value="Right"/>
+ <Setter Property="Margin" TargetName="MainPanelBorder" Value="0,0,0,11"/>
+ <Setter Property="Background" Value="{StaticResource ToolBarVerticalBackground}"/>
+ </Trigger>
+ <Trigger Property="IsEnabled" Value="false">
+ <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
+ </Trigger>
+ </ControlTemplate.Triggers>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
+ </Style>
+
+</ResourceDictionary>
+
+
+
+
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/Generic.xaml b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/Generic.xaml
new file mode 100644
index 000000000..0a6ffb56f
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/Generic.xaml
@@ -0,0 +1,102 @@
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:local="clr-namespace:Tango.Scripting.Editors"
+ xmlns:fa="http://schemas.fontawesome.io/icons/"
+ xmlns:editing="clr-namespace:Tango.Scripting.Editors.Editing"
+ xmlns:folding="clr-namespace:Tango.Scripting.Editors.Folding">
+
+ <ResourceDictionary.MergedDictionaries>
+ <ResourceDictionary Source="/Tango.Scripting.Editors;component/TextEditor.xaml" />
+ <ResourceDictionary Source="/Tango.Scripting.Editors;component/Search/DropDownButton.xaml" />
+ <ResourceDictionary Source="/Tango.Scripting.Editors;component/Search/SearchPanel.xaml" />
+ <ResourceDictionary Source="/Tango.Scripting.Editors;component/CodeCompletion/CompletionList.xaml" />
+ <ResourceDictionary Source="/Tango.Scripting.Editors;component/CodeCompletion/InsightWindow.xaml" />
+ </ResourceDictionary.MergedDictionaries>
+
+ <!--Colors-->
+ <Color x:Key="ScriptBackground">#1E1E1E</Color>
+ <Color x:Key="ScriptForeground">#E6E6E6</Color>
+ <Color x:Key="ScriptFoldingForeground">#A0A0A0</Color>
+ <Color x:Key="ScriptLineNumberForeground">#2B91AF</Color>
+
+ <!--Brushes-->
+ <SolidColorBrush x:Key="ScriptBackgroundBrush" Color="{StaticResource ScriptBackground}"></SolidColorBrush>
+ <SolidColorBrush x:Key="ScriptForegroundBrush" Color="{StaticResource ScriptForeground}"></SolidColorBrush>
+ <SolidColorBrush x:Key="ScriptFoldingForegroundBrush" Color="{StaticResource ScriptFoldingForeground}"></SolidColorBrush>
+ <SolidColorBrush x:Key="ScriptLineNumberForegroundBrush" Color="{StaticResource ScriptLineNumberForeground}"></SolidColorBrush>
+
+
+ <Style TargetType="{x:Type folding:FoldingMargin}">
+ <Setter Property="FoldingMarkerBackgroundBrush" Value="{StaticResource ScriptBackgroundBrush}"></Setter>
+ <Setter Property="FoldingMarkerBrush" Value="{StaticResource ScriptFoldingForegroundBrush}"></Setter>
+ <Setter Property="SelectedFoldingMarkerBackgroundBrush" Value="{StaticResource ScriptBackgroundBrush}"></Setter>
+ <Setter Property="SelectedFoldingMarkerBrush" Value="{StaticResource ScriptFoldingForegroundBrush}"></Setter>
+ <Setter Property="Control.Cursor" Value="/Tango.Scripting.Editors;component/themes/RightArrow.cur"/>
+ </Style>
+
+ <Style TargetType="{x:Type editing:LineNumberMargin}">
+ <Setter Property="Width" Value="55"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource ScriptLineNumberForegroundBrush}"></Setter>
+ <Setter Property="Control.Cursor" Value="/Tango.Scripting.Editors;component/themes/RightArrow.cur"/>
+ </Style>
+
+ <Style TargetType="{x:Type local:ScriptEditor}">
+ <Setter Property="Background" Value="{StaticResource ScriptBackgroundBrush}"></Setter>
+ <Setter Property="Foreground" Value="{StaticResource ScriptForegroundBrush}"></Setter>
+ <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"></Setter>
+ <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"></Setter>
+ <Setter Property="FontFamily" Value="Consolas"></Setter>
+ <Setter Property="FontSize" Value="13"></Setter>
+ <Setter Property="SyntaxHighlighting" Value="C#"></Setter>
+ <Setter Property="ShowLineNumbers" Value="True"></Setter>
+ <Setter Property="ContextMenu">
+ <Setter.Value>
+ <ContextMenu>
+ <ContextMenu.Resources>
+ <Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
+ <Setter Property="Foreground" Value="Gainsboro"></Setter>
+ </Style>
+ </ContextMenu.Resources>
+ <MenuItem Header="Cut" MinWidth="150" Command="Cut">
+ <MenuItem.Icon>
+ <fa:ImageAwesome Icon="Cut" Width="12" Foreground="Gainsboro" Margin="2" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <Separator/>
+ <MenuItem Header="Copy" Command="Copy">
+ <MenuItem.Icon>
+ <fa:ImageAwesome Icon="Copy" Width="12" Foreground="Gainsboro" Margin="2" />
+ </MenuItem.Icon>
+ </MenuItem>
+ <MenuItem Header="Paste" Command="Paste">
+ <MenuItem.Icon>
+ <fa:ImageAwesome Icon="Paste" Width="12" Foreground="Gainsboro" Margin="2" />
+ </MenuItem.Icon>
+ </MenuItem>
+ </ContextMenu>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type local:ScriptEditor}">
+ <Border Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}">
+
+ <ScrollViewer
+ Focusable="False"
+ Name="PART_ScrollViewer"
+ CanContentScroll="True"
+ VerticalScrollBarVisibility="{TemplateBinding VerticalScrollBarVisibility}"
+ HorizontalScrollBarVisibility="{TemplateBinding HorizontalScrollBarVisibility}"
+ Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TextArea}"
+ VerticalContentAlignment="Top"
+ HorizontalContentAlignment="Left"
+ Background="{TemplateBinding Background}"
+ Padding="{TemplateBinding Padding}"/>
+ </Border>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+</ResourceDictionary>
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/RightArrow.cur b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/RightArrow.cur
new file mode 100644
index 000000000..5691efbaf
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Themes/RightArrow.cur
Binary files differ
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Boxes.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Boxes.cs
new file mode 100644
index 000000000..517948bae
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Boxes.cs
@@ -0,0 +1,21 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Reuse the same instances for boxed booleans.
+ /// </summary>
+ static class Boxes
+ {
+ public static readonly object True = true;
+ public static readonly object False = false;
+
+ public static object Box(bool value)
+ {
+ return value ? True : False;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/BusyManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/BusyManager.cs
new file mode 100644
index 000000000..8551ff49e
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/BusyManager.cs
@@ -0,0 +1,55 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// This class is used to prevent stack overflows by representing a 'busy' flag
+ /// that prevents reentrance when another call is running.
+ /// However, using a simple 'bool busy' is not thread-safe, so we use a
+ /// thread-static BusyManager.
+ /// </summary>
+ static class BusyManager
+ {
+ public struct BusyLock : IDisposable
+ {
+ public static readonly BusyLock Failed = new BusyLock(null);
+
+ readonly List<object> objectList;
+
+ public BusyLock(List<object> objectList)
+ {
+ this.objectList = objectList;
+ }
+
+ public bool Success {
+ get { return objectList != null; }
+ }
+
+ public void Dispose()
+ {
+ if (objectList != null) {
+ objectList.RemoveAt(objectList.Count - 1);
+ }
+ }
+ }
+
+ [ThreadStatic] static List<object> _activeObjects;
+
+ public static BusyLock Enter(object obj)
+ {
+ List<object> activeObjects = _activeObjects;
+ if (activeObjects == null)
+ activeObjects = _activeObjects = new List<object>();
+ for (int i = 0; i < activeObjects.Count; i++) {
+ if (activeObjects[i] == obj)
+ return BusyLock.Failed;
+ }
+ activeObjects.Add(obj);
+ return new BusyLock(activeObjects);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CallbackOnDispose.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CallbackOnDispose.cs
new file mode 100644
index 000000000..a6eebf2f1
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CallbackOnDispose.cs
@@ -0,0 +1,35 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Invokes an action when it is disposed.
+ /// </summary>
+ /// <remarks>
+ /// This class ensures the callback is invoked at most once,
+ /// even when Dispose is called on multiple threads.
+ /// </remarks>
+ sealed class CallbackOnDispose : IDisposable
+ {
+ Action action;
+
+ public CallbackOnDispose(Action action)
+ {
+ Debug.Assert(action != null);
+ this.action = action;
+ }
+
+ public void Dispose()
+ {
+ Action a = Interlocked.Exchange(ref action, null);
+ if (a != null) {
+ a();
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CharRope.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CharRope.cs
new file mode 100644
index 000000000..844ab9544
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CharRope.cs
@@ -0,0 +1,172 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Poor man's template specialization: extension methods for Rope&lt;char&gt;.
+ /// </summary>
+ public static class CharRope
+ {
+ /// <summary>
+ /// Creates a new rope from the specified text.
+ /// </summary>
+ public static Rope<char> Create(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ return new Rope<char>(InitFromString(text));
+ }
+
+ /// <summary>
+ /// Retrieves the text for a portion of the rope.
+ /// Runs in O(lg N + M), where M=<paramref name="length"/>.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public static string ToString(this Rope<char> rope, int startIndex, int length)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ if (length == 0)
+ return string.Empty;
+ char[] buffer = new char[length];
+ rope.CopyTo(startIndex, buffer, 0, length);
+ return new string(buffer);
+ }
+
+ /// <summary>
+ /// Retrieves the text for a portion of the rope and writes it to the specified string builder.
+ /// Runs in O(lg N + M), where M=<paramref name="length"/>.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public static void WriteTo(this Rope<char> rope, StringBuilder output, int startIndex, int length)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ if (output == null)
+ throw new ArgumentNullException("output");
+ rope.VerifyRange(startIndex, length);
+ rope.root.WriteTo(startIndex, output, length);
+ }
+
+ /// <summary>
+ /// Appends text to this rope.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ public static void AddText(this Rope<char> rope, string text)
+ {
+ InsertText(rope, rope.Length, text);
+ }
+
+ /// <summary>
+ /// Inserts text into this rope.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public static void InsertText(this Rope<char> rope, int index, string text)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ rope.InsertRange(index, text.ToCharArray(), 0, text.Length);
+ /*if (index < 0 || index > rope.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index <= " + rope.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (text == null)
+ throw new ArgumentNullException("text");
+ if (text.Length == 0)
+ return;
+ rope.root = rope.root.Insert(index, text);
+ rope.OnChanged();*/
+ }
+
+ internal static RopeNode<char> InitFromString(string text)
+ {
+ if (text.Length == 0) {
+ return RopeNode<char>.emptyRopeNode;
+ }
+ RopeNode<char> node = RopeNode<char>.CreateNodes(text.Length);
+ FillNode(node, text, 0);
+ return node;
+ }
+
+ static void FillNode(RopeNode<char> node, string text, int start)
+ {
+ if (node.contents != null) {
+ text.CopyTo(start, node.contents, 0, node.length);
+ } else {
+ FillNode(node.left, text, start);
+ FillNode(node.right, text, start + node.left.length);
+ }
+ }
+
+ internal static void WriteTo(this RopeNode<char> node, int index, StringBuilder output, int count)
+ {
+ if (node.height == 0) {
+ if (node.contents == null) {
+ // function node
+ node.GetContentNode().WriteTo(index, output, count);
+ } else {
+ // leaf node: append data
+ output.Append(node.contents, index, count);
+ }
+ } else {
+ // concat node: do recursive calls
+ if (index + count <= node.left.length) {
+ node.left.WriteTo(index, output, count);
+ } else if (index >= node.left.length) {
+ node.right.WriteTo(index - node.left.length, output, count);
+ } else {
+ int amountInLeft = node.left.length - index;
+ node.left.WriteTo(index, output, amountInLeft);
+ node.right.WriteTo(0, output, count - amountInLeft);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the index of the first occurrence of any element in the specified array.
+ /// </summary>
+ /// <param name="rope">The target rope.</param>
+ /// <param name="anyOf">Array of characters being searched.</param>
+ /// <param name="startIndex">Start index of the search.</param>
+ /// <param name="length">Length of the area to search.</param>
+ /// <returns>The first index where any character was found; or -1 if no occurrence was found.</returns>
+ public static int IndexOfAny(this Rope<char> rope, char[] anyOf, int startIndex, int length)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+ if (anyOf == null)
+ throw new ArgumentNullException("anyOf");
+ rope.VerifyRange(startIndex, length);
+
+ while (length > 0) {
+ var entry = rope.FindNodeUsingCache(startIndex).UnsafePeek();
+ char[] contents = entry.node.contents;
+ int startWithinNode = startIndex - entry.nodeStartIndex;
+ int nodeLength = Math.Min(entry.node.length, startWithinNode + length);
+ for (int i = startIndex - entry.nodeStartIndex; i < nodeLength; i++) {
+ char element = contents[i];
+ foreach (char needle in anyOf) {
+ if (element == needle)
+ return entry.nodeStartIndex + i;
+ }
+ }
+ length -= nodeLength - startWithinNode;
+ startIndex = entry.nodeStartIndex + nodeLength;
+ }
+ return -1;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CompressingTreeList.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CompressingTreeList.cs
new file mode 100644
index 000000000..42f5007c9
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/CompressingTreeList.cs
@@ -0,0 +1,882 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// A IList{T} implementation that has efficient insertion and removal (in O(lg n) time)
+ /// and that saves memory by allocating only one node when a value is repeated in adjacent indices.
+ /// Based on this "compression", it also supports efficient InsertRange/SetRange/RemoveRange operations.
+ /// </summary>
+ /// <remarks>
+ /// Current memory usage: 5*IntPtr.Size + 12 + sizeof(T) per node.
+ /// Use this class only if lots of adjacent values are identical (can share one node).
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix",
+ Justification = "It's an IList<T> implementation")]
+ public sealed class CompressingTreeList<T> : IList<T>
+ {
+ // Further memory optimization: this tree could work without parent pointers. But that
+ // requires changing most of tree manipulating logic.
+ // Also possible is to remove the count field and calculate it as totalCount-left.totalCount-right.totalCount
+ // - but that would make tree manipulations more difficult to handle.
+
+ #region Node definition
+ sealed class Node
+ {
+ internal Node left, right, parent;
+ internal bool color;
+ internal int count, totalCount;
+ internal T value;
+
+ public Node(T value, int count)
+ {
+ this.value = value;
+ this.count = count;
+ this.totalCount = count;
+ }
+
+ internal Node LeftMost {
+ get {
+ Node node = this;
+ while (node.left != null)
+ node = node.left;
+ return node;
+ }
+ }
+
+ internal Node RightMost {
+ get {
+ Node node = this;
+ while (node.right != null)
+ node = node.right;
+ return node;
+ }
+ }
+
+ /// <summary>
+ /// Gets the inorder predecessor of the node.
+ /// </summary>
+ internal Node Predecessor {
+ get {
+ if (left != null) {
+ return left.RightMost;
+ } else {
+ Node node = this;
+ Node oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // go up until we are coming out of a right subtree
+ } while (node != null && node.left == oldNode);
+ return node;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the inorder successor of the node.
+ /// </summary>
+ internal Node Successor {
+ get {
+ if (right != null) {
+ return right.LeftMost;
+ } else {
+ Node node = this;
+ Node oldNode;
+ do {
+ oldNode = node;
+ node = node.parent;
+ // go up until we are coming out of a left subtree
+ } while (node != null && node.right == oldNode);
+ return node;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return "[TotalCount=" + totalCount + " Count=" + count + " Value=" + value + "]";
+ }
+ }
+ #endregion
+
+ #region Fields and Constructor
+ readonly Func<T, T, bool> comparisonFunc;
+ Node root;
+
+ /// <summary>
+ /// Creates a new CompressingTreeList instance.
+ /// </summary>
+ /// <param name="comparisonFunc">A function that checks two values for equality. If this
+ /// function returns true, a single node may be used to store the two values.</param>
+ public CompressingTreeList(Func<T, T, bool> comparisonFunc)
+ {
+ if (comparisonFunc == null)
+ throw new ArgumentNullException("comparisonFunc");
+ this.comparisonFunc = comparisonFunc;
+ }
+ #endregion
+
+ #region InsertRange
+ /// <summary>
+ /// Inserts <paramref name="item"/> <paramref name="count"/> times at position
+ /// <paramref name="index"/>.
+ /// </summary>
+ public void InsertRange(int index, int count, T item)
+ {
+ if (index < 0 || index > this.Count)
+ throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + this.Count);
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", count, "Value must not be negative");
+ if (count == 0)
+ return;
+ unchecked {
+ if (this.Count + count < 0)
+ throw new OverflowException("Cannot insert elements: total number of elements must not exceed int.MaxValue.");
+ }
+
+ if (root == null) {
+ root = new Node(item, count);
+ } else {
+ Node n = GetNode(ref index);
+ // check if we can put the value into the node n:
+ if (comparisonFunc(n.value, item)) {
+ n.count += count;
+ UpdateAugmentedData(n);
+ } else if (index == n.count) {
+ // this can only happen when appending at the end
+ Debug.Assert(n == root.RightMost);
+ InsertAsRight(n, new Node(item, count));
+ } else if (index == 0) {
+ // insert before:
+ // maybe we can put the value in the previous node?
+ Node p = n.Predecessor;
+ if (p != null && comparisonFunc(p.value, item)) {
+ p.count += count;
+ UpdateAugmentedData(p);
+ } else {
+ InsertBefore(n, new Node(item, count));
+ }
+ } else {
+ Debug.Assert(index > 0 && index < n.count);
+ // insert in the middle:
+ // split n into a new node and n
+ n.count -= index;
+ InsertBefore(n, new Node(n.value, index));
+ // then insert the new item in between
+ InsertBefore(n, new Node(item, count));
+ UpdateAugmentedData(n);
+ }
+ }
+ CheckProperties();
+ }
+
+ void InsertBefore(Node node, Node newNode)
+ {
+ if (node.left == null) {
+ InsertAsLeft(node, newNode);
+ } else {
+ InsertAsRight(node.left.RightMost, newNode);
+ }
+ }
+ #endregion
+
+ #region RemoveRange
+ /// <summary>
+ /// Removes <paramref name="count"/> items starting at position
+ /// <paramref name="index"/>.
+ /// </summary>
+ public void RemoveRange(int index, int count)
+ {
+ if (index < 0 || index > this.Count)
+ throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + this.Count);
+ if (count < 0 || index + count > this.Count)
+ throw new ArgumentOutOfRangeException("count", count, "0 <= length, index(" + index + ")+count <= " + this.Count);
+ if (count == 0)
+ return;
+
+ Node n = GetNode(ref index);
+ if (index + count < n.count) {
+ // just remove inside a single node
+ n.count -= count;
+ UpdateAugmentedData(n);
+ } else {
+ // keep only the part of n from 0 to index
+ Node firstNodeBeforeDeletedRange;
+ if (index > 0) {
+ count -= (n.count - index);
+ n.count = index;
+ UpdateAugmentedData(n);
+ firstNodeBeforeDeletedRange = n;
+ n = n.Successor;
+ } else {
+ Debug.Assert(index == 0);
+ firstNodeBeforeDeletedRange = n.Predecessor;
+ }
+ while (n != null && count >= n.count) {
+ count -= n.count;
+ Node s = n.Successor;
+ RemoveNode(n);
+ n = s;
+ }
+ if (count > 0) {
+ Debug.Assert(n != null && count < n.count);
+ n.count -= count;
+ UpdateAugmentedData(n);
+ }
+ if (n != null) {
+ Debug.Assert(n.Predecessor == firstNodeBeforeDeletedRange);
+ if (firstNodeBeforeDeletedRange != null && comparisonFunc(firstNodeBeforeDeletedRange.value, n.value)) {
+ firstNodeBeforeDeletedRange.count += n.count;
+ RemoveNode(n);
+ UpdateAugmentedData(firstNodeBeforeDeletedRange);
+ }
+ }
+ }
+
+ CheckProperties();
+ }
+ #endregion
+
+ #region SetRange
+ /// <summary>
+ /// Sets <paramref name="count"/> indices starting at <paramref name="index"/> to
+ /// <paramref name="item"/>
+ /// </summary>
+ public void SetRange(int index, int count, T item)
+ {
+ RemoveRange(index, count);
+ InsertRange(index, count, item);
+ }
+ #endregion
+
+ #region GetNode
+ Node GetNode(ref int index)
+ {
+ Node node = root;
+ while (true) {
+ if (node.left != null && index < node.left.totalCount) {
+ node = node.left;
+ } else {
+ if (node.left != null) {
+ index -= node.left.totalCount;
+ }
+ if (index < node.count || node.right == null)
+ return node;
+ index -= node.count;
+ node = node.right;
+ }
+ }
+ }
+ #endregion
+
+ #region UpdateAugmentedData
+ void UpdateAugmentedData(Node node)
+ {
+ int totalCount = node.count;
+ if (node.left != null) totalCount += node.left.totalCount;
+ if (node.right != null) totalCount += node.right.totalCount;
+ if (node.totalCount != totalCount) {
+ node.totalCount = totalCount;
+ if (node.parent != null)
+ UpdateAugmentedData(node.parent);
+ }
+ }
+ #endregion
+
+ #region IList<T> implementation
+ /// <summary>
+ /// Gets or sets an item by index.
+ /// </summary>
+ public T this[int index] {
+ get {
+ if (index < 0 || index >= this.Count)
+ throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
+ return GetNode(ref index).value;
+ }
+ set {
+ RemoveAt(index);
+ Insert(index, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of items in the list.
+ /// </summary>
+ public int Count {
+ get {
+ if (root != null)
+ return root.totalCount;
+ else
+ return 0;
+ }
+ }
+
+ bool ICollection<T>.IsReadOnly {
+ get {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets the index of the specified <paramref name="item"/>.
+ /// </summary>
+ public int IndexOf(T item)
+ {
+ int index = 0;
+ if (root != null) {
+ Node n = root.LeftMost;
+ while (n != null) {
+ if (comparisonFunc(n.value, item))
+ return index;
+ index += n.count;
+ n = n.Successor;
+ }
+ }
+ Debug.Assert(index == this.Count);
+ return -1;
+ }
+
+ /// <summary>
+ /// Gets the the first index so that all values from the result index to <paramref name="index"/>
+ /// are equal.
+ /// </summary>
+ public int GetStartOfRun(int index)
+ {
+ if (index < 0 || index >= this.Count)
+ throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
+ int indexInRun = index;
+ GetNode(ref indexInRun);
+ return index - indexInRun;
+ }
+
+ /// <summary>
+ /// Gets the first index after <paramref name="index"/> so that the value at the result index is not
+ /// equal to the value at <paramref name="index"/>.
+ /// That is, this method returns the exclusive end index of the run of equal values.
+ /// </summary>
+ public int GetEndOfRun(int index)
+ {
+ if (index < 0 || index >= this.Count)
+ throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
+ int indexInRun = index;
+ int runLength = GetNode(ref indexInRun).count;
+ return index - indexInRun + runLength;
+ }
+
+ /// <summary>
+ /// Gets the number of elements after <paramref name="index"/> that have the same value as each other.
+ /// </summary>
+ [Obsolete("This method may be confusing as it returns only the remaining run length after index. " +
+ "Use GetStartOfRun/GetEndOfRun instead.")]
+ public int GetRunLength(int index)
+ {
+ if (index < 0 || index >= this.Count)
+ throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
+ return GetNode(ref index).count - index;
+ }
+
+ /// <summary>
+ /// Applies the conversion function to all elements in this CompressingTreeList.
+ /// </summary>
+ public void Transform(Func<T, T> converter)
+ {
+ if (root == null)
+ return;
+ Node prevNode = null;
+ for (Node n = root.LeftMost; n != null; n = n.Successor) {
+ n.value = converter(n.value);
+ if (prevNode != null && comparisonFunc(prevNode.value, n.value)) {
+ n.count += prevNode.count;
+ UpdateAugmentedData(n);
+ RemoveNode(prevNode);
+ }
+ prevNode = n;
+ }
+ }
+
+
+ /// <summary>
+ /// Inserts the specified <paramref name="item"/> at <paramref name="index"/>
+ /// </summary>
+ public void Insert(int index, T item)
+ {
+ InsertRange(index, 1, item);
+ }
+
+ /// <summary>
+ /// Removes one item at <paramref name="index"/>
+ /// </summary>
+ public void RemoveAt(int index)
+ {
+ RemoveRange(index, 1);
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="item"/> to the end of the list.
+ /// </summary>
+ public void Add(T item)
+ {
+ InsertRange(this.Count, 1, item);
+ }
+
+ /// <summary>
+ /// Removes all items from this list.
+ /// </summary>
+ public void Clear()
+ {
+ root = null;
+ }
+
+ /// <summary>
+ /// Gets whether this list contains the specified item.
+ /// </summary>
+ public bool Contains(T item)
+ {
+ return IndexOf(item) >= 0;
+ }
+
+ /// <summary>
+ /// Copies all items in this list to the specified array.
+ /// </summary>
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (array.Length < this.Count)
+ throw new ArgumentException("The array is too small", "array");
+ if (arrayIndex < 0 || arrayIndex + this.Count > array.Length)
+ throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, "Value must be between 0 and " + (array.Length - this.Count));
+ foreach (T v in this) {
+ array[arrayIndex++] = v;
+ }
+ }
+
+ /// <summary>
+ /// Removes the specified item from this list.
+ /// </summary>
+ public bool Remove(T item)
+ {
+ int index = IndexOf(item);
+ if (index >= 0) {
+ RemoveAt(index);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ #endregion
+
+ #region IEnumerable<T>
+ /// <summary>
+ /// Gets an enumerator for this list.
+ /// </summary>
+ public IEnumerator<T> GetEnumerator()
+ {
+ if (root != null) {
+ Node n = root.LeftMost;
+ while (n != null) {
+ for (int i = 0; i < n.count; i++) {
+ yield return n.value;
+ }
+ n = n.Successor;
+ }
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ #endregion
+
+ #region Red/Black Tree
+ internal const bool RED = true;
+ internal const bool BLACK = false;
+
+ void InsertAsLeft(Node parentNode, Node newNode)
+ {
+ Debug.Assert(parentNode.left == null);
+ parentNode.left = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAugmentedData(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void InsertAsRight(Node parentNode, Node newNode)
+ {
+ Debug.Assert(parentNode.right == null);
+ parentNode.right = newNode;
+ newNode.parent = parentNode;
+ newNode.color = RED;
+ UpdateAugmentedData(parentNode);
+ FixTreeOnInsert(newNode);
+ }
+
+ void FixTreeOnInsert(Node node)
+ {
+ Debug.Assert(node != null);
+ Debug.Assert(node.color == RED);
+ Debug.Assert(node.left == null || node.left.color == BLACK);
+ Debug.Assert(node.right == null || node.right.color == BLACK);
+
+ Node parentNode = node.parent;
+ if (parentNode == null) {
+ // we inserted in the root -> the node must be black
+ // since this is a root node, making the node black increments the number of black nodes
+ // on all paths by one, so it is still the same for all paths.
+ node.color = BLACK;
+ return;
+ }
+ if (parentNode.color == BLACK) {
+ // if the parent node where we inserted was black, our red node is placed correctly.
+ // since we inserted a red node, the number of black nodes on each path is unchanged
+ // -> the tree is still balanced
+ return;
+ }
+ // parentNode is red, so there is a conflict here!
+
+ // because the root is black, parentNode is not the root -> there is a grandparent node
+ Node grandparentNode = parentNode.parent;
+ Node uncleNode = Sibling(parentNode);
+ if (uncleNode != null && uncleNode.color == RED) {
+ parentNode.color = BLACK;
+ uncleNode.color = BLACK;
+ grandparentNode.color = RED;
+ FixTreeOnInsert(grandparentNode);
+ return;
+ }
+ // now we know: parent is red but uncle is black
+ // First rotation:
+ if (node == parentNode.right && parentNode == grandparentNode.left) {
+ RotateLeft(parentNode);
+ node = node.left;
+ } else if (node == parentNode.left && parentNode == grandparentNode.right) {
+ RotateRight(parentNode);
+ node = node.right;
+ }
+ // because node might have changed, reassign variables:
+ parentNode = node.parent;
+ grandparentNode = parentNode.parent;
+
+ // Now recolor a bit:
+ parentNode.color = BLACK;
+ grandparentNode.color = RED;
+ // Second rotation:
+ if (node == parentNode.left && parentNode == grandparentNode.left) {
+ RotateRight(grandparentNode);
+ } else {
+ // because of the first rotation, this is guaranteed:
+ Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right);
+ RotateLeft(grandparentNode);
+ }
+ }
+
+ void RemoveNode(Node removedNode)
+ {
+ if (removedNode.left != null && removedNode.right != null) {
+ // replace removedNode with it's in-order successor
+
+ Node leftMost = removedNode.right.LeftMost;
+ RemoveNode(leftMost); // remove leftMost from its current location
+
+ // and overwrite the removedNode with it
+ ReplaceNode(removedNode, leftMost);
+ leftMost.left = removedNode.left;
+ if (leftMost.left != null) leftMost.left.parent = leftMost;
+ leftMost.right = removedNode.right;
+ if (leftMost.right != null) leftMost.right.parent = leftMost;
+ leftMost.color = removedNode.color;
+
+ UpdateAugmentedData(leftMost);
+ if (leftMost.parent != null) UpdateAugmentedData(leftMost.parent);
+ return;
+ }
+
+ // now either removedNode.left or removedNode.right is null
+ // get the remaining child
+ Node parentNode = removedNode.parent;
+ Node childNode = removedNode.left ?? removedNode.right;
+ ReplaceNode(removedNode, childNode);
+ if (parentNode != null) UpdateAugmentedData(parentNode);
+ if (removedNode.color == BLACK) {
+ if (childNode != null && childNode.color == RED) {
+ childNode.color = BLACK;
+ } else {
+ FixTreeOnDelete(childNode, parentNode);
+ }
+ }
+ }
+
+ void FixTreeOnDelete(Node node, Node parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (parentNode == null)
+ return;
+
+ // warning: node may be null
+ Node sibling = Sibling(node, parentNode);
+ if (sibling.color == RED) {
+ parentNode.color = RED;
+ sibling.color = BLACK;
+ if (node == parentNode.left) {
+ RotateLeft(parentNode);
+ } else {
+ RotateRight(parentNode);
+ }
+
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+ }
+
+ if (parentNode.color == BLACK
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ FixTreeOnDelete(parentNode, parentNode.parent);
+ return;
+ }
+
+ if (parentNode.color == RED
+ && sibling.color == BLACK
+ && GetColor(sibling.left) == BLACK
+ && GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ parentNode.color = BLACK;
+ return;
+ }
+
+ if (node == parentNode.left &&
+ sibling.color == BLACK &&
+ GetColor(sibling.left) == RED &&
+ GetColor(sibling.right) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.left.color = BLACK;
+ RotateRight(sibling);
+ }
+ else if (node == parentNode.right &&
+ sibling.color == BLACK &&
+ GetColor(sibling.right) == RED &&
+ GetColor(sibling.left) == BLACK)
+ {
+ sibling.color = RED;
+ sibling.right.color = BLACK;
+ RotateLeft(sibling);
+ }
+ sibling = Sibling(node, parentNode); // update value of sibling after rotation
+
+ sibling.color = parentNode.color;
+ parentNode.color = BLACK;
+ if (node == parentNode.left) {
+ if (sibling.right != null) {
+ Debug.Assert(sibling.right.color == RED);
+ sibling.right.color = BLACK;
+ }
+ RotateLeft(parentNode);
+ } else {
+ if (sibling.left != null) {
+ Debug.Assert(sibling.left.color == RED);
+ sibling.left.color = BLACK;
+ }
+ RotateRight(parentNode);
+ }
+ }
+
+ void ReplaceNode(Node replacedNode, Node newNode)
+ {
+ if (replacedNode.parent == null) {
+ Debug.Assert(replacedNode == root);
+ root = newNode;
+ } else {
+ if (replacedNode.parent.left == replacedNode)
+ replacedNode.parent.left = newNode;
+ else
+ replacedNode.parent.right = newNode;
+ }
+ if (newNode != null) {
+ newNode.parent = replacedNode.parent;
+ }
+ replacedNode.parent = null;
+ }
+
+ void RotateLeft(Node p)
+ {
+ // let q be p's right child
+ Node q = p.right;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's right child to be q's left child
+ p.right = q.left;
+ if (p.right != null) p.right.parent = p;
+ // set q's left child to be p
+ q.left = p;
+ p.parent = q;
+ UpdateAugmentedData(p);
+ UpdateAugmentedData(q);
+ }
+
+ void RotateRight(Node p)
+ {
+ // let q be p's left child
+ Node q = p.left;
+ Debug.Assert(q != null);
+ Debug.Assert(q.parent == p);
+ // set q to be the new root
+ ReplaceNode(p, q);
+
+ // set p's left child to be q's right child
+ p.left = q.right;
+ if (p.left != null) p.left.parent = p;
+ // set q's right child to be p
+ q.right = p;
+ p.parent = q;
+ UpdateAugmentedData(p);
+ UpdateAugmentedData(q);
+ }
+
+ static Node Sibling(Node node)
+ {
+ if (node == node.parent.left)
+ return node.parent.right;
+ else
+ return node.parent.left;
+ }
+
+ static Node Sibling(Node node, Node parentNode)
+ {
+ Debug.Assert(node == null || node.parent == parentNode);
+ if (node == parentNode.left)
+ return parentNode.right;
+ else
+ return parentNode.left;
+ }
+
+ static bool GetColor(Node node)
+ {
+ return node != null ? node.color : BLACK;
+ }
+ #endregion
+
+ #region CheckProperties
+ [Conditional("DATACONSISTENCYTEST")]
+ internal void CheckProperties()
+ {
+ #if DEBUG
+ if (root != null) {
+ CheckProperties(root);
+
+ // check red-black property:
+ int blackCount = -1;
+ CheckNodeProperties(root, null, RED, 0, ref blackCount);
+
+ // ensure that the tree is compressed:
+ Node p = root.LeftMost;
+ Node n = p.Successor;
+ while (n != null) {
+ Debug.Assert(!comparisonFunc(p.value, n.value));
+ p = n;
+ n = p.Successor;
+ }
+ }
+ #endif
+ }
+
+ #if DEBUG
+ void CheckProperties(Node node)
+ {
+ Debug.Assert(node.count > 0);
+ int totalCount = node.count;
+ if (node.left != null) {
+ CheckProperties(node.left);
+ totalCount += node.left.totalCount;
+ }
+ if (node.right != null) {
+ CheckProperties(node.right);
+ totalCount += node.right.totalCount;
+ }
+ Debug.Assert(node.totalCount == totalCount);
+ }
+
+ /*
+ 1. A node is either red or black.
+ 2. The root is black.
+ 3. All leaves are black. (The leaves are the NIL children.)
+ 4. Both children of every red node are black. (So every red node must have a black parent.)
+ 5. Every simple path from a node to a descendant leaf contains the same number of black nodes. (Not counting the leaf node.)
+ */
+ void CheckNodeProperties(Node node, Node parentNode, bool parentColor, int blackCount, ref int expectedBlackCount)
+ {
+ if (node == null) return;
+
+ Debug.Assert(node.parent == parentNode);
+
+ if (parentColor == RED) {
+ Debug.Assert(node.color == BLACK);
+ }
+ if (node.color == BLACK) {
+ blackCount++;
+ }
+ if (node.left == null && node.right == null) {
+ // node is a leaf node:
+ if (expectedBlackCount == -1)
+ expectedBlackCount = blackCount;
+ else
+ Debug.Assert(expectedBlackCount == blackCount);
+ }
+ CheckNodeProperties(node.left, node, node.color, blackCount, ref expectedBlackCount);
+ CheckNodeProperties(node.right, node, node.color, blackCount, ref expectedBlackCount);
+ }
+ #endif
+ #endregion
+
+ #region GetTreeAsString
+ internal string GetTreeAsString()
+ {
+ #if DEBUG
+ if (root == null)
+ return "<empty tree>";
+ StringBuilder b = new StringBuilder();
+ AppendTreeToString(root, b, 0);
+ return b.ToString();
+ #else
+ return "Not available in release build.";
+ #endif
+ }
+
+ #if DEBUG
+ static void AppendTreeToString(Node node, StringBuilder b, int indent)
+ {
+ if (node.color == RED)
+ b.Append("RED ");
+ else
+ b.Append("BLACK ");
+ b.AppendLine(node.ToString());
+ indent += 2;
+ if (node.left != null) {
+ b.Append(' ', indent);
+ b.Append("L: ");
+ AppendTreeToString(node.left, b, indent);
+ }
+ if (node.right != null) {
+ b.Append(' ', indent);
+ b.Append("R: ");
+ AppendTreeToString(node.right, b, indent);
+ }
+ }
+ #endif
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Constants.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Constants.cs
new file mode 100644
index 000000000..0344ada80
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Constants.cs
@@ -0,0 +1,15 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ static class Constants
+ {
+ /// <summary>
+ /// Multiply with this constant to convert from points to device-independent pixels.
+ /// </summary>
+ public const double PixelPerPoint = 4 / 3.0;
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/DelayedEvents.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/DelayedEvents.cs
new file mode 100644
index 000000000..8afc9f2ee
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/DelayedEvents.cs
@@ -0,0 +1,48 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Maintains a list of delayed events to raise.
+ /// </summary>
+ sealed class DelayedEvents
+ {
+ struct EventCall
+ {
+ EventHandler handler;
+ object sender;
+ EventArgs e;
+
+ public EventCall(EventHandler handler, object sender, EventArgs e)
+ {
+ this.handler = handler;
+ this.sender = sender;
+ this.e = e;
+ }
+
+ public void Call()
+ {
+ handler(sender, e);
+ }
+ }
+
+ Queue<EventCall> eventCalls = new Queue<EventCall>();
+
+ public void DelayedRaise(EventHandler handler, object sender, EventArgs e)
+ {
+ if (handler != null) {
+ eventCalls.Enqueue(new EventCall(handler, sender, e));
+ }
+ }
+
+ public void RaiseEvents()
+ {
+ while (eventCalls.Count > 0)
+ eventCalls.Dequeue().Call();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Deque.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Deque.cs
new file mode 100644
index 000000000..66496d68f
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Deque.cs
@@ -0,0 +1,174 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Double-ended queue.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+ [Serializable]
+ public sealed class Deque<T> : ICollection<T>
+ {
+ T[] arr = Empty<T>.Array;
+ int size, head, tail;
+
+ /// <inheritdoc/>
+ public int Count {
+ get { return size; }
+ }
+
+ /// <inheritdoc/>
+ public void Clear()
+ {
+ arr = Empty<T>.Array;
+ size = 0;
+ head = 0;
+ tail = 0;
+ }
+
+ /// <summary>
+ /// Gets/Sets an element inside the deque.
+ /// </summary>
+ public T this[int index] {
+ get {
+ ThrowUtil.CheckInRangeInclusive(index, "index", 0, size - 1);
+ return arr[(head + index) % arr.Length];
+ }
+ set {
+ ThrowUtil.CheckInRangeInclusive(index, "index", 0, size - 1);
+ arr[(head + index) % arr.Length] = value;
+ }
+ }
+
+ /// <summary>
+ /// Adds an element to the end of the deque.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "PushBack")]
+ public void PushBack(T item)
+ {
+ if (size == arr.Length)
+ SetCapacity(Math.Max(4, arr.Length * 2));
+ arr[tail++] = item;
+ if (tail == arr.Length) tail = 0;
+ size++;
+ }
+
+ /// <summary>
+ /// Pops an element from the end of the deque.
+ /// </summary>
+ public T PopBack()
+ {
+ if (size == 0)
+ throw new InvalidOperationException();
+ if (tail == 0)
+ tail = arr.Length - 1;
+ else
+ tail--;
+ T val = arr[tail];
+ arr[tail] = default(T); // allow GC to collect the element
+ size--;
+ return val;
+ }
+
+ /// <summary>
+ /// Adds an element to the front of the deque.
+ /// </summary>
+ public void PushFront(T item)
+ {
+ if (size == arr.Length)
+ SetCapacity(Math.Max(4, arr.Length * 2));
+ if (head == 0)
+ head = arr.Length - 1;
+ else
+ head--;
+ arr[head] = item;
+ size++;
+ }
+
+ /// <summary>
+ /// Pops an element from the end of the deque.
+ /// </summary>
+ public T PopFront()
+ {
+ if (size == 0)
+ throw new InvalidOperationException();
+ T val = arr[head];
+ arr[head] = default(T); // allow GC to collect the element
+ head++;
+ if (head == arr.Length) head = 0;
+ size--;
+ return val;
+ }
+
+ void SetCapacity(int capacity)
+ {
+ T[] newArr = new T[capacity];
+ CopyTo(newArr, 0);
+ head = 0;
+ tail = (size == capacity) ? 0 : size;
+ arr = newArr;
+ }
+
+ /// <inheritdoc/>
+ public IEnumerator<T> GetEnumerator()
+ {
+ if (head < tail) {
+ for (int i = head; i < tail; i++)
+ yield return arr[i];
+ } else {
+ for (int i = head; i < arr.Length; i++)
+ yield return arr[i];
+ for (int i = 0; i < tail; i++)
+ yield return arr[i];
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ bool ICollection<T>.IsReadOnly {
+ get { return false; }
+ }
+
+ void ICollection<T>.Add(T item)
+ {
+ PushBack(item);
+ }
+
+ /// <inheritdoc/>
+ public bool Contains(T item)
+ {
+ EqualityComparer<T> comparer = EqualityComparer<T>.Default;
+ foreach (T element in this)
+ if (comparer.Equals(item, element))
+ return true;
+ return false;
+ }
+
+ /// <inheritdoc/>
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (head < tail) {
+ Array.Copy(arr, head, array, arrayIndex, tail - head);
+ } else {
+ int num1 = arr.Length - head;
+ Array.Copy(arr, head, array, arrayIndex, num1);
+ Array.Copy(arr, 0, array, arrayIndex + num1, tail);
+ }
+ }
+
+ bool ICollection<T>.Remove(T item)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Empty.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Empty.cs
new file mode 100644
index 000000000..1f890dd7a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Empty.cs
@@ -0,0 +1,17 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.ObjectModel;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Provides immutable empty list instances.
+ /// </summary>
+ static class Empty<T>
+ {
+ public static readonly T[] Array = new T[0];
+ //public static readonly ReadOnlyCollection<T> ReadOnlyCollection = new ReadOnlyCollection<T>(Array);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ExtensionMethods.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ExtensionMethods.cs
new file mode 100644
index 000000000..d9b00ed33
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ExtensionMethods.cs
@@ -0,0 +1,214 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Xml;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ static class ExtensionMethods
+ {
+ #region Epsilon / IsClose / CoerceValue
+ /// <summary>
+ /// Epsilon used for <c>IsClose()</c> implementations.
+ /// We can use up quite a few digits in front of the decimal point (due to visual positions being relative to document origin),
+ /// and there's no need to be too accurate (we're dealing with pixels here),
+ /// so we will use the value 0.01.
+ /// Previosly we used 1e-8 but that was causing issues:
+ /// http://community.sharpdevelop.net/forums/t/16048.aspx
+ /// </summary>
+ public const double Epsilon = 0.01;
+
+ /// <summary>
+ /// Returns true if the doubles are close (difference smaller than 0.01).
+ /// </summary>
+ public static bool IsClose(this double d1, double d2)
+ {
+ if (d1 == d2) // required for infinities
+ return true;
+ return Math.Abs(d1 - d2) < Epsilon;
+ }
+
+ /// <summary>
+ /// Returns true if the doubles are close (difference smaller than 0.01).
+ /// </summary>
+ public static bool IsClose(this Size d1, Size d2)
+ {
+ return IsClose(d1.Width, d2.Width) && IsClose(d1.Height, d2.Height);
+ }
+
+ /// <summary>
+ /// Returns true if the doubles are close (difference smaller than 0.01).
+ /// </summary>
+ public static bool IsClose(this Vector d1, Vector d2)
+ {
+ return IsClose(d1.X, d2.X) && IsClose(d1.Y, d2.Y);
+ }
+
+ /// <summary>
+ /// Forces the value to stay between mininum and maximum.
+ /// </summary>
+ /// <returns>minimum, if value is less than minimum.
+ /// Maximum, if value is greater than maximum.
+ /// Otherwise, value.</returns>
+ public static double CoerceValue(this double value, double minimum, double maximum)
+ {
+ return Math.Max(Math.Min(value, maximum), minimum);
+ }
+
+ /// <summary>
+ /// Forces the value to stay between mininum and maximum.
+ /// </summary>
+ /// <returns>minimum, if value is less than minimum.
+ /// Maximum, if value is greater than maximum.
+ /// Otherwise, value.</returns>
+ public static int CoerceValue(this int value, int minimum, int maximum)
+ {
+ return Math.Max(Math.Min(value, maximum), minimum);
+ }
+ #endregion
+
+ #region CreateTypeface
+ /// <summary>
+ /// Creates typeface from the framework element.
+ /// </summary>
+ public static Typeface CreateTypeface(this FrameworkElement fe)
+ {
+ return new Typeface((FontFamily)fe.GetValue(TextBlock.FontFamilyProperty),
+ (FontStyle)fe.GetValue(TextBlock.FontStyleProperty),
+ (FontWeight)fe.GetValue(TextBlock.FontWeightProperty),
+ (FontStretch)fe.GetValue(TextBlock.FontStretchProperty));
+ }
+ #endregion
+
+ #region AddRange / Sequence
+ public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> elements)
+ {
+ foreach (T e in elements)
+ collection.Add(e);
+ }
+
+ /// <summary>
+ /// Creates an IEnumerable with a single value.
+ /// </summary>
+ public static IEnumerable<T> Sequence<T>(T value)
+ {
+ yield return value;
+ }
+ #endregion
+
+ #region XML reading
+ /// <summary>
+ /// Gets the value of the attribute, or null if the attribute does not exist.
+ /// </summary>
+ public static string GetAttributeOrNull(this XmlElement element, string attributeName)
+ {
+ XmlAttribute attr = element.GetAttributeNode(attributeName);
+ return attr != null ? attr.Value : null;
+ }
+
+ /// <summary>
+ /// Gets the value of the attribute as boolean, or null if the attribute does not exist.
+ /// </summary>
+ public static bool? GetBoolAttribute(this XmlElement element, string attributeName)
+ {
+ XmlAttribute attr = element.GetAttributeNode(attributeName);
+ return attr != null ? (bool?)XmlConvert.ToBoolean(attr.Value) : null;
+ }
+
+ /// <summary>
+ /// Gets the value of the attribute as boolean, or null if the attribute does not exist.
+ /// </summary>
+ public static bool? GetBoolAttribute(this XmlReader reader, string attributeName)
+ {
+ string attributeValue = reader.GetAttribute(attributeName);
+ if (attributeValue == null)
+ return null;
+ else
+ return XmlConvert.ToBoolean(attributeValue);
+ }
+ #endregion
+
+ #region DPI independence
+ public static Rect TransformToDevice(this Rect rect, Visual visual)
+ {
+ Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
+ return Rect.Transform(rect, matrix);
+ }
+
+ public static Rect TransformFromDevice(this Rect rect, Visual visual)
+ {
+ Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice;
+ return Rect.Transform(rect, matrix);
+ }
+
+ public static Size TransformToDevice(this Size size, Visual visual)
+ {
+ Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
+ return new Size(size.Width * matrix.M11, size.Height * matrix.M22);
+ }
+
+ public static Size TransformFromDevice(this Size size, Visual visual)
+ {
+ Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice;
+ return new Size(size.Width * matrix.M11, size.Height * matrix.M22);
+ }
+
+ public static Point TransformToDevice(this Point point, Visual visual)
+ {
+ Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
+ return new Point(point.X * matrix.M11, point.Y * matrix.M22);
+ }
+
+ public static Point TransformFromDevice(this Point point, Visual visual)
+ {
+ Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice;
+ return new Point(point.X * matrix.M11, point.Y * matrix.M22);
+ }
+ #endregion
+
+ #region System.Drawing <-> WPF conversions
+ public static System.Drawing.Point ToSystemDrawing(this Point p)
+ {
+ return new System.Drawing.Point((int)p.X, (int)p.Y);
+ }
+
+ public static Point ToWpf(this System.Drawing.Point p)
+ {
+ return new Point(p.X, p.Y);
+ }
+
+ public static Size ToWpf(this System.Drawing.Size s)
+ {
+ return new Size(s.Width, s.Height);
+ }
+
+ public static Rect ToWpf(this System.Drawing.Rectangle rect)
+ {
+ return new Rect(rect.Location.ToWpf(), rect.Size.ToWpf());
+ }
+ #endregion
+
+ [Conditional("DEBUG")]
+ public static void CheckIsFrozen(Freezable f)
+ {
+ if (f != null && !f.IsFrozen)
+ Debug.WriteLine("Performance warning: Not frozen: " + f.ToString());
+ }
+
+ [Conditional("DEBUG")]
+ public static void Log(bool condition, string format, params object[] args)
+ {
+ if (condition) {
+ string output = DateTime.Now.ToString("hh:MM:ss") + ": " + string.Format(format, args) + Environment.NewLine + Environment.StackTrace;
+ Console.WriteLine(output);
+ Debug.WriteLine(output);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/FileReader.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/FileReader.cs
new file mode 100644
index 000000000..b44c589d7
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/FileReader.cs
@@ -0,0 +1,208 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.IO;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Class that can open text files with auto-detection of the encoding.
+ /// </summary>
+ public static class FileReader
+ {
+ /// <summary>
+ /// Gets if the given encoding is a Unicode encoding (UTF).
+ /// </summary>
+ /// <remarks>
+ /// Returns true for UTF-7, UTF-8, UTF-16 LE, UTF-16 BE, UTF-32 LE and UTF-32 BE.
+ /// Returns false for all other encodings.
+ /// </remarks>
+ public static bool IsUnicode(Encoding encoding)
+ {
+ if (encoding == null)
+ throw new ArgumentNullException("encoding");
+ switch (encoding.CodePage) {
+ case 65000: // UTF-7
+ case 65001: // UTF-8
+ case 1200: // UTF-16 LE
+ case 1201: // UTF-16 BE
+ case 12000: // UTF-32 LE
+ case 12001: // UTF-32 BE
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Reads the content of the given stream.
+ /// </summary>
+ /// <param name="stream">The stream to read.
+ /// The stream must support seeking and must be positioned at its beginning.</param>
+ /// <param name="defaultEncoding">The encoding to use if the encoding cannot be auto-detected.</param>
+ /// <returns>The file content as string.</returns>
+ public static string ReadFileContent(Stream stream, Encoding defaultEncoding)
+ {
+ using (StreamReader reader = OpenStream(stream, defaultEncoding)) {
+ return reader.ReadToEnd();
+ }
+ }
+
+ /// <summary>
+ /// Reads the content of the file.
+ /// </summary>
+ /// <param name="fileName">The file name.</param>
+ /// <param name="defaultEncoding">The encoding to use if the encoding cannot be auto-detected.</param>
+ /// <returns>The file content as string.</returns>
+ public static string ReadFileContent(string fileName, Encoding defaultEncoding)
+ {
+ using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
+ return ReadFileContent(fs, defaultEncoding);
+ }
+ }
+
+ /// <summary>
+ /// Opens the specified file for reading.
+ /// </summary>
+ /// <param name="fileName">The file to open.</param>
+ /// <param name="defaultEncoding">The encoding to use if the encoding cannot be auto-detected.</param>
+ /// <returns>Returns a StreamReader that reads from the stream. Use
+ /// <see cref="StreamReader.CurrentEncoding"/> to get the encoding that was used.</returns>
+ public static StreamReader OpenFile(string fileName, Encoding defaultEncoding)
+ {
+ if (fileName == null)
+ throw new ArgumentNullException("fileName");
+ FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
+ try {
+ return OpenStream(fs, defaultEncoding);
+ // don't use finally: the stream must be kept open until the StreamReader closes it
+ } catch {
+ fs.Dispose();
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Opens the specified stream for reading.
+ /// </summary>
+ /// <param name="stream">The stream to open.</param>
+ /// <param name="defaultEncoding">The encoding to use if the encoding cannot be auto-detected.</param>
+ /// <returns>Returns a StreamReader that reads from the stream. Use
+ /// <see cref="StreamReader.CurrentEncoding"/> to get the encoding that was used.</returns>
+ public static StreamReader OpenStream(Stream stream, Encoding defaultEncoding)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+ if (stream.Position != 0)
+ throw new ArgumentException("stream is not positioned at beginning.", "stream");
+ if (defaultEncoding == null)
+ throw new ArgumentNullException("defaultEncoding");
+
+ if (stream.Length >= 2) {
+ // the autodetection of StreamReader is not capable of detecting the difference
+ // between ISO-8859-1 and UTF-8 without BOM.
+ int firstByte = stream.ReadByte();
+ int secondByte = stream.ReadByte();
+ switch ((firstByte << 8) | secondByte) {
+ case 0x0000: // either UTF-32 Big Endian or a binary file; use StreamReader
+ case 0xfffe: // Unicode BOM (UTF-16 LE or UTF-32 LE)
+ case 0xfeff: // UTF-16 BE BOM
+ case 0xefbb: // start of UTF-8 BOM
+ // StreamReader autodetection works
+ stream.Position = 0;
+ return new StreamReader(stream);
+ default:
+ return AutoDetect(stream, (byte)firstByte, (byte)secondByte, defaultEncoding);
+ }
+ } else {
+ if (defaultEncoding != null) {
+ return new StreamReader(stream, defaultEncoding);
+ } else {
+ return new StreamReader(stream);
+ }
+ }
+ }
+
+ static StreamReader AutoDetect(Stream fs, byte firstByte, byte secondByte, Encoding defaultEncoding)
+ {
+ int max = (int)Math.Min(fs.Length, 500000); // look at max. 500 KB
+ const int ASCII = 0;
+ const int Error = 1;
+ const int UTF8 = 2;
+ const int UTF8Sequence = 3;
+ int state = ASCII;
+ int sequenceLength = 0;
+ byte b;
+ for (int i = 0; i < max; i++) {
+ if (i == 0) {
+ b = firstByte;
+ } else if (i == 1) {
+ b = secondByte;
+ } else {
+ b = (byte)fs.ReadByte();
+ }
+ if (b < 0x80) {
+ // normal ASCII character
+ if (state == UTF8Sequence) {
+ state = Error;
+ break;
+ }
+ } else if (b < 0xc0) {
+ // 10xxxxxx : continues UTF8 byte sequence
+ if (state == UTF8Sequence) {
+ --sequenceLength;
+ if (sequenceLength < 0) {
+ state = Error;
+ break;
+ } else if (sequenceLength == 0) {
+ state = UTF8;
+ }
+ } else {
+ state = Error;
+ break;
+ }
+ } else if (b >= 0xc2 && b < 0xf5) {
+ // beginning of byte sequence
+ if (state == UTF8 || state == ASCII) {
+ state = UTF8Sequence;
+ if (b < 0xe0) {
+ sequenceLength = 1; // one more byte following
+ } else if (b < 0xf0) {
+ sequenceLength = 2; // two more bytes following
+ } else {
+ sequenceLength = 3; // three more bytes following
+ }
+ } else {
+ state = Error;
+ break;
+ }
+ } else {
+ // 0xc0, 0xc1, 0xf5 to 0xff are invalid in UTF-8 (see RFC 3629)
+ state = Error;
+ break;
+ }
+ }
+ fs.Position = 0;
+ switch (state) {
+ case ASCII:
+ case Error:
+ // when the file seems to be ASCII or non-UTF8,
+ // we read it using the user-specified encoding so it is saved again
+ // using that encoding.
+ if (IsUnicode(defaultEncoding)) {
+ // the file is not Unicode, so don't read it using Unicode even if the
+ // user has choosen Unicode as the default encoding.
+
+ // If we don't do this, SD will end up always adding a Byte Order Mark
+ // to ASCII files.
+ defaultEncoding = Encoding.Default; // use system encoding instead
+ }
+ return new StreamReader(fs, defaultEncoding);
+ default:
+ return new StreamReader(fs);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ImmutableStack.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ImmutableStack.cs
new file mode 100644
index 000000000..18a835fd2
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ImmutableStack.cs
@@ -0,0 +1,114 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// An immutable stack.
+ ///
+ /// Using 'foreach' on the stack will return the items from top to bottom (in the order they would be popped).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
+ [Serializable]
+ public sealed class ImmutableStack<T> : IEnumerable<T>
+ {
+ /// <summary>
+ /// Gets the empty stack instance.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "ImmutableStack is immutable")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
+
+ readonly T value;
+ readonly ImmutableStack<T> next;
+
+ private ImmutableStack()
+ {
+ }
+
+ private ImmutableStack(T value, ImmutableStack<T> next)
+ {
+ this.value = value;
+ this.next = next;
+ }
+
+ /// <summary>
+ /// Pushes an item on the stack. This does not modify the stack itself, but returns a new
+ /// one with the value pushed.
+ /// </summary>
+ public ImmutableStack<T> Push(T item)
+ {
+ return new ImmutableStack<T>(item, this);
+ }
+
+ /// <summary>
+ /// Gets the item on the top of the stack.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The stack is empty.</exception>
+ public T Peek()
+ {
+ if (IsEmpty)
+ throw new InvalidOperationException("Operation not valid on empty stack.");
+ return value;
+ }
+
+ internal T UnsafePeek()
+ {
+ Debug.Assert(!IsEmpty);
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the stack with the top item removed.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">The stack is empty.</exception>
+ public ImmutableStack<T> Pop()
+ {
+ if (IsEmpty)
+ throw new InvalidOperationException("Operation not valid on empty stack.");
+ return next;
+ }
+
+ /// <summary>
+ /// Gets if this stack is empty.
+ /// </summary>
+ public bool IsEmpty {
+ get { return next == null; }
+ }
+
+ /// <summary>
+ /// Gets an enumerator that iterates through the stack top-to-bottom.
+ /// </summary>
+ public IEnumerator<T> GetEnumerator()
+ {
+ ImmutableStack<T> t = this;
+ while (!t.IsEmpty) {
+ yield return t.value;
+ t = t.next;
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ StringBuilder b = new StringBuilder("[Stack");
+ foreach (T val in this) {
+ b.Append(' ');
+ b.Append(val);
+ }
+ b.Append(']');
+ return b.ToString();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/NullSafeCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/NullSafeCollection.cs
new file mode 100644
index 000000000..8a37c54ae
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/NullSafeCollection.cs
@@ -0,0 +1,31 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.ObjectModel;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// A collection that cannot contain null values.
+ /// </summary>
+ [Serializable]
+ public class NullSafeCollection<T> : Collection<T> where T : class
+ {
+ /// <inheritdoc/>
+ protected override void InsertItem(int index, T item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+ base.InsertItem(index, item);
+ }
+
+ /// <inheritdoc/>
+ protected override void SetItem(int index, T item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+ base.SetItem(index, item);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ObserveAddRemoveCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ObserveAddRemoveCollection.cs
new file mode 100644
index 000000000..091de0d91
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ObserveAddRemoveCollection.cs
@@ -0,0 +1,63 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.ObjectModel;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// A collection where adding and removing items causes a callback.
+ /// It is valid for the onAdd callback to throw an exception - this will prevent the new item from
+ /// being added to the collection.
+ /// </summary>
+ sealed class ObserveAddRemoveCollection<T> : Collection<T>
+ {
+ readonly Action<T> onAdd, onRemove;
+
+ public ObserveAddRemoveCollection(Action<T> onAdd, Action<T> onRemove)
+ {
+ this.onAdd = onAdd;
+ this.onRemove = onRemove;
+ }
+
+ protected override void ClearItems()
+ {
+ if (onRemove != null) {
+ foreach (T val in this)
+ onRemove(val);
+ }
+ base.ClearItems();
+ }
+
+ protected override void InsertItem(int index, T item)
+ {
+ if (onAdd != null)
+ onAdd(item);
+ base.InsertItem(index, item);
+ }
+
+ protected override void RemoveItem(int index)
+ {
+ if (onRemove != null)
+ onRemove(this[index]);
+ base.RemoveItem(index);
+ }
+
+ protected override void SetItem(int index, T item)
+ {
+ if (onRemove != null)
+ onRemove(this[index]);
+ try {
+ if (onAdd != null)
+ onAdd(item);
+ } catch {
+ // When adding the new item fails, just remove the old one
+ // (we cannot keep the old item since we already successfully called onRemove for it)
+ base.RemoveAt(index);
+ throw;
+ }
+ base.SetItem(index, item);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PixelSnapHelpers.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PixelSnapHelpers.cs
new file mode 100644
index 000000000..abd859533
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PixelSnapHelpers.cs
@@ -0,0 +1,92 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Contains static helper methods for aligning stuff on a whole number of pixels.
+ /// </summary>
+ public static class PixelSnapHelpers
+ {
+ /// <summary>
+ /// Gets the pixel size on the screen containing visual.
+ /// This method does not take transforms on visual into account.
+ /// </summary>
+ public static Size GetPixelSize(Visual visual)
+ {
+ if (visual == null)
+ throw new ArgumentNullException("visual");
+ PresentationSource source = PresentationSource.FromVisual(visual);
+ if (source != null) {
+ Matrix matrix = source.CompositionTarget.TransformFromDevice;
+ return new Size(matrix.M11, matrix.M22);
+ } else {
+ return new Size(1, 1);
+ }
+ }
+
+ /// <summary>
+ /// Aligns <paramref name="value"/> on the next middle of a pixel.
+ /// </summary>
+ /// <param name="value">The value that should be aligned</param>
+ /// <param name="pixelSize">The size of one pixel</param>
+ public static double PixelAlign(double value, double pixelSize)
+ {
+ // 0 -> 0.5
+ // 0.1 -> 0.5
+ // 0.5 -> 0.5
+ // 0.9 -> 0.5
+ // 1 -> 1.5
+ return pixelSize * (Math.Round((value / pixelSize) + 0.5) - 0.5);
+ }
+
+ /// <summary>
+ /// Aligns the borders of rect on the middles of pixels.
+ /// </summary>
+ public static Rect PixelAlign(Rect rect, Size pixelSize)
+ {
+ rect.X = PixelAlign(rect.X, pixelSize.Width);
+ rect.Y = PixelAlign(rect.Y, pixelSize.Height);
+ rect.Width = Round(rect.Width, pixelSize.Width);
+ rect.Height = Round(rect.Height, pixelSize.Height);
+ return rect;
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="point"/> to whole number of pixels.
+ /// </summary>
+ public static Point Round(Point point, Size pixelSize)
+ {
+ return new Point(Round(point.X, pixelSize.Width), Round(point.Y, pixelSize.Height));
+ }
+
+ /// <summary>
+ /// Rounds val to whole number of pixels.
+ /// </summary>
+ public static Rect Round(Rect rect, Size pixelSize)
+ {
+ return new Rect(Round(rect.X, pixelSize.Width), Round(rect.Y, pixelSize.Height),
+ Round(rect.Width, pixelSize.Width), Round(rect.Height, pixelSize.Height));
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="value"/> to a whole number of pixels.
+ /// </summary>
+ public static double Round(double value, double pixelSize)
+ {
+ return pixelSize * Math.Round(value / pixelSize);
+ }
+
+ /// <summary>
+ /// Rounds <paramref name="value"/> to an whole odd number of pixels.
+ /// </summary>
+ public static double RoundToOdd(double value, double pixelSize)
+ {
+ return Round(value - pixelSize, pixelSize * 2) + pixelSize;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PropertyChangedWeakEventManager.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PropertyChangedWeakEventManager.cs
new file mode 100644
index 000000000..9b70c8f54
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/PropertyChangedWeakEventManager.cs
@@ -0,0 +1,26 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// WeakEventManager for INotifyPropertyChanged.PropertyChanged.
+ /// </summary>
+ public sealed class PropertyChangedWeakEventManager : WeakEventManagerBase<PropertyChangedWeakEventManager, INotifyPropertyChanged>
+ {
+ /// <inheritdoc/>
+ protected override void StartListening(INotifyPropertyChanged source)
+ {
+ source.PropertyChanged += DeliverEvent;
+ }
+
+ /// <inheritdoc/>
+ protected override void StopListening(INotifyPropertyChanged source)
+ {
+ source.PropertyChanged -= DeliverEvent;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Rope.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Rope.cs
new file mode 100644
index 000000000..d27edd199
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Rope.cs
@@ -0,0 +1,789 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// A kind of List&lt;T&gt;, but more efficient for random insertions/removal.
+ /// Also has cheap Clone() and SubRope() implementations.
+ /// </summary>
+ /// <remarks>
+ /// This class is not thread-safe: multiple concurrent write operations or writes concurrent to reads have undefined behaviour.
+ /// Concurrent reads, however, are safe.
+ /// However, clones of a rope are safe to use on other threads even though they share data with the original rope.
+ /// </remarks>
+ [Serializable]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
+ public sealed class Rope<T> : IList<T>, ICloneable
+ {
+ internal RopeNode<T> root;
+
+ internal Rope(RopeNode<T> root)
+ {
+ this.root = root;
+ root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a new rope representing the empty string.
+ /// </summary>
+ public Rope()
+ {
+ // we'll construct the empty rope as a clone of an imaginary static empty rope
+ this.root = RopeNode<T>.emptyRopeNode;
+ root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a rope from the specified input.
+ /// This operation runs in O(N).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">input is null.</exception>
+ public Rope(IEnumerable<T> input)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+ Rope<T> inputRope = input as Rope<T>;
+ if (inputRope != null) {
+ // clone ropes instead of copying them
+ inputRope.root.Publish();
+ this.root = inputRope.root;
+ } else {
+ string text = input as string;
+ if (text != null) {
+ // if a string is IEnumerable<T>, then T must be char
+ ((Rope<char>)(object)this).root = CharRope.InitFromString(text);
+ } else {
+ T[] arr = ToArray(input);
+ this.root = RopeNode<T>.CreateFromArray(arr, 0, arr.Length);
+ }
+ }
+ this.root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a rope from a part of the array.
+ /// This operation runs in O(N).
+ /// </summary>
+ /// <exception cref="ArgumentNullException">input is null.</exception>
+ public Rope(T[] array, int arrayIndex, int count)
+ {
+ VerifyArrayWithRange(array, arrayIndex, count);
+ this.root = RopeNode<T>.CreateFromArray(array, arrayIndex, count);
+ this.root.CheckInvariants();
+ }
+
+ /// <summary>
+ /// Creates a new rope that lazily initalizes its content.
+ /// </summary>
+ /// <param name="length">The length of the rope that will be lazily loaded.</param>
+ /// <param name="initializer">
+ /// The callback that provides the content for this rope.
+ /// <paramref name="initializer"/> will be called exactly once when the content of this rope is first requested.
+ /// It must return a rope with the specified length.
+ /// Because the initializer function is not called when a rope is cloned, and such clones may be used on another threads,
+ /// it is possible for the initializer callback to occur on any thread.
+ /// </param>
+ /// <remarks>
+ /// Any modifications inside the rope will also cause the content to be initialized.
+ /// However, insertions at the beginning and the end, as well as inserting this rope into another or
+ /// using the <see cref="Concat(Rope{T},Rope{T})"/> method, allows constructions of larger ropes where parts are
+ /// lazily loaded.
+ /// However, even methods like Concat may sometimes cause the initializer function to be called, e.g. when
+ /// two short ropes are concatenated.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+ public Rope(int length, Func<Rope<T>> initializer)
+ {
+ if (initializer == null)
+ throw new ArgumentNullException("initializer");
+ if (length < 0)
+ throw new ArgumentOutOfRangeException("length", length, "Length must not be negative");
+ if (length == 0) {
+ this.root = RopeNode<T>.emptyRopeNode;
+ } else {
+ this.root = new FunctionNode<T>(length, initializer);
+ }
+ this.root.CheckInvariants();
+ }
+
+ static T[] ToArray(IEnumerable<T> input)
+ {
+ T[] arr = input as T[];
+ return arr ?? input.ToArray();
+ }
+
+ /// <summary>
+ /// Clones the rope.
+ /// This operation runs in linear time to the number of rope nodes touched since the last clone was created.
+ /// If you count the per-node cost to the operation modifying the rope (doing this doesn't increase the complexity of the modification operations);
+ /// the remainder of Clone() runs in O(1).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public Rope<T> Clone()
+ {
+ // The Publish() call actually modifies this rope instance; but this modification is thread-safe
+ // as long as the tree structure doesn't change during the operation.
+ root.Publish();
+ return new Rope<T>(root);
+ }
+
+ object ICloneable.Clone()
+ {
+ return this.Clone();
+ }
+
+ /// <summary>
+ /// Resets the rope to an empty list.
+ /// Runs in O(1).
+ /// </summary>
+ public void Clear()
+ {
+ root = RopeNode<T>.emptyRopeNode;
+ OnChanged();
+ }
+
+ /// <summary>
+ /// Gets the length of the rope.
+ /// Runs in O(1).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public int Length {
+ get { return root.length; }
+ }
+
+ /// <summary>
+ /// Gets the length of the rope.
+ /// Runs in O(1).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public int Count {
+ get { return root.length; }
+ }
+
+ /// <summary>
+ /// Inserts another rope into this rope.
+ /// Runs in O(lg N + lg M), plus a per-node cost as if <c>newElements.Clone()</c> was called.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public void InsertRange(int index, Rope<T> newElements)
+ {
+ if (index < 0 || index > this.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (newElements == null)
+ throw new ArgumentNullException("newElements");
+ newElements.root.Publish();
+ root = root.Insert(index, newElements.root);
+ OnChanged();
+ }
+
+ /// <summary>
+ /// Inserts new elemetns into this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public void InsertRange(int index, IEnumerable<T> newElements)
+ {
+ if (newElements == null)
+ throw new ArgumentNullException("newElements");
+ Rope<T> newElementsRope = newElements as Rope<T>;
+ if (newElementsRope != null) {
+ InsertRange(index, newElementsRope);
+ } else {
+ T[] arr = ToArray(newElements);
+ InsertRange(index, arr, 0, arr.Length);
+ }
+ }
+
+ /// <summary>
+ /// Inserts new elements into this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">index or length is outside the valid range.</exception>
+ public void InsertRange(int index, T[] array, int arrayIndex, int count)
+ {
+ if (index < 0 || index > this.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ VerifyArrayWithRange(array, arrayIndex, count);
+ if (count > 0) {
+ root = root.Insert(index, array, arrayIndex, count);
+ OnChanged();
+ }
+ }
+
+ /// <summary>
+ /// Appends multiple elements to the end of this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ public void AddRange(IEnumerable<T> newElements)
+ {
+ InsertRange(this.Length, newElements);
+ }
+
+ /// <summary>
+ /// Appends another rope to the end of this rope.
+ /// Runs in O(lg N + lg M), plus a per-node cost as if <c>newElements.Clone()</c> was called.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">newElements is null.</exception>
+ public void AddRange(Rope<T> newElements)
+ {
+ InsertRange(this.Length, newElements);
+ }
+
+ /// <summary>
+ /// Appends new elements to the end of this rope.
+ /// Runs in O(lg N + M), where N is the length of this rope and M is the number of new elements.
+ /// </summary>
+ /// <exception cref="ArgumentNullException">array is null.</exception>
+ public void AddRange(T[] array, int arrayIndex, int count)
+ {
+ InsertRange(this.Length, array, arrayIndex, count);
+ }
+
+ /// <summary>
+ /// Removes a range of elements from the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ public void RemoveRange(int index, int count)
+ {
+ VerifyRange(index, count);
+ if (count > 0) {
+ root = root.RemoveRange(index, count);
+ OnChanged();
+ }
+ }
+
+ /// <summary>
+ /// Copies a range of the specified array into the rope, overwriting existing elements.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ public void SetRange(int index, T[] array, int arrayIndex, int count)
+ {
+ VerifyRange(index, count);
+ VerifyArrayWithRange(array, arrayIndex, count);
+ if (count > 0) {
+ root = root.StoreElements(index, array, arrayIndex, count);
+ OnChanged();
+ }
+ }
+
+ /// <summary>
+ /// Creates a new rope and initializes it with a part of this rope.
+ /// Runs in O(lg N) plus a per-node cost as if <c>this.Clone()</c> was called.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public Rope<T> GetRange(int index, int count)
+ {
+ VerifyRange(index, count);
+ Rope<T> newRope = Clone();
+ int endIndex = index + count;
+ newRope.RemoveRange(endIndex, newRope.Length - endIndex);
+ newRope.RemoveRange(0, index);
+ return newRope;
+ }
+
+ /*
+ #region Equality
+ /// <summary>
+ /// Gets whether the two ropes have the same content.
+ /// Runs in O(N + M).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public bool Equals(Rope other)
+ {
+ if (other == null)
+ return false;
+ // quick detection for ropes that are clones of each other:
+ if (other.root == this.root)
+ return true;
+ if (other.Length != this.Length)
+ return false;
+ using (RopeTextReader a = new RopeTextReader(this, false)) {
+ using (RopeTextReader b = new RopeTextReader(other, false)) {
+ int charA, charB;
+ do {
+ charA = a.Read();
+ charB = b.Read();
+ if (charA != charB)
+ return false;
+ } while (charA != -1);
+ return true;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets whether two ropes have the same content.
+ /// Runs in O(N + M).
+ /// </summary>
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as Rope);
+ }
+
+ /// <summary>
+ /// Calculates the hash code of the rope's content.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public override int GetHashCode()
+ {
+ int hashcode = 0;
+ using (RopeTextReader reader = new RopeTextReader(this, false)) {
+ unchecked {
+ int val;
+ while ((val = reader.Read()) != -1) {
+ hashcode = hashcode * 31 + val;
+ }
+ }
+ }
+ return hashcode;
+ }
+ #endregion
+ */
+
+ /// <summary>
+ /// Concatenates two ropes. The input ropes are not modified.
+ /// Runs in O(lg N + lg M).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static Rope<T> Concat(Rope<T> left, Rope<T> right)
+ {
+ if (left == null)
+ throw new ArgumentNullException("left");
+ if (right == null)
+ throw new ArgumentNullException("right");
+ left.root.Publish();
+ right.root.Publish();
+ return new Rope<T>(RopeNode<T>.Concat(left.root, right.root));
+ }
+
+ /// <summary>
+ /// Concatenates multiple ropes. The input ropes are not modified.
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static Rope<T> Concat(params Rope<T>[] ropes)
+ {
+ if (ropes == null)
+ throw new ArgumentNullException("ropes");
+ Rope<T> result = new Rope<T>();
+ foreach (Rope<T> r in ropes)
+ result.AddRange(r);
+ return result;
+ }
+
+ #region Caches / Changed event
+ internal struct RopeCacheEntry
+ {
+ internal readonly RopeNode<T> node;
+ internal readonly int nodeStartIndex;
+
+ internal RopeCacheEntry(RopeNode<T> node, int nodeStartOffset)
+ {
+ this.node = node;
+ this.nodeStartIndex = nodeStartOffset;
+ }
+
+ internal bool IsInside(int offset)
+ {
+ return offset >= nodeStartIndex && offset < nodeStartIndex + node.length;
+ }
+ }
+
+ // cached pointer to 'last used node', used to speed up accesses by index that are close together
+ [NonSerialized]
+ volatile ImmutableStack<RopeCacheEntry> lastUsedNodeStack;
+
+ internal void OnChanged()
+ {
+ lastUsedNodeStack = null;
+
+ root.CheckInvariants();
+ }
+ #endregion
+
+ #region GetChar / SetChar
+ /// <summary>
+ /// Gets/Sets a single character.
+ /// Runs in O(lg N) for random access. Sequential read-only access benefits from a special optimization and runs in amortized O(1).
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to Length-1).</exception>
+ /// <remarks>
+ /// The getter counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public T this[int index] {
+ get {
+ // use unsigned integers - this way negative values for index overflow and can be tested for with the same check
+ if (unchecked((uint)index >= (uint)this.Length)) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ RopeCacheEntry entry = FindNodeUsingCache(index).UnsafePeek();
+ return entry.node.contents[index - entry.nodeStartIndex];
+ }
+ set {
+ if (index < 0 || index >= this.Length) {
+ throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ root = root.SetElement(index, value);
+ OnChanged();
+ /* Here's a try at implementing the setter using the cached node stack (UNTESTED code!).
+ * However I don't use the code because it's complicated and doesn't integrate correctly with change notifications.
+ * Instead, I'll use the much easier to understand recursive solution.
+ * Oh, and it also doesn't work correctly with function nodes.
+ ImmutableStack<RopeCacheEntry> nodeStack = FindNodeUsingCache(offset);
+ RopeCacheEntry entry = nodeStack.Peek();
+ if (!entry.node.isShared) {
+ entry.node.contents[offset - entry.nodeStartOffset] = value;
+ // missing: clear the caches except for the node stack cache (e.g. ToString() cache?)
+ } else {
+ RopeNode oldNode = entry.node;
+ RopeNode newNode = oldNode.Clone();
+ newNode.contents[offset - entry.nodeStartOffset] = value;
+ for (nodeStack = nodeStack.Pop(); !nodeStack.IsEmpty; nodeStack = nodeStack.Pop()) {
+ RopeNode parentNode = nodeStack.Peek().node;
+ RopeNode newParentNode = parentNode.CloneIfShared();
+ if (newParentNode.left == oldNode) {
+ newParentNode.left = newNode;
+ } else {
+ Debug.Assert(newParentNode.right == oldNode);
+ newParentNode.right = newNode;
+ }
+ if (parentNode == newParentNode) {
+ // we were able to change the existing node (it was not shared);
+ // there's no reason to go further upwards
+ ClearCacheOnModification();
+ return;
+ } else {
+ oldNode = parentNode;
+ newNode = newParentNode;
+ }
+ }
+ // we reached the root of the rope.
+ Debug.Assert(root == oldNode);
+ root = newNode;
+ ClearCacheOnModification();
+ }*/
+ }
+ }
+
+ internal ImmutableStack<RopeCacheEntry> FindNodeUsingCache(int index)
+ {
+ Debug.Assert(index >= 0 && index < this.Length);
+
+ // thread safety: fetch stack into local variable
+ ImmutableStack<RopeCacheEntry> stack = lastUsedNodeStack;
+ ImmutableStack<RopeCacheEntry> oldStack = stack;
+
+ if (stack == null) {
+ stack = ImmutableStack<RopeCacheEntry>.Empty.Push(new RopeCacheEntry(root, 0));
+ }
+ while (!stack.UnsafePeek().IsInside(index))
+ stack = stack.Pop();
+ while (true) {
+ RopeCacheEntry entry = stack.UnsafePeek();
+ // check if we've reached a leaf or function node
+ if (entry.node.height == 0) {
+ if (entry.node.contents == null) {
+ // this is a function node - go down into its subtree
+ entry = new RopeCacheEntry(entry.node.GetContentNode(), entry.nodeStartIndex);
+ // entry is now guaranteed NOT to be another function node
+ }
+ if (entry.node.contents != null) {
+ // this is a node containing actual content, so we're done
+ break;
+ }
+ }
+ // go down towards leaves
+ if (index - entry.nodeStartIndex >= entry.node.left.length)
+ stack = stack.Push(new RopeCacheEntry(entry.node.right, entry.nodeStartIndex + entry.node.left.length));
+ else
+ stack = stack.Push(new RopeCacheEntry(entry.node.left, entry.nodeStartIndex));
+ }
+
+ // write back stack to volatile cache variable
+ // (in multithreaded access, it doesn't matter which of the threads wins - it's just a cache)
+ if (oldStack != stack) {
+ // no need to write when we the cache variable didn't change
+ lastUsedNodeStack = stack;
+ }
+
+ // this method guarantees that it finds a leaf node
+ Debug.Assert(stack.Peek().node.contents != null);
+ return stack;
+ }
+ #endregion
+
+ #region ToString / WriteTo
+ internal void VerifyRange(int startIndex, int length)
+ {
+ if (startIndex < 0 || startIndex > this.Length) {
+ throw new ArgumentOutOfRangeException("startIndex", startIndex, "0 <= startIndex <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (length < 0 || startIndex + length > this.Length) {
+ throw new ArgumentOutOfRangeException("length", length, "0 <= length, startIndex(" + startIndex + ")+length <= " + this.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+
+ internal static void VerifyArrayWithRange(T[] array, int arrayIndex, int count)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+ if (arrayIndex < 0 || arrayIndex > array.Length) {
+ throw new ArgumentOutOfRangeException("startIndex", arrayIndex, "0 <= arrayIndex <= " + array.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ if (count < 0 || arrayIndex + count > array.Length) {
+ throw new ArgumentOutOfRangeException("count", count, "0 <= length, arrayIndex(" + arrayIndex + ")+count <= " + array.Length.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+
+ /// <summary>
+ /// Creates a string from the rope. Runs in O(N).
+ /// </summary>
+ /// <returns>A string consisting of all elements in the rope as comma-separated list in {}.
+ /// As a special case, Rope&lt;char&gt; will return its contents as string without any additional separators or braces,
+ /// so it can be used like StringBuilder.ToString().</returns>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public override string ToString()
+ {
+ Rope<char> charRope = this as Rope<char>;
+ if (charRope != null) {
+ return charRope.ToString(0, this.Length);
+ } else {
+ StringBuilder b = new StringBuilder();
+ foreach (T element in this) {
+ if (b.Length == 0)
+ b.Append('{');
+ else
+ b.Append(", ");
+ b.Append(element.ToString());
+ }
+ b.Append('}');
+ return b.ToString();
+ }
+ }
+
+ internal string GetTreeAsString()
+ {
+ #if DEBUG
+ return root.GetTreeAsString();
+ #else
+ return "Not available in release build.";
+ #endif
+ }
+ #endregion
+
+ bool ICollection<T>.IsReadOnly {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// Finds the first occurance of item.
+ /// Runs in O(N).
+ /// </summary>
+ /// <returns>The index of the first occurance of item, or -1 if it cannot be found.</returns>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public int IndexOf(T item)
+ {
+ var comparer = EqualityComparer<T>.Default;
+ int index = 0;
+ foreach (T element in this) {
+ if (comparer.Equals(item, element))
+ return index;
+ index++;
+ }
+ return -1;
+ }
+
+ /// <summary>
+ /// Inserts the item at the specified index in the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ public void Insert(int index, T item)
+ {
+ InsertRange(index, new[] { item }, 0, 1);
+ }
+
+ /// <summary>
+ /// Removes a single item from the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ public void RemoveAt(int index)
+ {
+ RemoveRange(index, 1);
+ }
+
+ /// <summary>
+ /// Appends the item at the end of the rope.
+ /// Runs in O(lg N).
+ /// </summary>
+ public void Add(T item)
+ {
+ InsertRange(this.Length, new[] { item }, 0, 1);
+ }
+
+ /// <summary>
+ /// Searches the item in the rope.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public bool Contains(T item)
+ {
+ return IndexOf(item) >= 0;
+ }
+
+ /// <summary>
+ /// Copies the whole content of the rope into the specified array.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ CopyTo(0, array, arrayIndex, this.Length);
+ }
+
+ /// <summary>
+ /// Copies the a part of the rope into the specified array.
+ /// Runs in O(lg N + M).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public void CopyTo(int index, T[] array, int arrayIndex, int count)
+ {
+ VerifyRange(index, count);
+ VerifyArrayWithRange(array, arrayIndex, count);
+ this.root.CopyTo(index, array, arrayIndex, count);
+ }
+
+ /// <summary>
+ /// Removes the first occurance of an item from the rope.
+ /// Runs in O(N).
+ /// </summary>
+ public bool Remove(T item)
+ {
+ int index = IndexOf(item);
+ if (index >= 0) {
+ RemoveAt(index);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Retrieves an enumerator to iterate through the rope.
+ /// The enumerator will reflect the state of the rope from the GetEnumerator() call, further modifications
+ /// to the rope will not be visible to the enumerator.
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public IEnumerator<T> GetEnumerator()
+ {
+ this.root.Publish();
+ return Enumerate(root);
+ }
+
+ /// <summary>
+ /// Creates an array and copies the contents of the rope into it.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public T[] ToArray()
+ {
+ T[] arr = new T[this.Length];
+ CopyTo(arr, 0);
+ return arr;
+ }
+
+ /// <summary>
+ /// Creates an array and copies the contents of the rope into it.
+ /// Runs in O(N).
+ /// </summary>
+ /// <remarks>
+ /// This method counts as a read access and may be called concurrently to other read accesses.
+ /// </remarks>
+ public T[] ToArray(int startIndex, int count)
+ {
+ VerifyRange(startIndex, count);
+ T[] arr = new T[count];
+ CopyTo(startIndex, arr, 0, count);
+ return arr;
+ }
+
+ static IEnumerator<T> Enumerate(RopeNode<T> node)
+ {
+ Stack<RopeNode<T>> stack = new Stack<RopeNode<T>>();
+ while (node != null) {
+ // go to leftmost node, pushing the right parts that we'll have to visit later
+ while (node.contents == null) {
+ if (node.height == 0) {
+ // go down into function nodes
+ node = node.GetContentNode();
+ continue;
+ }
+ Debug.Assert(node.right != null);
+ stack.Push(node.right);
+ node = node.left;
+ }
+ // yield contents of leaf node
+ for (int i = 0; i < node.length; i++) {
+ yield return node.contents[i];
+ }
+ // go up to the next node not visited yet
+ if (stack.Count > 0)
+ node = stack.Pop();
+ else
+ node = null;
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeNode.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeNode.cs
new file mode 100644
index 000000000..87f060c1b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeNode.cs
@@ -0,0 +1,605 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Runtime.Serialization;
+
+using System.Text;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ // Class used to represent a node in the tree.
+ // There are three types of nodes:
+ // Concat nodes: height>0, left!=null, right!=null, contents==null
+ // Leaf nodes: height==0, left==null, right==null, contents!=null
+ // Function nodes: height==0, left==null, right==null, contents==null, are of type FunctionNode<T>
+
+ [Serializable]
+ class RopeNode<T>
+ {
+ internal const int NodeSize = 256;
+
+ internal static readonly RopeNode<T> emptyRopeNode = new RopeNode<T> { isShared = true, contents = new T[RopeNode<T>.NodeSize] };
+
+ // Fields for pointers to sub-nodes. Only non-null for concat nodes (height>=1)
+ internal RopeNode<T> left, right;
+ internal volatile bool isShared; // specifies whether this node is shared between multiple ropes
+ // the total length of all text in this subtree
+ internal int length;
+ // the height of this subtree: 0 for leaf nodes; 1+max(left.height,right.height) for concat nodes
+ internal byte height;
+
+ // The character data. Only non-null for leaf nodes (height=0) that aren't function nodes.
+ internal T[] contents;
+
+ internal int Balance {
+ get { return right.height - left.height; }
+ }
+
+ [Conditional("DATACONSISTENCYTEST")]
+ internal void CheckInvariants()
+ {
+ if (height == 0) {
+ Debug.Assert(left == null && right == null);
+ if (contents == null) {
+ Debug.Assert(this is FunctionNode<T>);
+ Debug.Assert(length > 0);
+ Debug.Assert(isShared);
+ } else {
+ Debug.Assert(contents != null && contents.Length == NodeSize);
+ Debug.Assert(length >= 0 && length <= NodeSize);
+ }
+ } else {
+ Debug.Assert(left != null && right != null);
+ Debug.Assert(contents == null);
+ Debug.Assert(length == left.length + right.length);
+ Debug.Assert(height == 1 + Math.Max(left.height, right.height));
+ Debug.Assert(Math.Abs(this.Balance) <= 1);
+
+ // this is an additional invariant that forces the tree to combine small leafs to prevent excessive memory usage:
+ Debug.Assert(length > NodeSize);
+ // note that this invariant ensures that all nodes except for the empty rope's single node have at least length 1
+
+ if (isShared)
+ Debug.Assert(left.isShared && right.isShared);
+ left.CheckInvariants();
+ right.CheckInvariants();
+ }
+ }
+
+ internal RopeNode<T> Clone()
+ {
+ if (height == 0) {
+ // If a function node needs cloning, we'll evaluate it.
+ if (contents == null)
+ return GetContentNode().Clone();
+ T[] newContents = new T[NodeSize];
+ contents.CopyTo(newContents, 0);
+ return new RopeNode<T> {
+ length = this.length,
+ contents = newContents
+ };
+ } else {
+ return new RopeNode<T> {
+ left = this.left,
+ right = this.right,
+ length = this.length,
+ height = this.height
+ };
+ }
+ }
+
+ internal RopeNode<T> CloneIfShared()
+ {
+ if (isShared)
+ return Clone();
+ else
+ return this;
+ }
+
+ internal void Publish()
+ {
+ if (!isShared) {
+ if (left != null)
+ left.Publish();
+ if (right != null)
+ right.Publish();
+ // it's important that isShared=true is set at the end:
+ // Publish() must not return until the whole subtree is marked as shared, even when
+ // Publish() is called concurrently.
+ isShared = true;
+ }
+ }
+
+ internal static RopeNode<T> CreateFromArray(T[] arr, int index, int length)
+ {
+ if (length == 0) {
+ return emptyRopeNode;
+ }
+ RopeNode<T> node = CreateNodes(length);
+ return node.StoreElements(0, arr, index, length);
+ }
+
+ internal static RopeNode<T> CreateNodes(int totalLength)
+ {
+ int leafCount = (totalLength + NodeSize - 1) / NodeSize;
+ return CreateNodes(leafCount, totalLength);
+ }
+
+ static RopeNode<T> CreateNodes(int leafCount, int totalLength)
+ {
+ Debug.Assert(leafCount > 0);
+ Debug.Assert(totalLength > 0);
+ RopeNode<T> result = new RopeNode<T>();
+ result.length = totalLength;
+ if (leafCount == 1) {
+ result.contents = new T[NodeSize];
+ } else {
+ int rightSide = leafCount / 2;
+ int leftSide = leafCount - rightSide;
+ int leftLength = leftSide * NodeSize;
+ result.left = CreateNodes(leftSide, leftLength);
+ result.right = CreateNodes(rightSide, totalLength - leftLength);
+ result.height = (byte)(1 + Math.Max(result.left.height, result.right.height));
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Balances this node and recomputes the 'height' field.
+ /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value.
+ /// </summary>
+ internal void Rebalance()
+ {
+ // Rebalance() shouldn't be called on shared nodes - it's only called after modifications!
+ Debug.Assert(!isShared);
+ // leaf nodes are always balanced (we don't use 'height' to detect leaf nodes here
+ // because Balance is supposed to recompute the height).
+ if (left == null)
+ return;
+
+ // ensure we didn't miss a MergeIfPossible step
+ Debug.Assert(this.length > NodeSize);
+
+ // We need to loop until it's balanced. Rotations might cause two small leaves to combine to a larger one,
+ // which changes the height and might mean we need additional balancing steps.
+ while (Math.Abs(this.Balance) > 1) {
+ // AVL balancing
+ // note: because we don't care about the identity of concat nodes, this works a little different than usual
+ // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged
+ if (this.Balance > 1) {
+ if (right.Balance < 0) {
+ right = right.CloneIfShared();
+ right.RotateRight();
+ }
+ this.RotateLeft();
+ // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
+ this.left.Rebalance();
+ } else if (this.Balance < -1) {
+ if (left.Balance > 0) {
+ left = left.CloneIfShared();
+ left.RotateLeft();
+ }
+ this.RotateRight();
+ // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
+ this.right.Rebalance();
+ }
+ }
+
+ Debug.Assert(Math.Abs(this.Balance) <= 1);
+ this.height = (byte)(1 + Math.Max(left.height, right.height));
+ }
+
+ void RotateLeft()
+ {
+ Debug.Assert(!isShared);
+
+ /* Rotate tree to the left
+ *
+ * this this
+ * / \ / \
+ * A right ===> left C
+ * / \ / \
+ * B C A B
+ */
+ RopeNode<T> a = left;
+ RopeNode<T> b = right.left;
+ RopeNode<T> c = right.right;
+ // reuse right concat node, if possible
+ this.left = right.isShared ? new RopeNode<T>() : right;
+ this.left.left = a;
+ this.left.right = b;
+ this.left.length = a.length + b.length;
+ this.left.height = (byte)(1 + Math.Max(a.height, b.height));
+ this.right = c;
+
+ this.left.MergeIfPossible();
+ }
+
+ void RotateRight()
+ {
+ Debug.Assert(!isShared);
+
+ /* Rotate tree to the right
+ *
+ * this this
+ * / \ / \
+ * left C ===> A right
+ * / \ / \
+ * A B B C
+ */
+ RopeNode<T> a = left.left;
+ RopeNode<T> b = left.right;
+ RopeNode<T> c = right;
+ // reuse left concat node, if possible
+ this.right = left.isShared ? new RopeNode<T>() : left;
+ this.right.left = b;
+ this.right.right = c;
+ this.right.length = b.length + c.length;
+ this.right.height = (byte)(1 + Math.Max(b.height, c.height));
+ this.left = a;
+
+ this.right.MergeIfPossible();
+ }
+
+ void MergeIfPossible()
+ {
+ Debug.Assert(!isShared);
+
+ if (this.length <= NodeSize) {
+ // Convert this concat node to leaf node.
+ // We know left and right cannot be concat nodes (they would have merged already),
+ // but they could be function nodes.
+ this.height = 0;
+ int lengthOnLeftSide = this.left.length;
+ if (this.left.isShared) {
+ this.contents = new T[NodeSize];
+ left.CopyTo(0, this.contents, 0, lengthOnLeftSide);
+ } else {
+ // must be a leaf node: function nodes are always marked shared
+ Debug.Assert(this.left.contents != null);
+ // steal buffer from left side
+ this.contents = this.left.contents;
+ #if DEBUG
+ // In debug builds, explicitly mark left node as 'damaged' - but no one else should be using it
+ // because it's not shared.
+ this.left.contents = Empty<T>.Array;
+ #endif
+ }
+ this.left = null;
+ right.CopyTo(0, this.contents, lengthOnLeftSide, this.right.length);
+ this.right = null;
+ }
+ }
+
+ /// <summary>
+ /// Copies from the array to this node.
+ /// </summary>
+ internal RopeNode<T> StoreElements(int index, T[] array, int arrayIndex, int count)
+ {
+ RopeNode<T> result = this.CloneIfShared();
+ // result cannot be function node after a call to Clone()
+ if (result.height == 0) {
+ // leaf node:
+ Array.Copy(array, arrayIndex, result.contents, index, count);
+ } else {
+ // concat node:
+ if (index + count <= result.left.length) {
+ result.left = result.left.StoreElements(index, array, arrayIndex, count);
+ } else if (index >= this.left.length) {
+ result.right = result.right.StoreElements(index - result.left.length, array, arrayIndex, count);
+ } else {
+ int amountInLeft = result.left.length - index;
+ result.left = result.left.StoreElements(index, array, arrayIndex, amountInLeft);
+ result.right = result.right.StoreElements(0, array, arrayIndex + amountInLeft, count - amountInLeft);
+ }
+ result.Rebalance(); // tree layout might have changed if function nodes were replaced with their content
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Copies from this node to the array.
+ /// </summary>
+ internal void CopyTo(int index, T[] array, int arrayIndex, int count)
+ {
+ if (height == 0) {
+ if (this.contents == null) {
+ // function node
+ this.GetContentNode().CopyTo(index, array, arrayIndex, count);
+ } else {
+ // leaf node
+ Array.Copy(this.contents, index, array, arrayIndex, count);
+ }
+ } else {
+ // concat node
+ if (index + count <= this.left.length) {
+ this.left.CopyTo(index, array, arrayIndex, count);
+ } else if (index >= this.left.length) {
+ this.right.CopyTo(index - this.left.length, array, arrayIndex, count);
+ } else {
+ int amountInLeft = this.left.length - index;
+ this.left.CopyTo(index, array, arrayIndex, amountInLeft);
+ this.right.CopyTo(0, array, arrayIndex + amountInLeft, count - amountInLeft);
+ }
+ }
+ }
+
+ internal RopeNode<T> SetElement(int offset, T value)
+ {
+ RopeNode<T> result = CloneIfShared();
+ // result of CloneIfShared() is leaf or concat node
+ if (result.height == 0) {
+ result.contents[offset] = value;
+ } else {
+ if (offset < result.left.length) {
+ result.left = result.left.SetElement(offset, value);
+ } else {
+ result.right = result.right.SetElement(offset - result.left.length, value);
+ }
+ result.Rebalance(); // tree layout might have changed if function nodes were replaced with their content
+ }
+ return result;
+ }
+
+ internal static RopeNode<T> Concat(RopeNode<T> left, RopeNode<T> right)
+ {
+ if (left.length == 0)
+ return right;
+ if (right.length == 0)
+ return left;
+
+ if (left.length + right.length <= NodeSize) {
+ left = left.CloneIfShared();
+ // left is guaranteed to be leaf node after cloning:
+ // - it cannot be function node (due to clone)
+ // - it cannot be concat node (too short)
+ right.CopyTo(0, left.contents, left.length, right.length);
+ left.length += right.length;
+ return left;
+ } else {
+ RopeNode<T> concatNode = new RopeNode<T>();
+ concatNode.left = left;
+ concatNode.right = right;
+ concatNode.length = left.length + right.length;
+ concatNode.Rebalance();
+ return concatNode;
+ }
+ }
+
+ /// <summary>
+ /// Splits this leaf node at offset and returns a new node with the part of the text after offset.
+ /// </summary>
+ RopeNode<T> SplitAfter(int offset)
+ {
+ Debug.Assert(!isShared && height == 0 && contents != null);
+ RopeNode<T> newPart = new RopeNode<T>();
+ newPart.contents = new T[NodeSize];
+ newPart.length = this.length - offset;
+ Array.Copy(this.contents, offset, newPart.contents, 0, newPart.length);
+ this.length = offset;
+ return newPart;
+ }
+
+ internal RopeNode<T> Insert(int offset, RopeNode<T> newElements)
+ {
+ if (offset == 0) {
+ return Concat(newElements, this);
+ } else if (offset == this.length) {
+ return Concat(this, newElements);
+ }
+
+ // first clone this node (converts function nodes to leaf or concat nodes)
+ RopeNode<T> result = CloneIfShared();
+ if (result.height == 0) {
+ // leaf node: we'll need to split this node
+ RopeNode<T> left = result;
+ RopeNode<T> right = left.SplitAfter(offset);
+ return Concat(Concat(left, newElements), right);
+ } else {
+ // concat node
+ if (offset < result.left.length) {
+ result.left = result.left.Insert(offset, newElements);
+ } else {
+ result.right = result.right.Insert(offset - result.left.length, newElements);
+ }
+ result.length += newElements.length;
+ result.Rebalance();
+ return result;
+ }
+ }
+
+ internal RopeNode<T> Insert(int offset, T[] array, int arrayIndex, int count)
+ {
+ Debug.Assert(count > 0);
+
+ if (this.length + count < RopeNode<char>.NodeSize) {
+ RopeNode<T> result = CloneIfShared();
+ // result must be leaf node (Clone never returns function nodes, too short for concat node)
+ int lengthAfterOffset = result.length - offset;
+ T[] resultContents = result.contents;
+ for (int i = lengthAfterOffset; i >= 0; i--) {
+ resultContents[i + offset + count] = resultContents[i + offset];
+ }
+ Array.Copy(array, arrayIndex, resultContents, offset, count);
+ result.length += count;
+ return result;
+ } else if (height == 0) {
+ // TODO: implement this more efficiently?
+ return Insert(offset, CreateFromArray(array, arrayIndex, count));
+ } else {
+ // this is a concat node (both leafs and function nodes are handled by the case above)
+ RopeNode<T> result = CloneIfShared();
+ if (offset < result.left.length) {
+ result.left = result.left.Insert(offset, array, arrayIndex, count);
+ } else {
+ result.right = result.right.Insert(offset - result.left.length, array, arrayIndex, count);
+ }
+ result.length += count;
+ result.Rebalance();
+ return result;
+ }
+ }
+
+ internal RopeNode<T> RemoveRange(int index, int count)
+ {
+ Debug.Assert(count > 0);
+
+ // produce empty node when one node is deleted completely
+ if (index == 0 && count == this.length)
+ return emptyRopeNode;
+
+ int endIndex = index + count;
+ RopeNode<T> result = CloneIfShared(); // convert function node to concat/leaf
+ if (result.height == 0) {
+ int remainingAfterEnd = result.length - endIndex;
+ for (int i = 0; i < remainingAfterEnd; i++) {
+ result.contents[index + i] = result.contents[endIndex + i];
+ }
+ result.length -= count;
+ } else {
+ if (endIndex <= result.left.length) {
+ // deletion is only within the left part
+ result.left = result.left.RemoveRange(index, count);
+ } else if (index >= result.left.length) {
+ // deletion is only within the right part
+ result.right = result.right.RemoveRange(index - result.left.length, count);
+ } else {
+ // deletion overlaps both parts
+ int deletionAmountOnLeftSide = result.left.length - index;
+ result.left = result.left.RemoveRange(index, deletionAmountOnLeftSide);
+ result.right = result.right.RemoveRange(0, count - deletionAmountOnLeftSide);
+ }
+ // The deletion might have introduced empty nodes. Those must be removed.
+ if (result.left.length == 0)
+ return result.right;
+ if (result.right.length == 0)
+ return result.left;
+
+ result.length -= count;
+ result.MergeIfPossible();
+ result.Rebalance();
+ }
+ return result;
+ }
+
+ #region Debug Output
+ #if DEBUG
+ internal virtual void AppendTreeToString(StringBuilder b, int indent)
+ {
+ b.AppendLine(ToString());
+ indent += 2;
+ if (left != null) {
+ b.Append(' ', indent);
+ b.Append("L: ");
+ left.AppendTreeToString(b, indent);
+ }
+ if (right != null) {
+ b.Append(' ', indent);
+ b.Append("R: ");
+ right.AppendTreeToString(b, indent);
+ }
+ }
+
+ public override string ToString()
+ {
+ if (contents != null) {
+ char[] charContents = contents as char[];
+ if (charContents != null)
+ return "[Leaf length=" + length + ", isShared=" + isShared + ", text=\"" + new string(charContents, 0, length) + "\"]";
+ else
+ return "[Leaf length=" + length + ", isShared=" + isShared + "\"]";
+ } else {
+ return "[Concat length=" + length + ", isShared=" + isShared + ", height=" + height + ", Balance=" + this.Balance + "]";
+ }
+ }
+
+ internal string GetTreeAsString()
+ {
+ StringBuilder b = new StringBuilder();
+ AppendTreeToString(b, 0);
+ return b.ToString();
+ }
+ #endif
+ #endregion
+
+ /// <summary>
+ /// Gets the root node of the subtree from a lazily evaluated function node.
+ /// Such nodes are always marked as shared.
+ /// GetContentNode() will return either a Concat or Leaf node, never another FunctionNode.
+ /// </summary>
+ internal virtual RopeNode<T> GetContentNode()
+ {
+ throw new InvalidOperationException("Called GetContentNode() on non-FunctionNode.");
+ }
+ }
+
+ sealed class FunctionNode<T> : RopeNode<T>
+ {
+ Func<Rope<T>> initializer;
+ RopeNode<T> cachedResults;
+
+ public FunctionNode(int length, Func<Rope<T>> initializer)
+ {
+ Debug.Assert(length > 0);
+ Debug.Assert(initializer != null);
+
+ this.length = length;
+ this.initializer = initializer;
+ // Function nodes are immediately shared, but cannot be cloned.
+ // This ensures we evaluate every initializer only once.
+ this.isShared = true;
+ }
+
+ internal override RopeNode<T> GetContentNode()
+ {
+ lock (this) {
+ if (this.cachedResults == null) {
+ if (this.initializer == null)
+ throw new InvalidOperationException("Trying to load this node recursively; or: a previous call to a rope initializer failed.");
+ Func<Rope<T>> initializerCopy = this.initializer;
+ this.initializer = null;
+ Rope<T> resultRope = initializerCopy();
+ if (resultRope == null)
+ throw new InvalidOperationException("Rope initializer returned null.");
+ RopeNode<T> resultNode = resultRope.root;
+ resultNode.Publish(); // result is shared between returned rope and the rope containing this function node
+ if (resultNode.length != this.length)
+ throw new InvalidOperationException("Rope initializer returned rope with incorrect length.");
+ if (resultNode.height == 0 && resultNode.contents == null) {
+ // ResultNode is another function node.
+ // We want to guarantee that GetContentNode() never returns function nodes, so we have to
+ // go down further in the tree.
+ this.cachedResults = resultNode.GetContentNode();
+ } else {
+ this.cachedResults = resultNode;
+ }
+ }
+ return this.cachedResults;
+ }
+ }
+
+ #if DEBUG
+ internal override void AppendTreeToString(StringBuilder b, int indent)
+ {
+ RopeNode<T> resultNode;
+ lock (this) {
+ b.AppendLine(ToString());
+ resultNode = cachedResults;
+ }
+ indent += 2;
+ if (resultNode != null) {
+ b.Append(' ', indent);
+ b.Append("C: ");
+ resultNode.AppendTreeToString(b, indent);
+ }
+ }
+
+ public override string ToString()
+ {
+ return "[FunctionNode length=" + length + " initializerRan=" + (initializer == null) + "]";
+ }
+ #endif
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeTextReader.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeTextReader.cs
new file mode 100644
index 000000000..43c8fc57b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/RopeTextReader.cs
@@ -0,0 +1,105 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// TextReader implementation that reads text from a rope.
+ /// </summary>
+ public sealed class RopeTextReader : TextReader
+ {
+ Stack<RopeNode<char>> stack = new Stack<RopeNode<char>>();
+ RopeNode<char> currentNode;
+ int indexInsideNode;
+
+ /// <summary>
+ /// Creates a new RopeTextReader.
+ /// Internally, this method creates a Clone of the rope; so the text reader will always read through the old
+ /// version of the rope if it is modified. <seealso cref="Rope{T}.Clone()"/>
+ /// </summary>
+ public RopeTextReader(Rope<char> rope)
+ {
+ if (rope == null)
+ throw new ArgumentNullException("rope");
+
+ // We force the user to iterate through a clone of the rope to keep the API contract of RopeTextReader simple
+ // (what happens when a rope is modified while iterating through it?)
+ rope.root.Publish();
+
+ // special case for the empty rope:
+ // leave currentNode initialized to null (RopeTextReader doesn't support empty nodes)
+ if (rope.Length != 0) {
+ currentNode = rope.root;
+ GoToLeftMostLeaf();
+ }
+ }
+
+ void GoToLeftMostLeaf()
+ {
+ while (currentNode.contents == null) {
+ if (currentNode.height == 0) {
+ // this is a function node - move to its contained rope
+ currentNode = currentNode.GetContentNode();
+ continue;
+ }
+ Debug.Assert(currentNode.right != null);
+ stack.Push(currentNode.right);
+ currentNode = currentNode.left;
+ }
+ Debug.Assert(currentNode.height == 0);
+ }
+
+ /// <inheritdoc/>
+ public override int Peek()
+ {
+ if (currentNode == null)
+ return -1;
+ return currentNode.contents[indexInsideNode];
+ }
+
+ /// <inheritdoc/>
+ public override int Read()
+ {
+ if (currentNode == null)
+ return -1;
+ char result = currentNode.contents[indexInsideNode++];
+ if (indexInsideNode >= currentNode.length)
+ GoToNextNode();
+ return result;
+ }
+
+ void GoToNextNode()
+ {
+ if (stack.Count == 0) {
+ currentNode = null;
+ } else {
+ indexInsideNode = 0;
+ currentNode = stack.Pop();
+ GoToLeftMostLeaf();
+ }
+ }
+
+ /// <inheritdoc/>
+ public override int Read(char[] buffer, int index, int count)
+ {
+ if (currentNode == null)
+ return 0;
+ int amountInCurrentNode = currentNode.length - indexInsideNode;
+ if (count < amountInCurrentNode) {
+ Array.Copy(currentNode.contents, indexInsideNode, buffer, index, count);
+ indexInsideNode += count;
+ return count;
+ } else {
+ // read to end of current node
+ Array.Copy(currentNode.contents, indexInsideNode, buffer, index, amountInCurrentNode);
+ GoToNextNode();
+ return amountInCurrentNode;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/StringSegment.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/StringSegment.cs
new file mode 100644
index 000000000..f2fad60cf
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/StringSegment.cs
@@ -0,0 +1,107 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Represents a string with a segment.
+ /// Similar to System.ArraySegment&lt;T&gt;, but for strings instead of arrays.
+ /// </summary>
+ public struct StringSegment : IEquatable<StringSegment>
+ {
+ readonly string text;
+ readonly int offset;
+ readonly int count;
+
+ /// <summary>
+ /// Creates a new StringSegment.
+ /// </summary>
+ public StringSegment(string text, int offset, int count)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ if (offset < 0 || offset > text.Length)
+ throw new ArgumentOutOfRangeException("offset");
+ if (offset + count > text.Length)
+ throw new ArgumentOutOfRangeException("count");
+ this.text = text;
+ this.offset = offset;
+ this.count = count;
+ }
+
+ /// <summary>
+ /// Creates a new StringSegment.
+ /// </summary>
+ public StringSegment(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.text = text;
+ this.offset = 0;
+ this.count = text.Length;
+ }
+
+ /// <summary>
+ /// Gets the string used for this segment.
+ /// </summary>
+ public string Text {
+ get { return text; }
+ }
+
+ /// <summary>
+ /// Gets the start offset of the segment with the text.
+ /// </summary>
+ public int Offset {
+ get { return offset; }
+ }
+
+ /// <summary>
+ /// Gets the length of the segment.
+ /// </summary>
+ public int Count {
+ get { return count; }
+ }
+
+ #region Equals and GetHashCode implementation
+ /// <inheritdoc/>
+ public override bool Equals(object obj)
+ {
+ if (obj is StringSegment)
+ return Equals((StringSegment)obj); // use Equals method below
+ else
+ return false;
+ }
+
+ /// <inheritdoc/>
+ public bool Equals(StringSegment other)
+ {
+ // add comparisions for all members here
+ return object.ReferenceEquals(this.text, other.text) && offset == other.offset && count == other.count;
+ }
+
+ /// <inheritdoc/>
+ public override int GetHashCode()
+ {
+ return text.GetHashCode() ^ offset ^ count;
+ }
+
+ /// <summary>
+ /// Equality operator.
+ /// </summary>
+ public static bool operator ==(StringSegment left, StringSegment right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Inequality operator.
+ /// </summary>
+ public static bool operator !=(StringSegment left, StringSegment right)
+ {
+ return !left.Equals(right);
+ }
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/TextFormatterFactory.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/TextFormatterFactory.cs
new file mode 100644
index 000000000..dd6fe8b9e
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/TextFormatterFactory.cs
@@ -0,0 +1,71 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.TextFormatting;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Creates TextFormatter instances that with the correct TextFormattingMode, if running on .NET 4.0.
+ /// </summary>
+ static class TextFormatterFactory
+ {
+ /// <summary>
+ /// Creates a <see cref="TextFormatter"/> using the formatting mode used by the specified owner object.
+ /// </summary>
+ public static TextFormatter Create(DependencyObject owner)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+ return TextFormatter.Create(TextOptions.GetTextFormattingMode(owner));
+ }
+
+ /// <summary>
+ /// Returns whether the specified dependency property affects the text formatter creation.
+ /// Controls should re-create their text formatter for such property changes.
+ /// </summary>
+ public static bool PropertyChangeAffectsTextFormatter(DependencyProperty dp)
+ {
+ return dp == TextOptions.TextFormattingModeProperty;
+ }
+
+ /// <summary>
+ /// Creates formatted text.
+ /// </summary>
+ /// <param name="element">The owner element. The text formatter setting are read from this element.</param>
+ /// <param name="text">The text.</param>
+ /// <param name="typeface">The typeface to use. If this parameter is null, the typeface of the <paramref name="element"/> will be used.</param>
+ /// <param name="emSize">The font size. If this parameter is null, the font size of the <paramref name="element"/> will be used.</param>
+ /// <param name="foreground">The foreground color. If this parameter is null, the foreground of the <paramref name="element"/> will be used.</param>
+ /// <returns>A FormattedText object using the specified settings.</returns>
+ public static FormattedText CreateFormattedText(FrameworkElement element, string text, Typeface typeface, double? emSize, Brush foreground)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+ if (text == null)
+ throw new ArgumentNullException("text");
+ if (typeface == null)
+ typeface = element.CreateTypeface();
+ if (emSize == null)
+ emSize = TextBlock.GetFontSize(element);
+ if (foreground == null)
+ foreground = TextBlock.GetForeground(element);
+ return new FormattedText(
+ text,
+ CultureInfo.CurrentCulture,
+ FlowDirection.LeftToRight,
+ typeface,
+ emSize.Value,
+ foreground,
+ null,
+ TextOptions.GetTextFormattingMode(element)
+ );
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ThrowUtil.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ThrowUtil.cs
new file mode 100644
index 000000000..69dbb5ad0
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/ThrowUtil.cs
@@ -0,0 +1,56 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Globalization;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Contains exception-throwing helper methods.
+ /// </summary>
+ static class ThrowUtil
+ {
+ /// <summary>
+ /// Throws an ArgumentNullException if <paramref name="val"/> is null; otherwise
+ /// returns val.
+ /// </summary>
+ /// <example>
+ /// Use this method to throw an ArgumentNullException when using parameters for base
+ /// constructor calls.
+ /// <code>
+ /// public VisualLineText(string text) : base(ThrowUtil.CheckNotNull(text, "text").Length)
+ /// </code>
+ /// </example>
+ public static T CheckNotNull<T>(T val, string parameterName) where T : class
+ {
+ if (val == null)
+ throw new ArgumentNullException(parameterName);
+ return val;
+ }
+
+ public static int CheckNotNegative(int val, string parameterName)
+ {
+ if (val < 0)
+ throw new ArgumentOutOfRangeException(parameterName, val, "value must not be negative");
+ return val;
+ }
+
+ public static int CheckInRangeInclusive(int val, string parameterName, int lower, int upper)
+ {
+ if (val < lower || val > upper)
+ throw new ArgumentOutOfRangeException(parameterName, val, "Expected: " + lower.ToString(CultureInfo.InvariantCulture) + " <= " + parameterName + " <= " + upper.ToString(CultureInfo.InvariantCulture));
+ return val;
+ }
+
+ public static InvalidOperationException NoDocumentAssigned()
+ {
+ return new InvalidOperationException("Document is null");
+ }
+
+ public static InvalidOperationException NoValidCaretPosition()
+ {
+ return new InvalidOperationException("Could not find a valid caret position in the line");
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/WeakEventManagerBase.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/WeakEventManagerBase.cs
new file mode 100644
index 000000000..87e4de515
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/WeakEventManagerBase.cs
@@ -0,0 +1,86 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Diagnostics;
+using System.Windows;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// WeakEventManager with AddListener/RemoveListener and CurrentManager implementation.
+ /// Helps implementing the WeakEventManager pattern with less code.
+ /// </summary>
+ public abstract class WeakEventManagerBase<TManager, TEventSource> : WeakEventManager
+ where TManager : WeakEventManagerBase<TManager, TEventSource>, new()
+ where TEventSource : class
+ {
+ /// <summary>
+ /// Creates a new WeakEventManagerBase instance.
+ /// </summary>
+ protected WeakEventManagerBase()
+ {
+ Debug.Assert(GetType() == typeof(TManager));
+ }
+
+ /// <summary>
+ /// Adds a weak event listener.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static void AddListener(TEventSource source, IWeakEventListener listener)
+ {
+ CurrentManager.ProtectedAddListener(source, listener);
+ }
+
+ /// <summary>
+ /// Removes a weak event listener.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
+ public static void RemoveListener(TEventSource source, IWeakEventListener listener)
+ {
+ CurrentManager.ProtectedRemoveListener(source, listener);
+ }
+
+ /// <inheritdoc/>
+ protected sealed override void StartListening(object source)
+ {
+ if (source == null)
+ throw new ArgumentNullException("source");
+ StartListening((TEventSource)source);
+ }
+
+ /// <inheritdoc/>
+ protected sealed override void StopListening(object source)
+ {
+ if (source == null)
+ throw new ArgumentNullException("source");
+ StopListening((TEventSource)source);
+ }
+
+ /// <summary>
+ /// Attaches the event handler.
+ /// </summary>
+ protected abstract void StartListening(TEventSource source);
+
+ /// <summary>
+ /// Detaches the event handler.
+ /// </summary>
+ protected abstract void StopListening(TEventSource source);
+
+ /// <summary>
+ /// Gets the current manager.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
+ protected static TManager CurrentManager {
+ get {
+ Type managerType = typeof(TManager);
+ TManager manager = (TManager)GetCurrentManager(managerType);
+ if (manager == null) {
+ manager = new TManager();
+ SetCurrentManager(managerType, manager);
+ }
+ return manager;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Win32.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Win32.cs
new file mode 100644
index 000000000..421c35c5a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Utils/Win32.cs
@@ -0,0 +1,85 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media;
+
+namespace Tango.Scripting.Editors.Utils
+{
+ /// <summary>
+ /// Wrapper around Win32 functions.
+ /// </summary>
+ static class Win32
+ {
+ /// <summary>
+ /// Gets the caret blink time.
+ /// </summary>
+ public static TimeSpan CaretBlinkTime {
+ get { return TimeSpan.FromMilliseconds(SafeNativeMethods.GetCaretBlinkTime()); }
+ }
+
+ /// <summary>
+ /// Creates an invisible Win32 caret for the specified Visual with the specified size (coordinates local to the owner visual).
+ /// </summary>
+ public static bool CreateCaret(Visual owner, Size size)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+ HwndSource source = PresentationSource.FromVisual(owner) as HwndSource;
+ if (source != null) {
+ Vector r = owner.PointToScreen(new Point(size.Width, size.Height)) - owner.PointToScreen(new Point(0, 0));
+ return SafeNativeMethods.CreateCaret(source.Handle, IntPtr.Zero, (int)Math.Ceiling(r.X), (int)Math.Ceiling(r.Y));
+ } else {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Sets the position of the caret previously created using <see cref="CreateCaret"/>. position is relative to the owner visual.
+ /// </summary>
+ public static bool SetCaretPosition(Visual owner, Point position)
+ {
+ if (owner == null)
+ throw new ArgumentNullException("owner");
+ HwndSource source = PresentationSource.FromVisual(owner) as HwndSource;
+ if (source != null) {
+ Point pointOnRootVisual = owner.TransformToAncestor(source.RootVisual).Transform(position);
+ Point pointOnHwnd = pointOnRootVisual.TransformToDevice(source.RootVisual);
+ return SafeNativeMethods.SetCaretPos((int)pointOnHwnd.X, (int)pointOnHwnd.Y);
+ } else {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Destroys the caret previously created using <see cref="CreateCaret"/>.
+ /// </summary>
+ public static bool DestroyCaret()
+ {
+ return SafeNativeMethods.DestroyCaret();
+ }
+
+ [SuppressUnmanagedCodeSecurity]
+ static class SafeNativeMethods
+ {
+ [DllImport("user32.dll")]
+ public static extern int GetCaretBlinkTime();
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool SetCaretPos(int x, int y);
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool DestroyCaret();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttribute.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttribute.cs
new file mode 100644
index 000000000..b8b726ef9
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttribute.cs
@@ -0,0 +1,129 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Name-value pair in a tag
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
+ public class AXmlAttribute: AXmlObject
+ {
+ /// <summary> Name with namespace prefix - exactly as in source file </summary>
+ public string Name { get; internal set; }
+ /// <summary> Equals sign and surrounding whitespace </summary>
+ public string EqualsSign { get; internal set; }
+ /// <summary> The raw value - exactly as in source file (*probably* quoted and escaped) </summary>
+ public string QuotedValue { get; internal set; }
+ /// <summary> Unquoted and dereferenced value of the attribute </summary>
+ public string Value { get; internal set; }
+
+ internal override void DebugCheckConsistency(bool checkParentPointers)
+ {
+ DebugAssert(Name != null, "Null Name");
+ DebugAssert(EqualsSign != null, "Null EqualsSign");
+ DebugAssert(QuotedValue != null, "Null QuotedValue");
+ DebugAssert(Value != null, "Null Value");
+ base.DebugCheckConsistency(checkParentPointers);
+ }
+
+ #region Helpper methods
+
+ /// <summary> The element containing this attribute </summary>
+ /// <returns> Null if orphaned </returns>
+ public AXmlElement ParentElement {
+ get {
+ AXmlTag tag = this.Parent as AXmlTag;
+ if (tag != null) {
+ return tag.Parent as AXmlElement;
+ }
+ return null;
+ }
+ }
+
+ /// <summary> The part of name before ":"</summary>
+ /// <returns> Empty string if not found </returns>
+ public string Prefix {
+ get {
+ return GetNamespacePrefix(this.Name);
+ }
+ }
+
+ /// <summary> The part of name after ":" </summary>
+ /// <returns> Whole name if ":" not found </returns>
+ public string LocalName {
+ get {
+ return GetLocalName(this.Name);
+ }
+ }
+
+ /// <summary>
+ /// Resolved namespace of the name. Empty string if not found
+ /// From the specification: "The namespace name for an unprefixed attribute name always has no value."
+ /// </summary>
+ public string Namespace {
+ get {
+ if (string.IsNullOrEmpty(this.Prefix)) return NoNamespace;
+
+ AXmlElement elem = this.ParentElement;
+ if (elem != null) {
+ return elem.ResolvePrefix(this.Prefix);
+ }
+ return NoNamespace; // Orphaned attribute
+ }
+ }
+
+ /// <summary> Attribute is declaring namespace ("xmlns" or "xmlns:*") </summary>
+ public bool IsNamespaceDeclaration {
+ get {
+ return this.Name == "xmlns" || this.Prefix == "xmlns";
+ }
+ }
+
+ #endregion
+
+ /// <inheritdoc/>
+ public override void AcceptVisitor(IAXmlVisitor visitor)
+ {
+ visitor.VisitAttribute(this);
+ }
+
+ /// <inheritdoc/>
+ internal override bool UpdateDataFrom(AXmlObject source)
+ {
+ if (!base.UpdateDataFrom(source)) return false;
+ AXmlAttribute src = (AXmlAttribute)source;
+ if (this.Name != src.Name ||
+ this.EqualsSign != src.EqualsSign ||
+ this.QuotedValue != src.QuotedValue ||
+ this.Value != src.Value)
+ {
+ OnChanging();
+ this.Name = src.Name;
+ this.EqualsSign = src.EqualsSign;
+ this.QuotedValue = src.QuotedValue;
+ this.Value = src.Value;
+ OnChanged();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}{2}{3}']", base.ToString(), this.Name, this.EqualsSign, this.Value);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttributeCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttributeCollection.cs
new file mode 100644
index 000000000..95fa83cd0
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlAttributeCollection.cs
@@ -0,0 +1,119 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Specailized attribute collection with attribute name caching
+ /// </summary>
+ public class AXmlAttributeCollection: FilteredCollection<AXmlAttribute, AXmlObjectCollection<AXmlObject>>
+ {
+ /// <summary> Empty unbound collection </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
+ Justification = "InsertItem prevents modifying the Empty collection")]
+ public static readonly AXmlAttributeCollection Empty = new AXmlAttributeCollection();
+
+ /// <summary> Create unbound collection </summary>
+ protected AXmlAttributeCollection() {}
+
+ /// <summary> Wrap the given collection. Non-attributes are filtered </summary>
+ public AXmlAttributeCollection(AXmlObjectCollection<AXmlObject> source): base(source) {}
+
+ /// <summary> Wrap the given collection. Non-attributes are filtered. Items not matching the condition are filtered. </summary>
+ public AXmlAttributeCollection(AXmlObjectCollection<AXmlObject> source, Predicate<object> condition): base(source, condition) {}
+
+ Dictionary<string, List<AXmlAttribute>> hashtable = new Dictionary<string, List<AXmlAttribute>>();
+
+ void AddToHashtable(AXmlAttribute attr)
+ {
+ string localName = attr.LocalName;
+ if (!hashtable.ContainsKey(localName)) {
+ hashtable[localName] = new List<AXmlAttribute>(1);
+ }
+ hashtable[localName].Add(attr);
+ }
+
+ void RemoveFromHashtable(AXmlAttribute attr)
+ {
+ string localName = attr.LocalName;
+ hashtable[localName].Remove(attr);
+ }
+
+ static List<AXmlAttribute> NoAttributes = new List<AXmlAttribute>();
+
+ /// <summary>
+ /// Get all attributes with given local name.
+ /// Hash table is used for lookup so this is cheap.
+ /// </summary>
+ public IEnumerable<AXmlAttribute> GetByLocalName(string localName)
+ {
+ if (hashtable.ContainsKey(localName)) {
+ return hashtable[localName];
+ } else {
+ return NoAttributes;
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void ClearItems()
+ {
+ foreach(AXmlAttribute item in this) {
+ RemoveFromHashtable(item);
+ item.Changing -= item_Changing;
+ item.Changed -= item_Changed;
+ }
+ base.ClearItems();
+ }
+
+ /// <inheritdoc/>
+ protected override void InsertItem(int index, AXmlAttribute item)
+ {
+ // prevent insertions into the static 'Empty' instance
+ if (this == Empty)
+ throw new NotSupportedException("Cannot insert into AXmlAttributeCollection.Empty");
+
+ AddToHashtable(item);
+ item.Changing += item_Changing;
+ item.Changed += item_Changed;
+ base.InsertItem(index, item);
+ }
+
+ /// <inheritdoc/>
+ protected override void RemoveItem(int index)
+ {
+ RemoveFromHashtable(this[index]);
+ this[index].Changing -= item_Changing;
+ this[index].Changed -= item_Changed;
+ base.RemoveItem(index);
+ }
+
+ /// <inheritdoc/>
+ protected override void SetItem(int index, AXmlAttribute item)
+ {
+ RemoveFromHashtable(this[index]);
+ this[index].Changing -= item_Changing;
+ this[index].Changed -= item_Changed;
+
+ AddToHashtable(item);
+ item.Changing += item_Changing;
+ item.Changed += item_Changed;
+ base.SetItem(index, item);
+ }
+
+ // Every item in the collection should be registered to these handlers
+ // so that we can handle renames
+
+ void item_Changing(object sender, AXmlObjectEventArgs e)
+ {
+ RemoveFromHashtable((AXmlAttribute)e.Object);
+ }
+
+ void item_Changed(object sender, AXmlObjectEventArgs e)
+ {
+ AddToHashtable((AXmlAttribute)e.Object);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs
new file mode 100644
index 000000000..3cc716de5
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlContainer.cs
@@ -0,0 +1,282 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Abstact base class for all types that can contain child nodes
+ /// </summary>
+ public abstract class AXmlContainer: AXmlObject
+ {
+ /// <summary>
+ /// Children of the node. It is read-only.
+ /// Note that is has CollectionChanged event.
+ /// </summary>
+ public AXmlObjectCollection<AXmlObject> Children { get; private set; }
+
+ /// <summary> Create new container </summary>
+ protected AXmlContainer()
+ {
+ this.Children = new AXmlObjectCollection<AXmlObject>();
+ }
+
+ #region Helpper methods
+
+ ObservableCollection<AXmlElement> elements;
+
+ /// <summary> Gets direcly nested elements (non-recursive) </summary>
+ public ObservableCollection<AXmlElement> Elements {
+ get {
+ if (elements == null) {
+ elements = new FilteredCollection<AXmlElement, AXmlObjectCollection<AXmlObject>>(this.Children);
+ }
+ return elements;
+ }
+ }
+
+ internal AXmlObject FirstChild {
+ get {
+ return this.Children[0];
+ }
+ }
+
+ internal AXmlObject LastChild {
+ get {
+ return this.Children[this.Children.Count - 1];
+ }
+ }
+
+ #endregion
+
+ /// <inheritdoc/>
+ public override IEnumerable<AXmlObject> GetSelfAndAllChildren()
+ {
+ return (new AXmlObject[] { this }).Flatten(
+ delegate(AXmlObject i) {
+ AXmlContainer container = i as AXmlContainer;
+ if (container != null)
+ return container.Children;
+ else
+ return null;
+ }
+ );
+ }
+
+ /// <summary>
+ /// Gets a child fully containg the given offset.
+ /// Goes recursively down the tree.
+ /// Specail case if at the end of attribute or text
+ /// </summary>
+ public AXmlObject GetChildAtOffset(int offset)
+ {
+ foreach(AXmlObject child in this.Children) {
+ if ((child is AXmlAttribute || child is AXmlText) && offset == child.EndOffset) return child;
+ if (child.StartOffset < offset && offset < child.EndOffset) {
+ AXmlContainer container = child as AXmlContainer;
+ if (container != null) {
+ return container.GetChildAtOffset(offset);
+ } else {
+ return child;
+ }
+ }
+ }
+ return this; // No childs at offset
+ }
+
+ // Only these four methods should be used to modify the collection
+
+ /// <summary> To be used exlucively by the parser </summary>
+ internal void AddChild(AXmlObject item)
+ {
+ // Childs can be only added to newly parsed items
+ Assert(this.Parent == null, "I have to be new");
+ Assert(item.IsCached, "Added item must be in cache");
+ // Do not set parent pointer
+ this.Children.InsertItemAt(this.Children.Count, item);
+ }
+
+ /// <summary> To be used exlucively by the parser </summary>
+ internal void AddChildren(IEnumerable<AXmlObject> items)
+ {
+ // Childs can be only added to newly parsed items
+ Assert(this.Parent == null, "I have to be new");
+ // Do not set parent pointer
+ this.Children.InsertItemsAt(this.Children.Count, items.ToList());
+ }
+
+ /// <summary>
+ /// To be used exclusively by the children update algorithm.
+ /// Insert child and keep links consistent.
+ /// </summary>
+ void InsertChild(int index, AXmlObject item)
+ {
+ AXmlParser.Log("Inserting {0} at index {1}", item, index);
+
+ Assert(this.Document != null, "Can not insert to dangling object");
+ Assert(item.Parent != this, "Can not own item twice");
+
+ SetParentPointersInTree(item);
+
+ this.Children.InsertItemAt(index, item);
+
+ this.Document.OnObjectInserted(index, item);
+ }
+
+ /// <summary> Recursively fix all parent pointer in a tree </summary>
+ /// <remarks>
+ /// Cache constraint:
+ /// If cached item has parent set, then the whole subtree must be consistent and document set
+ /// </remarks>
+ void SetParentPointersInTree(AXmlObject item)
+ {
+ // All items come from the parser cache
+
+ if (item.Parent == null) {
+ // Dangling object - either a new parser object or removed tree (still cached)
+ item.Parent = this;
+ item.Document = this.Document;
+ AXmlContainer container = item as AXmlContainer;
+ if (container != null) {
+ foreach(AXmlObject child in container.Children) {
+ container.SetParentPointersInTree(child);
+ }
+ }
+ } else if (item.Parent == this) {
+ // If node is attached and then deattached, it will have null parent pointer
+ // but valid subtree - so its children will alredy have correct parent pointer
+ // like in this case
+ // item.DebugCheckConsistency(false);
+ // Rest of the tree is consistent - do not recurse
+ } else {
+ // From cache & parent set => consitent subtree
+ // item.DebugCheckConsistency(false);
+ // The parent (or any futher parents) can not be part of parsed document
+ // becuase otherwise this item would be included twice => safe to change parents
+ // Maintain cache constraint by setting parents to null
+ foreach(AXmlObject ancest in item.GetAncestors().ToList()) {
+ ancest.Parent = null;
+ }
+ item.Parent = this;
+ // Rest of the tree is consistent - do not recurse
+ }
+ }
+
+ /// <summary>
+ /// To be used exclusively by the children update algorithm.
+ /// Remove child, set parent to null and notify the document
+ /// </summary>
+ void RemoveChild(int index)
+ {
+ AXmlObject removed = this.Children[index];
+ AXmlParser.Log("Removing {0} at index {1}", removed, index);
+
+ // Stop tracking if the object can not be used again
+ if (!removed.IsCached)
+ this.Document.Parser.TrackedSegments.RemoveParsedObject(removed);
+
+ // Null parent pointer
+ Assert(removed.Parent == this, "Inconsistent child");
+ removed.Parent = null;
+
+ this.Children.RemoveItemAt(index);
+
+ this.Document.OnObjectRemoved(index, removed);
+ }
+
+ /// <summary> Verify that the subtree is consistent. Only in debug build. </summary>
+ /// <remarks> Parent pointers might be null or pointing somewhere else in parse tree </remarks>
+ internal override void DebugCheckConsistency(bool checkParentPointers)
+ {
+ base.DebugCheckConsistency(checkParentPointers);
+ AXmlObject prevChild = null;
+ int myStartOffset = this.StartOffset;
+ int myEndOffset = this.EndOffset;
+ foreach(AXmlObject child in this.Children) {
+ Assert(child.Length != 0, "Empty child");
+ if (checkParentPointers) {
+ Assert(child.Parent != null, "Null parent reference");
+ Assert(child.Parent == this, "Inccorect parent reference");
+ }
+ if (this.Document != null) {
+ Assert(child.Document != null, "Child has null document");
+ Assert(child.Document == this.Document, "Child is in different document");
+ }
+ if (this.IsCached)
+ Assert(child.IsCached, "Child not in cache");
+ Assert(myStartOffset <= child.StartOffset && child.EndOffset <= myEndOffset, "Child not within parent text range");
+ if (prevChild != null)
+ Assert(prevChild.EndOffset <= child.StartOffset, "Overlaping childs");
+ child.DebugCheckConsistency(checkParentPointers);
+ prevChild = child;
+ }
+ }
+
+ /// <remarks>
+ /// Note the the method is not called recuively.
+ /// Only the helper methods are recursive.
+ /// </remarks>
+ internal void UpdateTreeFrom(AXmlContainer srcContainer)
+ {
+ this.StartOffset = srcContainer.StartOffset; // Force the update
+ this.UpdateDataFrom(srcContainer);
+ RemoveChildrenNotIn(srcContainer.Children);
+ InsertAndUpdateChildrenFrom(srcContainer.Children);
+ }
+
+ void RemoveChildrenNotIn(IList<AXmlObject> srcList)
+ {
+ Dictionary<int, AXmlObject> srcChildren = srcList.ToDictionary(i => i.StartOffset);
+ for(int i = 0; i < this.Children.Count;) {
+ AXmlObject child = this.Children[i];
+ AXmlObject srcChild;
+
+ if (srcChildren.TryGetValue(child.StartOffset, out srcChild) && child.CanUpdateDataFrom(srcChild)) {
+ // Keep only one item with given offset (we might have several due to deletion)
+ srcChildren.Remove(child.StartOffset);
+ // If contaner that needs updating
+ AXmlContainer childAsContainer = child as AXmlContainer;
+ if (childAsContainer != null && child.LastUpdatedFrom != srcChild)
+ childAsContainer.RemoveChildrenNotIn(((AXmlContainer)srcChild).Children);
+ i++;
+ } else {
+ RemoveChild(i);
+ }
+ }
+ }
+
+ void InsertAndUpdateChildrenFrom(IList<AXmlObject> srcList)
+ {
+ for(int i = 0; i < srcList.Count; i++) {
+ // End of our list?
+ if (i == this.Children.Count) {
+ InsertChild(i, srcList[i]);
+ continue;
+ }
+ AXmlObject child = this.Children[i];
+ AXmlObject srcChild = srcList[i];
+
+ if (child.CanUpdateDataFrom(srcChild)) { // includes offset test
+ // Does it need updating?
+ if (child.LastUpdatedFrom != srcChild) {
+ child.UpdateDataFrom(srcChild);
+ AXmlContainer childAsContainer = child as AXmlContainer;
+ if (childAsContainer != null)
+ childAsContainer.InsertAndUpdateChildrenFrom(((AXmlContainer)srcChild).Children);
+ }
+ } else {
+ InsertChild(i, srcChild);
+ }
+ }
+ Assert(this.Children.Count == srcList.Count, "List lengths differ after update");
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlDocument.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlDocument.cs
new file mode 100644
index 000000000..f96c50ca4
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlDocument.cs
@@ -0,0 +1,69 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// The root object of the XML document
+ /// </summary>
+ public class AXmlDocument: AXmlContainer
+ {
+ /// <summary> Parser that produced this document </summary>
+ internal AXmlParser Parser { get; set; }
+
+ /// <summary> Occurs when object is added to any part of the document </summary>
+ public event EventHandler<NotifyCollectionChangedEventArgs> ObjectInserted;
+ /// <summary> Occurs when object is removed from any part of the document </summary>
+ public event EventHandler<NotifyCollectionChangedEventArgs> ObjectRemoved;
+ /// <summary> Occurs before local data of any object in the document changes </summary>
+ public event EventHandler<AXmlObjectEventArgs> ObjectChanging;
+ /// <summary> Occurs after local data of any object in the document changed </summary>
+ public event EventHandler<AXmlObjectEventArgs> ObjectChanged;
+
+ internal void OnObjectInserted(int index, AXmlObject obj)
+ {
+ if (ObjectInserted != null)
+ ObjectInserted(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new AXmlObject[] { obj }.ToList(), index));
+ }
+
+ internal void OnObjectRemoved(int index, AXmlObject obj)
+ {
+ if (ObjectRemoved != null)
+ ObjectRemoved(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new AXmlObject[] { obj }.ToList(), index));
+ }
+
+ internal void OnObjectChanging(AXmlObject obj)
+ {
+ if (ObjectChanging != null)
+ ObjectChanging(this, new AXmlObjectEventArgs() { Object = obj } );
+ }
+
+ internal void OnObjectChanged(AXmlObject obj)
+ {
+ if (ObjectChanged != null)
+ ObjectChanged(this, new AXmlObjectEventArgs() { Object = obj } );
+ }
+
+ /// <inheritdoc/>
+ public override void AcceptVisitor(IAXmlVisitor visitor)
+ {
+ visitor.VisitDocument(this);
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[{0} Chld:{1}]", base.ToString(), this.Children.Count);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs
new file mode 100644
index 000000000..080d538e8
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlElement.cs
@@ -0,0 +1,226 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Logical grouping of other nodes together.
+ /// </summary>
+ public class AXmlElement: AXmlContainer
+ {
+ /// <summary> No tags are missing anywhere within this element (recursive) </summary>
+ public bool IsProperlyNested { get; set; }
+ /// <returns> True in wellformed XML </returns>
+ public bool HasStartOrEmptyTag { get; set; }
+ /// <returns> True in wellformed XML </returns>
+ public bool HasEndTag { get; set; }
+
+ /// <inheritdoc/>
+ internal override bool UpdateDataFrom(AXmlObject source)
+ {
+ if (!base.UpdateDataFrom(source)) return false;
+ AXmlElement src = (AXmlElement)source;
+ // Clear the cache for this - quite expensive
+ attributesAndElements = null;
+ if (this.IsProperlyNested != src.IsProperlyNested ||
+ this.HasStartOrEmptyTag != src.HasStartOrEmptyTag ||
+ this.HasEndTag != src.HasEndTag)
+ {
+ OnChanging();
+ this.IsProperlyNested = src.IsProperlyNested;
+ this.HasStartOrEmptyTag = src.HasStartOrEmptyTag;
+ this.HasEndTag = src.HasEndTag;
+ OnChanged();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /// <summary> The start or empty-element tag if there is any </summary>
+ internal AXmlTag StartTag {
+ get {
+ Assert(HasStartOrEmptyTag, "Does not have a start tag");
+ return (AXmlTag)this.Children[0];
+ }
+ }
+
+ /// <summary> The end tag if there is any </summary>
+ internal AXmlTag EndTag {
+ get {
+ Assert(HasEndTag, "Does not have an end tag");
+ return (AXmlTag)this.Children[this.Children.Count - 1];
+ }
+ }
+
+ internal override void DebugCheckConsistency(bool checkParentPointers)
+ {
+ DebugAssert(Children.Count > 0, "No children");
+ base.DebugCheckConsistency(checkParentPointers);
+ }
+
+ #region Helpper methods
+
+ /// <summary> Gets attributes of the element </summary>
+ /// <remarks>
+ /// Warning: this is a cenvenience method to access the attributes of the start tag.
+ /// However, since the start tag might be moved/replaced, this property might return
+ /// different values over time.
+ /// </remarks>
+ public AXmlAttributeCollection Attributes {
+ get {
+ if (this.HasStartOrEmptyTag) {
+ return this.StartTag.Attributes;
+ } else {
+ return AXmlAttributeCollection.Empty;
+ }
+ }
+ }
+
+ ObservableCollection<AXmlObject> attributesAndElements;
+
+ /// <summary> Gets both attributes and elements. Expensive, avoid use. </summary>
+ /// <remarks> Warning: the collection will regenerate after each update </remarks>
+ public ObservableCollection<AXmlObject> AttributesAndElements {
+ get {
+ if (attributesAndElements == null) {
+ if (this.HasStartOrEmptyTag) {
+ attributesAndElements = new MergedCollection<AXmlObject, ObservableCollection<AXmlObject>> (
+ // New wrapper with RawObject types
+ new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.StartTag.Children, x => x is AXmlAttribute),
+ new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.Children, x => x is AXmlElement)
+ );
+ } else {
+ attributesAndElements = new FilteredCollection<AXmlObject, AXmlObjectCollection<AXmlObject>>(this.Children, x => x is AXmlElement);
+ }
+ }
+ return attributesAndElements;
+ }
+ }
+
+ /// <summary> Name with namespace prefix - exactly as in source </summary>
+ public string Name {
+ get {
+ if (this.HasStartOrEmptyTag) {
+ return this.StartTag.Name;
+ } else {
+ return this.EndTag.Name;
+ }
+ }
+ }
+
+ /// <summary> The part of name before ":" </summary>
+ /// <returns> Empty string if not found </returns>
+ public string Prefix {
+ get {
+ return GetNamespacePrefix(this.Name);
+ }
+ }
+
+ /// <summary> The part of name after ":" </summary>
+ /// <returns> Empty string if not found </returns>
+ public string LocalName {
+ get {
+ return GetLocalName(this.Name);
+ }
+ }
+
+ /// <summary> Resolved namespace of the name </summary>
+ /// <returns> Empty string if prefix is not found </returns>
+ public string Namespace {
+ get {
+ string prefix = this.Prefix;
+ if (string.IsNullOrEmpty(prefix)) {
+ return FindDefaultNamespace();
+ } else {
+ return ResolvePrefix(prefix);
+ }
+ }
+ }
+
+ /// <summary> Find the defualt namespace for this context </summary>
+ public string FindDefaultNamespace()
+ {
+ AXmlElement current = this;
+ while(current != null) {
+ string namesapce = current.GetAttributeValue(NoNamespace, "xmlns");
+ if (namesapce != null) return namesapce;
+ current = current.Parent as AXmlElement;
+ }
+ return string.Empty; // No namesapce
+ }
+
+ /// <summary>
+ /// Recursively resolve given prefix in this context. Prefix must have some value.
+ /// </summary>
+ /// <returns> Empty string if prefix is not found </returns>
+ public string ResolvePrefix(string prefix)
+ {
+ if (string.IsNullOrEmpty(prefix)) throw new ArgumentException("No prefix given", "prefix");
+
+ // Implicit namesapces
+ if (prefix == "xml") return XmlNamespace;
+ if (prefix == "xmlns") return XmlnsNamespace;
+
+ AXmlElement current = this;
+ while(current != null) {
+ string namesapce = current.GetAttributeValue(XmlnsNamespace, prefix);
+ if (namesapce != null) return namesapce;
+ current = current.Parent as AXmlElement;
+ }
+ return NoNamespace; // Can not find prefix
+ }
+
+ /// <summary>
+ /// Get unquoted value of attribute.
+ /// It looks in the no namespace (empty string).
+ /// </summary>
+ /// <returns>Null if not found</returns>
+ public string GetAttributeValue(string localName)
+ {
+ return GetAttributeValue(NoNamespace, localName);
+ }
+
+ /// <summary>
+ /// Get unquoted value of attribute
+ /// </summary>
+ /// <param name="namespace">Namespace. Can be no namepace (empty string), which is the default for attributes.</param>
+ /// <param name="localName">Local name - text after ":"</param>
+ /// <returns>Null if not found</returns>
+ public string GetAttributeValue(string @namespace, string localName)
+ {
+ @namespace = @namespace ?? string.Empty;
+ foreach(AXmlAttribute attr in this.Attributes.GetByLocalName(localName)) {
+ DebugAssert(attr.LocalName == localName, "Bad hashtable");
+ if (attr.Namespace == @namespace) {
+ return attr.Value;
+ }
+ }
+ return null;
+ }
+
+ #endregion
+
+ /// <inheritdoc/>
+ public override void AcceptVisitor(IAXmlVisitor visitor)
+ {
+ visitor.VisitElement(this);
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}' Attr:{2} Chld:{3} Nest:{4}]", base.ToString(), this.Name, this.HasStartOrEmptyTag ? this.StartTag.Children.Count : 0, this.Children.Count, this.IsProperlyNested ? "Ok" : "Bad");
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs
new file mode 100644
index 000000000..4951a7c0b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObject.cs
@@ -0,0 +1,266 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Abstact base class for all types
+ /// </summary>
+ public abstract class AXmlObject: TextSegment
+ {
+ /// <summary> Empty string. The namespace used if there is no "xmlns" specified </summary>
+ public static readonly string NoNamespace = string.Empty;
+
+ /// <summary> Namespace for "xml:" prefix: "http://www.w3.org/XML/1998/namespace" </summary>
+ public static readonly string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
+
+ /// <summary> Namesapce for "xmlns:" prefix: "http://www.w3.org/2000/xmlns/" </summary>
+ public static readonly string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
+
+ /// <summary> Parent node. </summary>
+ /// <remarks>
+ /// New cached items start with null parent.
+ /// Cache constraint:
+ /// If cached item has parent set, then the whole subtree must be consistent
+ /// </remarks>
+ public AXmlObject Parent { get; set; }
+
+ /// <summary>
+ /// Gets the document that has owns this object.
+ /// Once set, it is not changed. Not even set to null.
+ /// </summary>
+ internal AXmlDocument Document { get; set; }
+
+ /// <summary> Creates new object </summary>
+ protected AXmlObject()
+ {
+ this.LastUpdatedFrom = this;
+ }
+
+ /// <summary> Occurs before the value of any local properties changes. Nested changes do not cause the event to occur </summary>
+ public event EventHandler<AXmlObjectEventArgs> Changing;
+
+ /// <summary> Occurs after the value of any local properties changed. Nested changes do not cause the event to occur </summary>
+ public event EventHandler<AXmlObjectEventArgs> Changed;
+
+ /// <summary> Raises Changing event </summary>
+ protected void OnChanging()
+ {
+ AXmlParser.Log("Changing {0}", this);
+ if (Changing != null) {
+ Changing(this, new AXmlObjectEventArgs() { Object = this } );
+ }
+ AXmlDocument doc = this.Document;
+ if (doc != null) {
+ doc.OnObjectChanging(this);
+ }
+ // As a convenience, also rasie an event for the parent element
+ AXmlTag me = this as AXmlTag;
+ if (me != null && (me.IsStartOrEmptyTag || me.IsEndTag) && me.Parent is AXmlElement) {
+ me.Parent.OnChanging();
+ }
+ }
+
+ /// <summary> Raises Changed event </summary>
+ protected void OnChanged()
+ {
+ AXmlParser.Log("Changed {0}", this);
+ if (Changed != null) {
+ Changed(this, new AXmlObjectEventArgs() { Object = this } );
+ }
+ AXmlDocument doc = this.Document;
+ if (doc != null) {
+ doc.OnObjectChanged(this);
+ }
+ // As a convenience, also rasie an event for the parent element
+ AXmlTag me = this as AXmlTag;
+ if (me != null && (me.IsStartOrEmptyTag || me.IsEndTag) && me.Parent is AXmlElement) {
+ me.Parent.OnChanged();
+ }
+ }
+
+ List<SyntaxError> syntaxErrors;
+
+ /// <summary>
+ /// The error that occured in the context of this node (excluding nested nodes)
+ /// </summary>
+ public IEnumerable<SyntaxError> MySyntaxErrors {
+ get {
+ if (syntaxErrors == null) {
+ return new SyntaxError[] {};
+ } else {
+ return syntaxErrors;
+ }
+ }
+ }
+
+ /// <summary>
+ /// The error that occured in the context of this node and all nested nodes.
+ /// It has O(n) cost.
+ /// </summary>
+ public IEnumerable<SyntaxError> SyntaxErrors {
+ get {
+ return GetSelfAndAllChildren().SelectMany(obj => obj.MySyntaxErrors);
+ }
+ }
+
+ internal void AddSyntaxError(SyntaxError error)
+ {
+ DebugAssert(error.Object == this, "Must own the error");
+ if (this.syntaxErrors == null) this.syntaxErrors = new List<SyntaxError>();
+ syntaxErrors.Add(error);
+ }
+
+ /// <summary> Throws exception if condition is false </summary>
+ /// <remarks> Present in release mode - use only for very cheap aserts </remarks>
+ protected static void Assert(bool condition, string message)
+ {
+ if (!condition) {
+ throw new InternalException("Assertion failed: " + message);
+ }
+ }
+
+ /// <summary> Throws exception if condition is false </summary>
+ [Conditional("DEBUG")]
+ protected static void DebugAssert(bool condition, string message)
+ {
+ if (!condition) {
+ throw new InternalException("Assertion failed: " + message);
+ }
+ }
+
+ /// <summary> Recursively gets self and all nested nodes. </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "Using a method makes the API look more LINQ-like and indicates that the returned collection is computed every time.")]
+ public virtual IEnumerable<AXmlObject> GetSelfAndAllChildren()
+ {
+ return new AXmlObject[] { this };
+ }
+
+ /// <summary> Get all ancestors of this node </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
+ Justification = "Using a method makes the API look more LINQ-like and indicates that the returned collection is computed every time.")]
+ public IEnumerable<AXmlObject> GetAncestors()
+ {
+ AXmlObject curr = this.Parent;
+ while(curr != null) {
+ yield return curr;
+ curr = curr.Parent;
+ }
+ }
+
+ /// <summary> Call appropriate visit method on the given visitor </summary>
+ public abstract void AcceptVisitor(IAXmlVisitor visitor);
+
+ /// <summary> The parser tree object this object was updated from </summary>
+ /// <remarks> Initialized to 'this' </remarks>
+ internal AXmlObject LastUpdatedFrom { get; private set; }
+
+ internal bool IsCached { get; set; }
+
+ /// <summary> Is call to UpdateDataFrom is allowed? </summary>
+ internal bool CanUpdateDataFrom(AXmlObject source)
+ {
+ return
+ this.GetType() == source.GetType() &&
+ this.StartOffset == source.StartOffset &&
+ (this.LastUpdatedFrom == source || !this.IsCached);
+ }
+
+ /// <summary> Copy all data from the 'source' to this object </summary>
+ /// <remarks> Returns true if any updates were done </remarks>
+ internal virtual bool UpdateDataFrom(AXmlObject source)
+ {
+ Assert(this.GetType() == source.GetType(), "Source has different type");
+ DebugAssert(this.StartOffset == source.StartOffset, "Source has different StartOffset");
+
+ if (this.LastUpdatedFrom == source) {
+ DebugAssert(this.EndOffset == source.EndOffset, "Source has different EndOffset");
+ return false;
+ }
+
+ Assert(!this.IsCached, "Can not update cached item");
+ Assert(source.IsCached, "Must update from cache");
+
+ this.LastUpdatedFrom = source;
+ this.StartOffset = source.StartOffset;
+ // In some cases we are just updating objects of that same
+ // type and position and hoping to be luckily right
+ this.EndOffset = source.EndOffset;
+
+ // Do not bother comparing - assume changed if non-null
+ if (this.syntaxErrors != null || source.syntaxErrors != null) {
+ // May be called again in derived class - oh, well, does not matter
+ OnChanging();
+ this.Document.Parser.TrackedSegments.RemoveSyntaxErrorsOf(this);
+ if (source.syntaxErrors == null) {
+ this.syntaxErrors = null;
+ } else {
+ this.syntaxErrors = new List<SyntaxError>();
+ foreach(var error in source.MySyntaxErrors) {
+ // The object differs, so create our own copy
+ // The source still might need it in the future and we do not want to break it
+ this.AddSyntaxError(error.Clone(this));
+ }
+ }
+ this.Document.Parser.TrackedSegments.AddSyntaxErrorsOf(this);
+ OnChanged();
+ }
+
+ return true;
+ }
+
+ /// <summary> Verify that the item is consistent. Only in debug build. </summary>
+ [Conditional("DEBUG")]
+ internal virtual void DebugCheckConsistency(bool allowNullParent)
+ {
+
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0}({1}-{2})", this.GetType().Name.Remove(0, 4), this.StartOffset, this.EndOffset);
+ }
+
+ #region Helpper methods
+
+ /// <summary> The part of name before ":" </summary>
+ /// <returns> Empty string if not found </returns>
+ protected static string GetNamespacePrefix(string name)
+ {
+ if (string.IsNullOrEmpty(name)) return string.Empty;
+ int colonIndex = name.IndexOf(':');
+ if (colonIndex != -1) {
+ return name.Substring(0, colonIndex);
+ } else {
+ return string.Empty;
+ }
+ }
+
+ /// <summary> The part of name after ":" </summary>
+ /// <returns> Whole name if ":" not found </returns>
+ protected static string GetLocalName(string name)
+ {
+ if (string.IsNullOrEmpty(name)) return string.Empty;
+ int colonIndex = name.IndexOf(':');
+ if (colonIndex != -1) {
+ return name.Remove(0, colonIndex + 1);
+ } else {
+ return name ?? string.Empty;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectCollection.cs
new file mode 100644
index 000000000..3a2c7cc21
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectCollection.cs
@@ -0,0 +1,90 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Collection that is publicly read-only and has support
+ /// for adding/removing multiple items at a time.
+ /// </summary>
+ public class AXmlObjectCollection<T>: Collection<T>, INotifyCollectionChanged
+ {
+ /// <summary> Occurs when the collection is changed </summary>
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ /// <summary> Raises <see cref="CollectionChanged"/> event </summary>
+ // Do not inherit - it is not called if event is null
+ void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ if (CollectionChanged != null) {
+ CollectionChanged(this, e);
+ }
+ }
+
+ /// <inheritdoc/>
+ protected override void ClearItems()
+ {
+ throw new NotSupportedException();
+ }
+
+ /// <inheritdoc/>
+ protected override void InsertItem(int index, T item)
+ {
+ throw new NotSupportedException();
+ }
+
+ /// <inheritdoc/>
+ protected override void RemoveItem(int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ /// <inheritdoc/>
+ protected override void SetItem(int index, T item)
+ {
+ throw new NotSupportedException();
+ }
+
+ internal void InsertItemAt(int index, T item)
+ {
+ base.InsertItem(index, item);
+ if (CollectionChanged != null)
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new T[] { item }.ToList(), index));
+ }
+
+ internal void RemoveItemAt(int index)
+ {
+ T removed = this[index];
+ base.RemoveItem(index);
+ if (CollectionChanged != null)
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new T[] { removed }.ToList(), index));
+ }
+
+ internal void InsertItemsAt(int index, IList<T> items)
+ {
+ for(int i = 0; i < items.Count; i++) {
+ base.InsertItem(index + i, items[i]);
+ }
+ if (CollectionChanged != null)
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (IList)items, index));
+ }
+
+ internal void RemoveItemsAt(int index, int count)
+ {
+ List<T> removed = new List<T>();
+ for(int i = 0; i < count; i++) {
+ removed.Add(this[index]);
+ base.RemoveItem(index);
+ }
+ if (CollectionChanged != null)
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (IList)removed, index));
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectEventArgs.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectEventArgs.cs
new file mode 100644
index 000000000..27ea041e6
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlObjectEventArgs.cs
@@ -0,0 +1,21 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary> Holds event args for event caused by <see cref="AXmlObject"/> </summary>
+ public class AXmlObjectEventArgs: EventArgs
+ {
+ /// <summary> The object that caused the event </summary>
+ public AXmlObject Object { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlParser.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlParser.cs
new file mode 100644
index 000000000..625ff59d9
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlParser.cs
@@ -0,0 +1,201 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Creates object tree from XML document.
+ /// </summary>
+ /// <remarks>
+ /// The created tree fully describes the document and thus the orginal XML file can be
+ /// exactly reproduced.
+ ///
+ /// Any further parses will reparse only the changed parts and the existing tree will
+ /// be updated with the changes. The user can add event handlers to be notified of
+ /// the changes. The parser tries to minimize the number of changes to the tree.
+ /// (for example, it will add a single child at the start of collection rather than
+ /// clearing the collection and adding new children)
+ ///
+ /// The object tree consists of following types:
+ /// RawObject - Abstact base class for all types
+ /// RawContainer - Abstact base class for all types that can contain child nodes
+ /// RawDocument - The root object of the XML document
+ /// RawElement - Logical grouping of other nodes together. The first child is always the start tag.
+ /// RawTag - Represents any markup starting with "&lt;" and (hopefully) ending with ">"
+ /// RawAttribute - Name-value pair in a tag
+ /// RawText - Whitespace or character data
+ ///
+ /// For example, see the following XML and the produced object tree:
+ /// <![CDATA[
+ /// <!-- My favourite quote -->
+ /// <quote author="Albert Einstein">
+ /// Make everything as simple as possible, but not simpler.
+ /// </quote>
+ ///
+ /// RawDocument
+ /// RawTag "<!--" "-->"
+ /// RawText " My favourite quote "
+ /// RawElement
+ /// RawTag "<" "quote" ">"
+ /// RawText " "
+ /// RawAttribute 'author="Albert Einstein"'
+ /// RawText "\n Make everything as simple as possible, but not simpler.\n"
+ /// RawTag "</" "quote" ">"
+ /// ]]>
+ ///
+ /// The precise content of RawTag depends on what it represents:
+ /// <![CDATA[
+ /// Start tag: "<" Name? (RawText+ RawAttribute)* RawText* (">" | "/>")
+ /// End tag: "</" Name? (RawText+ RawAttribute)* RawText* ">"
+ /// P.instr.: "<?" Name? (RawText)* "?>"
+ /// Comment: "<!--" (RawText)* "-->"
+ /// CData: "<![CDATA[" (RawText)* "]]" ">"
+ /// DTD: "<!DOCTYPE" (RawText+ RawTag)* RawText* ">" (DOCTYPE or other DTD names)
+ /// UknownBang: "<!" (RawText)* ">"
+ /// ]]>
+ ///
+ /// The type of tag can be identified by the opening backet.
+ /// There are helpper properties in the RawTag class to identify the type, exactly
+ /// one of the properties will be true.
+ ///
+ /// The closing bracket may be missing or may be different for mallformed XML.
+ ///
+ /// Note that there can always be multiple consequtive RawText nodes.
+ /// This is to ensure that idividual texts are not too long.
+ ///
+ /// XML Spec: http://www.w3.org/TR/xml/
+ /// XML EBNF: http://www.jelks.nu/XML/xmlebnf.html
+ ///
+ /// Internals:
+ ///
+ /// "Try" methods can silently fail by returning false.
+ /// MoveTo methods do not move if they are already at the given target
+ /// If methods return some object, it must be no-empty. It is up to the caller to ensure
+ /// the context is appropriate for reading.
+ ///
+ /// </remarks>
+ public class AXmlParser
+ {
+ AXmlDocument userDocument;
+
+ internal TrackedSegmentCollection TrackedSegments { get; private set; }
+
+ /// <summary>
+ /// Generate syntax error when seeing enity reference other then the build-in ones
+ /// </summary>
+ public bool UnknownEntityReferenceIsError { get; set; }
+
+ /// <summary> Create new parser </summary>
+ public AXmlParser()
+ {
+ this.Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
+ ClearInternal();
+ }
+
+ /// <summary> Throws exception if condition is false </summary>
+ internal static void Assert(bool condition, string message)
+ {
+ if (!condition) {
+ throw new InternalException("Assertion failed: " + message);
+ }
+ }
+
+ /// <summary> Throws exception if condition is false </summary>
+ [Conditional("DEBUG")]
+ internal static void DebugAssert(bool condition, string message)
+ {
+ if (!condition) {
+ throw new InternalException("Assertion failed: " + message);
+ }
+ }
+
+ [Conditional("DEBUG")]
+ internal static void Log(string text, params object[] pars)
+ {
+ //System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "XML: " + text, pars));
+ }
+
+ /// <summary>
+ /// Incrementaly parse the given text.
+ /// You have to hold the write lock.
+ /// </summary>
+ /// <param name="input">
+ /// The full XML text of the new document.
+ /// </param>
+ /// <param name="changesSinceLastParse">
+ /// Changes since last parse. Null will cause full reparse.
+ /// </param>
+ public AXmlDocument Parse(string input, IEnumerable<DocumentChangeEventArgs> changesSinceLastParse)
+ {
+ if (!Lock.IsWriteLockHeld)
+ throw new InvalidOperationException("Lock needed!");
+
+ // Use changes to invalidate cache
+ if (changesSinceLastParse != null) {
+ this.TrackedSegments.UpdateOffsetsAndInvalidate(changesSinceLastParse);
+ } else {
+ this.TrackedSegments.InvalidateAll();
+ }
+
+ TagReader tagReader = new TagReader(this, input);
+ List<AXmlObject> tags = tagReader.ReadAllTags();
+ AXmlDocument parsedDocument = new TagMatchingHeuristics(this, input, tags).ReadDocument();
+ tagReader.PrintStringCacheStats();
+ AXmlParser.Log("Updating main DOM tree...");
+ userDocument.UpdateTreeFrom(parsedDocument);
+ userDocument.DebugCheckConsistency(true);
+ Assert(userDocument.GetSelfAndAllChildren().Count() == parsedDocument.GetSelfAndAllChildren().Count(), "Parsed document and updated document have different number of children");
+ return userDocument;
+ }
+
+ /// <summary>
+ /// Makes calls to Parse() thread-safe. Use Lock everywhere Parse() is called.
+ /// </summary>
+ public ReaderWriterLockSlim Lock { get; private set; }
+
+ /// <summary>
+ /// Returns the last cached version of the document.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">No read lock is held by the current thread.</exception>
+ public AXmlDocument LastDocument {
+ get {
+ if (!Lock.IsReadLockHeld)
+ throw new InvalidOperationException("Read lock needed!");
+
+ return userDocument;
+ }
+ }
+
+ /// <summary>
+ /// Clears the parser data.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">No write lock is held by the current thread.</exception>
+ public void Clear()
+ {
+ if (!Lock.IsWriteLockHeld)
+ throw new InvalidOperationException("Write lock needed!");
+
+ ClearInternal();
+ }
+
+ void ClearInternal()
+ {
+ this.UnknownEntityReferenceIsError = true;
+ this.TrackedSegments = new TrackedSegmentCollection();
+ this.userDocument = new AXmlDocument() { Parser = this };
+ this.userDocument.Document = this.userDocument;
+ // Track the document
+ this.TrackedSegments.AddParsedObject(this.userDocument, null);
+ this.userDocument.IsCached = false;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlTag.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlTag.cs
new file mode 100644
index 000000000..8283cea22
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlTag.cs
@@ -0,0 +1,108 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Represents any markup starting with "&lt;" and (hopefully) ending with ">"
+ /// </summary>
+ public class AXmlTag: AXmlContainer
+ {
+ /// <summary> These identify the start of DTD elements </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification="ReadOnlyCollection is immutable")]
+ public static readonly ReadOnlyCollection<string> DtdNames = new ReadOnlyCollection<string>(
+ new string[] {"<!DOCTYPE", "<!NOTATION", "<!ELEMENT", "<!ATTLIST", "<!ENTITY" } );
+
+ /// <summary> Opening bracket - usually "&lt;" </summary>
+ public string OpeningBracket { get; internal set; }
+ /// <summary> Name following the opening bracket </summary>
+ public string Name { get; internal set; }
+ /// <summary> Opening bracket - usually "&gt;" </summary>
+ public string ClosingBracket { get; internal set; }
+
+ /// <summary> True if tag starts with "&lt;" </summary>
+ public bool IsStartOrEmptyTag { get { return OpeningBracket == "<"; } }
+ /// <summary> True if tag starts with "&lt;" and ends with "&gt;" </summary>
+ public bool IsStartTag { get { return OpeningBracket == "<" && ClosingBracket == ">"; } }
+ /// <summary> True if tag starts with "&lt;" and does not end with "&gt;" </summary>
+ public bool IsEmptyTag { get { return OpeningBracket == "<" && ClosingBracket != ">" ; } }
+ /// <summary> True if tag starts with "&lt;/" </summary>
+ public bool IsEndTag { get { return OpeningBracket == "</"; } }
+ /// <summary> True if tag starts with "&lt;?" </summary>
+ public bool IsProcessingInstruction { get { return OpeningBracket == "<?"; } }
+ /// <summary> True if tag starts with "&lt;!--" </summary>
+ public bool IsComment { get { return OpeningBracket == "<!--"; } }
+ /// <summary> True if tag starts with "&lt;![CDATA[" </summary>
+ public bool IsCData { get { return OpeningBracket == "<![CDATA["; } }
+ /// <summary> True if tag starts with one of the DTD starts </summary>
+ public bool IsDocumentType { get { return DtdNames.Contains(OpeningBracket); } }
+ /// <summary> True if tag starts with "&lt;!" </summary>
+ public bool IsUnknownBang { get { return OpeningBracket == "<!"; } }
+
+ #region Helpper methods
+
+ AXmlAttributeCollection attributes;
+
+ /// <summary> Gets attributes of the tag (if applicable) </summary>
+ public AXmlAttributeCollection Attributes {
+ get {
+ if (attributes == null) {
+ attributes = new AXmlAttributeCollection(this.Children);
+ }
+ return attributes;
+ }
+ }
+
+ #endregion
+
+ internal override void DebugCheckConsistency(bool checkParentPointers)
+ {
+ Assert(OpeningBracket != null, "Null OpeningBracket");
+ Assert(Name != null, "Null Name");
+ Assert(ClosingBracket != null, "Null ClosingBracket");
+ base.DebugCheckConsistency(checkParentPointers);
+ }
+
+ /// <inheritdoc/>
+ public override void AcceptVisitor(IAXmlVisitor visitor)
+ {
+ visitor.VisitTag(this);
+ }
+
+ /// <inheritdoc/>
+ internal override bool UpdateDataFrom(AXmlObject source)
+ {
+ if (!base.UpdateDataFrom(source)) return false;
+ AXmlTag src = (AXmlTag)source;
+ if (this.OpeningBracket != src.OpeningBracket ||
+ this.Name != src.Name ||
+ this.ClosingBracket != src.ClosingBracket)
+ {
+ OnChanging();
+ this.OpeningBracket = src.OpeningBracket;
+ this.Name = src.Name;
+ this.ClosingBracket = src.ClosingBracket;
+ OnChanged();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[{0} '{1}{2}{3}' Attr:{4}]", base.ToString(), this.OpeningBracket, this.Name, this.ClosingBracket, this.Children.Count);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlText.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlText.cs
new file mode 100644
index 000000000..8470979ce
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AXmlText.cs
@@ -0,0 +1,62 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Whitespace or character data
+ /// </summary>
+ public class AXmlText: AXmlObject
+ {
+ /// <summary> The context in which the text occured </summary>
+ internal TextType Type { get; set; }
+ /// <summary> The text exactly as in source </summary>
+ public string EscapedValue { get; set; }
+ /// <summary> The text with all entity references resloved </summary>
+ public string Value { get; set; }
+ /// <summary> True if the text contains only whitespace characters </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace",
+ Justification = "System.Xml also uses 'Whitespace'")]
+ public bool ContainsOnlyWhitespace { get; set; }
+
+ /// <inheritdoc/>
+ public override void AcceptVisitor(IAXmlVisitor visitor)
+ {
+ visitor.VisitText(this);
+ }
+
+ /// <inheritdoc/>
+ internal override bool UpdateDataFrom(AXmlObject source)
+ {
+ if (!base.UpdateDataFrom(source)) return false;
+ AXmlText src = (AXmlText)source;
+ if (this.EscapedValue != src.EscapedValue ||
+ this.Value != src.Value)
+ {
+ OnChanging();
+ this.EscapedValue = src.EscapedValue;
+ this.Value = src.Value;
+ OnChanged();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /// <inheritdoc/>
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[{0} Text.Length={1}]", base.ToString(), this.EscapedValue.Length);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AbstractAXmlVisitor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AbstractAXmlVisitor.cs
new file mode 100644
index 000000000..1c8e72bbb
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/AbstractAXmlVisitor.cs
@@ -0,0 +1,44 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Derive from this class to create visitor for the XML tree
+ /// </summary>
+ public abstract class AbstractAXmlVisitor : IAXmlVisitor
+ {
+ /// <summary> Visit RawDocument </summary>
+ public virtual void VisitDocument(AXmlDocument document)
+ {
+ foreach(AXmlObject child in document.Children) child.AcceptVisitor(this);
+ }
+
+ /// <summary> Visit RawElement </summary>
+ public virtual void VisitElement(AXmlElement element)
+ {
+ foreach(AXmlObject child in element.Children) child.AcceptVisitor(this);
+ }
+
+ /// <summary> Visit RawTag </summary>
+ public virtual void VisitTag(AXmlTag tag)
+ {
+ foreach(AXmlObject child in tag.Children) child.AcceptVisitor(this);
+ }
+
+ /// <summary> Visit RawAttribute </summary>
+ public virtual void VisitAttribute(AXmlAttribute attribute)
+ {
+
+ }
+
+ /// <summary> Visit RawText </summary>
+ public virtual void VisitText(AXmlText text)
+ {
+
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/CanonicalPrintAXmlVisitor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/CanonicalPrintAXmlVisitor.cs
new file mode 100644
index 000000000..df2ac5e81
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/CanonicalPrintAXmlVisitor.cs
@@ -0,0 +1,119 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Linq;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Converts the XML tree back to text in canonical form.
+ /// See http://www.w3.org/TR/xml-c14n
+ /// </summary>
+ public class CanonicalPrintAXmlVisitor: AbstractAXmlVisitor
+ {
+ StringBuilder sb = new StringBuilder();
+
+ /// <summary>
+ /// Gets the pretty printed text
+ /// </summary>
+ public string Output {
+ get {
+ return sb.ToString();
+ }
+ }
+
+ /// <summary> Create canonical text from a document </summary>
+ public static string Print(AXmlDocument doc)
+ {
+ CanonicalPrintAXmlVisitor visitor = new CanonicalPrintAXmlVisitor();
+ visitor.VisitDocument(doc);
+ return visitor.Output;
+ }
+
+ /// <summary> Visit RawDocument </summary>
+ public override void VisitDocument(AXmlDocument document)
+ {
+ foreach(AXmlObject child in document.Children) {
+ AXmlTag childAsTag = child as AXmlTag;
+ // Only procssing instructions or elements
+ if (childAsTag != null && childAsTag.IsProcessingInstruction && childAsTag.Name != "xml") {
+ VisitTag(childAsTag);
+ } else {
+ AXmlElement childAsElement = child as AXmlElement;
+ if (childAsElement != null) {
+ VisitElement(childAsElement);
+ }
+ }
+ }
+ }
+
+ /// <summary> Visit RawElement </summary>
+ public override void VisitElement(AXmlElement element)
+ {
+ base.VisitElement(element);
+ }
+
+ /// <summary> Visit RawTag </summary>
+ public override void VisitTag(AXmlTag tag)
+ {
+ if (tag.IsStartOrEmptyTag) {
+ sb.Append('<');
+ sb.Append(tag.Name);
+ foreach(AXmlAttribute attr in tag.Children.OfType<AXmlAttribute>().OrderBy(a => a.Name)) {
+ VisitAttribute(attr);
+ }
+ sb.Append('>');
+ if (tag.IsEmptyTag) {
+ // Use explicit start-end pair
+ sb.AppendFormat("</{0}>", tag.Name);
+ }
+ } else if (tag.IsEndTag) {
+ sb.AppendFormat("</{0}>", tag.Name);
+ } else if (tag.IsProcessingInstruction) {
+ sb.Append("<?");
+ sb.Append(tag.Name);
+ foreach(AXmlText text in tag.Children.OfType<AXmlText>()) {
+ sb.Append(text.Value);
+ }
+ if (tag.Children.Count == 0)
+ sb.Append(' ');
+ sb.Append("?>");
+ } else if (tag.IsCData) {
+ foreach(AXmlText text in tag.Children.OfType<AXmlText>()) {
+ sb.Append(Escape(text.Value));
+ }
+ }
+ }
+
+ /// <summary> Visit RawAttribute </summary>
+ public override void VisitAttribute(AXmlAttribute attribute)
+ {
+ sb.Append(' ');
+ sb.Append(attribute.Name);
+ sb.Append("=");
+ sb.Append('"');
+ sb.Append(Escape(attribute.Value));
+ sb.Append('"');
+ }
+
+ /// <summary> Visit RawText </summary>
+ public override void VisitText(AXmlText text)
+ {
+ sb.Append(Escape(text.Value));
+ }
+
+ static string Escape(string text)
+ {
+ return text
+ .Replace("&", "&amp;")
+ .Replace("<", "&lt;")
+ .Replace(">", "&gt;")
+ .Replace("\"", "&quot;")
+ .Replace("\u0009", "&#9;")
+ .Replace("\u000A", "&#10;")
+ .Replace("\u000D", "&#13;");
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/ExtensionMethods.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/ExtensionMethods.cs
new file mode 100644
index 000000000..34664a23a
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/ExtensionMethods.cs
@@ -0,0 +1,47 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ static class ExtensionMethods
+ {
+ // Copied from ICSharpCode.SharpDevelop.Dom.ExtensionMethods
+ /// <summary>
+ /// Converts a recursive data structure into a flat list.
+ /// </summary>
+ /// <param name="input">The root elements of the recursive data structure.</param>
+ /// <param name="recursion">The function that gets the children of an element.</param>
+ /// <returns>Iterator that enumerates the tree structure in preorder.</returns>
+ public static IEnumerable<T> Flatten<T>(this IEnumerable<T> input, Func<T, IEnumerable<T>> recursion)
+ {
+ Stack<IEnumerator<T>> stack = new Stack<IEnumerator<T>>();
+ try {
+ stack.Push(input.GetEnumerator());
+ while (stack.Count > 0) {
+ while (stack.Peek().MoveNext()) {
+ T element = stack.Peek().Current;
+ yield return element;
+ IEnumerable<T> children = recursion(element);
+ if (children != null) {
+ stack.Push(children.GetEnumerator());
+ }
+ }
+ stack.Pop().Dispose();
+ }
+ } finally {
+ while (stack.Count > 0) {
+ stack.Pop().Dispose();
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/FilteredCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/FilteredCollection.cs
new file mode 100644
index 000000000..fd936d1b0
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/FilteredCollection.cs
@@ -0,0 +1,99 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Collection that presents only some items from the wrapped collection.
+ /// It implicitely filters object that are not of type T (or derived).
+ /// </summary>
+ public class FilteredCollection<T, TCollection>: ObservableCollection<T> where TCollection : INotifyCollectionChanged, IList
+ {
+ TCollection source;
+ Predicate<object> condition;
+ List<int> srcPtrs = new List<int>(); // Index to the original collection
+
+ /// <summary> Create unbound collection </summary>
+ protected FilteredCollection() {}
+
+ /// <summary> Wrap the given collection. Items of type other then T are filtered </summary>
+ public FilteredCollection(TCollection source) : this (source, x => true) { }
+
+ /// <summary> Wrap the given collection. Items of type other then T are filtered. Items not matching the condition are filtered. </summary>
+ public FilteredCollection(TCollection source, Predicate<object> condition)
+ {
+ this.source = source;
+ this.condition = condition;
+
+ this.source.CollectionChanged += SourceCollectionChanged;
+
+ Reset();
+ }
+
+ void Reset()
+ {
+ this.Clear();
+ srcPtrs.Clear();
+ for(int i = 0; i < source.Count; i++) {
+ if (source[i] is T && condition(source[i])) {
+ this.Add((T)source[i]);
+ srcPtrs.Add(i);
+ }
+ }
+ }
+
+ void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ switch(e.Action) {
+ case NotifyCollectionChangedAction.Add:
+ // Update pointers
+ for(int i = 0; i < srcPtrs.Count; i++) {
+ if (srcPtrs[i] >= e.NewStartingIndex) {
+ srcPtrs[i] += e.NewItems.Count;
+ }
+ }
+ // Find where to add items
+ int addIndex = srcPtrs.FindIndex(srcPtr => srcPtr >= e.NewStartingIndex);
+ if (addIndex == -1) addIndex = this.Count;
+ // Add items to collection
+ for(int i = 0; i < e.NewItems.Count; i++) {
+ if (e.NewItems[i] is T && condition(e.NewItems[i])) {
+ this.InsertItem(addIndex, (T)e.NewItems[i]);
+ srcPtrs.Insert(addIndex, e.NewStartingIndex + i);
+ addIndex++;
+ }
+ }
+ break;
+ case NotifyCollectionChangedAction.Remove:
+ // Remove the item from our collection
+ for(int i = 0; i < e.OldItems.Count; i++) {
+ // Anyone points to the removed item?
+ int removeIndex = srcPtrs.IndexOf(e.OldStartingIndex + i);
+ // Remove
+ if (removeIndex != -1) {
+ this.RemoveAt(removeIndex);
+ srcPtrs.RemoveAt(removeIndex);
+ }
+ }
+ // Update pointers
+ for(int i = 0; i < srcPtrs.Count; i++) {
+ if (srcPtrs[i] >= e.OldStartingIndex) {
+ srcPtrs[i] -= e.OldItems.Count;
+ }
+ }
+ break;
+ case NotifyCollectionChangedAction.Reset:
+ Reset();
+ break;
+ default:
+ throw new NotSupportedException(e.Action.ToString());
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/IAXmlVisitor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/IAXmlVisitor.cs
new file mode 100644
index 000000000..cbd4b2549
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/IAXmlVisitor.cs
@@ -0,0 +1,29 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Visitor for the XML tree
+ /// </summary>
+ public interface IAXmlVisitor
+ {
+ /// <summary> Visit RawDocument </summary>
+ void VisitDocument(AXmlDocument document);
+
+ /// <summary> Visit RawElement </summary>
+ void VisitElement(AXmlElement element);
+
+ /// <summary> Visit RawTag </summary>
+ void VisitTag(AXmlTag tag);
+
+ /// <summary> Visit RawAttribute </summary>
+ void VisitAttribute(AXmlAttribute attribute);
+
+ /// <summary> Visit RawText </summary>
+ void VisitText(AXmlText text);
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/InternalException.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/InternalException.cs
new file mode 100644
index 000000000..a52ac4b4f
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/InternalException.cs
@@ -0,0 +1,45 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Exception used for internal errors in XML parser.
+ /// This exception indicates a bug in AvalonEdit.
+ /// </summary>
+ [Serializable()]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception is not public because it is not supposed to be caught by user code - it indicates a bug in AvalonEdit.")]
+ class InternalException : Exception
+ {
+ /// <summary>
+ /// Creates a new InternalException instance.
+ /// </summary>
+ public InternalException() : base()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new InternalException instance.
+ /// </summary>
+ public InternalException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new InternalException instance.
+ /// </summary>
+ public InternalException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Creates a new InternalException instance.
+ /// </summary>
+ protected InternalException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/MergedCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/MergedCollection.cs
new file mode 100644
index 000000000..79574053b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/MergedCollection.cs
@@ -0,0 +1,70 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Two collections in sequence
+ /// </summary>
+ public class MergedCollection<T, TCollection> : ObservableCollection<T> where TCollection : INotifyCollectionChanged, IList<T>
+ {
+ TCollection a;
+ TCollection b;
+
+ /// <summary> Create a wrapper containing elements of 'a' and then 'b' </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
+ public MergedCollection(TCollection a, TCollection b)
+ {
+ this.a = a;
+ this.b = b;
+
+ this.a.CollectionChanged += SourceCollectionAChanged;
+ this.b.CollectionChanged += SourceCollectionBChanged;
+
+ Reset();
+ }
+
+ void Reset()
+ {
+ this.Clear();
+ foreach(T item in a) this.Add(item);
+ foreach(T item in b) this.Add(item);
+ }
+
+ void SourceCollectionAChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ SourceCollectionChanged(0, e);
+ }
+
+ void SourceCollectionBChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ SourceCollectionChanged(a.Count, e);
+ }
+
+ void SourceCollectionChanged(int collectionStart, NotifyCollectionChangedEventArgs e)
+ {
+ switch(e.Action) {
+ case NotifyCollectionChangedAction.Add:
+ for (int i = 0; i < e.NewItems.Count; i++) {
+ this.InsertItem(collectionStart + e.NewStartingIndex + i, (T)e.NewItems[i]);
+ }
+ break;
+ case NotifyCollectionChangedAction.Remove:
+ for (int i = 0; i < e.OldItems.Count; i++) {
+ this.RemoveAt(collectionStart + e.OldStartingIndex);
+ }
+ break;
+ case NotifyCollectionChangedAction.Reset:
+ Reset();
+ break;
+ default:
+ throw new NotSupportedException(e.Action.ToString());
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/PrettyPrintAXmlVisitor.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/PrettyPrintAXmlVisitor.cs
new file mode 100644
index 000000000..cb393c552
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/PrettyPrintAXmlVisitor.cs
@@ -0,0 +1,69 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Converts the XML tree back to text.
+ /// The text should exactly match the original.
+ /// </summary>
+ public class PrettyPrintAXmlVisitor: AbstractAXmlVisitor
+ {
+ StringBuilder sb = new StringBuilder();
+
+ /// <summary>
+ /// Gets the pretty printed text
+ /// </summary>
+ public string Output {
+ get {
+ return sb.ToString();
+ }
+ }
+
+ /// <summary> Create XML text from a document </summary>
+ public static string PrettyPrint(AXmlDocument doc)
+ {
+ PrettyPrintAXmlVisitor visitor = new PrettyPrintAXmlVisitor();
+ visitor.VisitDocument(doc);
+ return visitor.Output;
+ }
+
+ /// <summary> Visit RawDocument </summary>
+ public override void VisitDocument(AXmlDocument document)
+ {
+ base.VisitDocument(document);
+ }
+
+ /// <summary> Visit RawElement </summary>
+ public override void VisitElement(AXmlElement element)
+ {
+ base.VisitElement(element);
+ }
+
+ /// <summary> Visit RawTag </summary>
+ public override void VisitTag(AXmlTag tag)
+ {
+ sb.Append(tag.OpeningBracket);
+ sb.Append(tag.Name);
+ base.VisitTag(tag);
+ sb.Append(tag.ClosingBracket);
+ }
+
+ /// <summary> Visit RawAttribute </summary>
+ public override void VisitAttribute(AXmlAttribute attribute)
+ {
+ sb.Append(attribute.Name);
+ sb.Append(attribute.EqualsSign);
+ sb.Append(attribute.QuotedValue);
+ }
+
+ /// <summary> Visit RawText </summary>
+ public override void VisitText(AXmlText text)
+ {
+ sb.Append(text.EscapedValue);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/SyntaxError.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/SyntaxError.cs
new file mode 100644
index 000000000..4c2e5d636
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/SyntaxError.cs
@@ -0,0 +1,36 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary> Information about syntax error that occured during parsing </summary>
+ public class SyntaxError: TextSegment
+ {
+ /// <summary> Object for which the error occured </summary>
+ public AXmlObject Object { get; internal set; }
+ /// <summary> Textual description of the error </summary>
+ public string Message { get; internal set; }
+ /// <summary> Any user data </summary>
+ public object Tag { get; set; }
+
+ internal SyntaxError Clone(AXmlObject newOwner)
+ {
+ return new SyntaxError {
+ Object = newOwner,
+ Message = Message,
+ Tag = Tag,
+ StartOffset = StartOffset,
+ EndOffset = EndOffset,
+ };
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagMatchingHeuristics.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagMatchingHeuristics.cs
new file mode 100644
index 000000000..52c692cd8
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagMatchingHeuristics.cs
@@ -0,0 +1,439 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Tango.Scripting.Editors.Utils;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ class TagMatchingHeuristics
+ {
+ const int maxConfigurationCount = 10;
+
+ AXmlParser parser;
+ TrackedSegmentCollection trackedSegments;
+ string input;
+ List<AXmlObject> tags;
+
+ public TagMatchingHeuristics(AXmlParser parser, string input, List<AXmlObject> tags)
+ {
+ this.parser = parser;
+ this.trackedSegments = parser.TrackedSegments;
+ this.input = input;
+ this.tags = tags;
+ }
+
+ public AXmlDocument ReadDocument()
+ {
+ AXmlDocument doc = new AXmlDocument() { Parser = parser };
+
+ // AXmlParser.Log("Flat stream: {0}", PrintObjects(tags));
+ List<AXmlObject> valid = MatchTags(tags);
+ // AXmlParser.Log("Fixed stream: {0}", PrintObjects(valid));
+ IEnumerator<AXmlObject> validStream = valid.GetEnumerator();
+ validStream.MoveNext(); // Move to first
+ while(true) {
+ // End of stream?
+ try {
+ if (validStream.Current == null) break;
+ } catch (InvalidCastException) {
+ break;
+ }
+ doc.AddChild(ReadTextOrElement(validStream));
+ }
+
+ if (doc.Children.Count > 0) {
+ doc.StartOffset = doc.FirstChild.StartOffset;
+ doc.EndOffset = doc.LastChild.EndOffset;
+ }
+
+ // Check well formed
+ foreach(AXmlTag xmlDeclaration in doc.Children.OfType<AXmlTag>().Where(t => t.IsProcessingInstruction && string.Equals(t.Name, "xml", StringComparison.OrdinalIgnoreCase))) {
+ if (xmlDeclaration.StartOffset != 0)
+ TagReader.OnSyntaxError(doc, xmlDeclaration.StartOffset, xmlDeclaration.StartOffset + 5,
+ "XML declaration must be at the start of document");
+ }
+ int elemCount = doc.Children.OfType<AXmlElement>().Count();
+ if (elemCount == 0)
+ TagReader.OnSyntaxError(doc, doc.EndOffset, doc.EndOffset,
+ "Root element is missing");
+ if (elemCount > 1) {
+ AXmlElement next = doc.Children.OfType<AXmlElement>().Skip(1).First();
+ TagReader.OnSyntaxError(doc, next.StartOffset, next.StartOffset,
+ "Only one root element is allowed");
+ }
+ foreach(AXmlTag tag in doc.Children.OfType<AXmlTag>()) {
+ if (tag.IsCData)
+ TagReader.OnSyntaxError(doc, tag.StartOffset, tag.EndOffset,
+ "CDATA not allowed in document root");
+ }
+ foreach(AXmlText text in doc.Children.OfType<AXmlText>()) {
+ if (!text.ContainsOnlyWhitespace)
+ TagReader.OnSyntaxError(doc, text.StartOffset, text.EndOffset,
+ "Only whitespace is allowed in document root");
+ }
+
+
+ AXmlParser.Log("Constructed {0}", doc);
+ trackedSegments.AddParsedObject(doc, null);
+ return doc;
+ }
+
+ static AXmlObject ReadSingleObject(IEnumerator<AXmlObject> objStream)
+ {
+ AXmlObject obj = objStream.Current;
+ objStream.MoveNext();
+ return obj;
+ }
+
+ AXmlObject ReadTextOrElement(IEnumerator<AXmlObject> objStream)
+ {
+ AXmlObject curr = objStream.Current;
+ if (curr is AXmlText || curr is AXmlElement) {
+ return ReadSingleObject(objStream);
+ } else {
+ AXmlTag currTag = (AXmlTag)curr;
+ if (currTag == StartTagPlaceholder) {
+ return ReadElement(objStream);
+ } else if (currTag.IsStartOrEmptyTag) {
+ return ReadElement(objStream);
+ } else {
+ return ReadSingleObject(objStream);
+ }
+ }
+ }
+
+ AXmlElement ReadElement(IEnumerator<AXmlObject> objStream)
+ {
+ AXmlElement element = new AXmlElement();
+ element.IsProperlyNested = true;
+
+ // Read start tag
+ AXmlTag startTag = ReadSingleObject(objStream) as AXmlTag;
+ AXmlParser.DebugAssert(startTag != null, "Start tag expected");
+ AXmlParser.DebugAssert(startTag.IsStartOrEmptyTag || startTag == StartTagPlaceholder, "Start tag expected");
+ if (startTag == StartTagPlaceholder) {
+ element.HasStartOrEmptyTag = false;
+ element.IsProperlyNested = false;
+ TagReader.OnSyntaxError(element, objStream.Current.StartOffset, objStream.Current.EndOffset,
+ "Matching openning tag was not found");
+ } else {
+ element.HasStartOrEmptyTag = true;
+ element.AddChild(startTag);
+ }
+
+ // Read content and end tag
+ if (startTag == StartTagPlaceholder || // Check first in case the start tag is null
+ element.StartTag.IsStartTag)
+ {
+ while(true) {
+ AXmlTag currTag = objStream.Current as AXmlTag; // Peek
+ if (currTag == EndTagPlaceholder) {
+ TagReader.OnSyntaxError(element, element.LastChild.EndOffset, element.LastChild.EndOffset,
+ "Expected '</{0}>'", element.StartTag.Name);
+ ReadSingleObject(objStream);
+ element.HasEndTag = false;
+ element.IsProperlyNested = false;
+ break;
+ } else if (currTag != null && currTag.IsEndTag) {
+ if (element.HasStartOrEmptyTag && currTag.Name != element.StartTag.Name) {
+ TagReader.OnSyntaxError(element, currTag.StartOffset + 2, currTag.StartOffset + 2 + currTag.Name.Length,
+ "Expected '{0}'. End tag must have same name as start tag.", element.StartTag.Name);
+ }
+ element.AddChild(ReadSingleObject(objStream));
+ element.HasEndTag = true;
+ break;
+ }
+ AXmlObject nested = ReadTextOrElement(objStream);
+
+ AXmlElement nestedAsElement = nested as AXmlElement;
+ if (nestedAsElement != null) {
+ if (!nestedAsElement.IsProperlyNested)
+ element.IsProperlyNested = false;
+ element.AddChildren(Split(nestedAsElement).ToList());
+ } else {
+ element.AddChild(nested);
+ }
+ }
+ } else {
+ element.HasEndTag = false;
+ }
+
+ element.StartOffset = element.FirstChild.StartOffset;
+ element.EndOffset = element.LastChild.EndOffset;
+
+ AXmlParser.Assert(element.HasStartOrEmptyTag || element.HasEndTag, "Must have at least start or end tag");
+
+ AXmlParser.Log("Constructed {0}", element);
+ trackedSegments.AddParsedObject(element, null); // Need all elements in cache for offset tracking
+ return element;
+ }
+
+ IEnumerable<AXmlObject> Split(AXmlElement elem)
+ {
+ int myIndention = GetIndentLevel(elem);
+ // Has start tag and no end tag ? (other then empty-element tag)
+ if (elem.HasStartOrEmptyTag && elem.StartTag.IsStartTag && !elem.HasEndTag && myIndention != -1) {
+ int lastAccepted = 0; // Accept start tag
+ while (lastAccepted + 1 < elem.Children.Count) {
+ AXmlObject nextItem = elem.Children[lastAccepted + 1];
+ if (nextItem is AXmlText) {
+ lastAccepted++; continue; // Accept
+ } else {
+ // Include all more indented items
+ if (GetIndentLevel(nextItem) > myIndention) {
+ lastAccepted++; continue; // Accept
+ } else {
+ break; // Reject
+ }
+ }
+ }
+ // Accepted everything?
+ if (lastAccepted + 1 == elem.Children.Count) {
+ yield return elem;
+ yield break;
+ }
+ AXmlParser.Log("Splitting {0} - take {1} of {2} nested", elem, lastAccepted, elem.Children.Count - 1);
+ AXmlElement topHalf = new AXmlElement();
+ topHalf.HasStartOrEmptyTag = elem.HasStartOrEmptyTag;
+ topHalf.HasEndTag = elem.HasEndTag;
+ topHalf.AddChildren(elem.Children.Take(1 + lastAccepted)); // Start tag + nested
+ topHalf.StartOffset = topHalf.FirstChild.StartOffset;
+ topHalf.EndOffset = topHalf.LastChild.EndOffset;
+ TagReader.OnSyntaxError(topHalf, topHalf.LastChild.EndOffset, topHalf.LastChild.EndOffset,
+ "Expected '</{0}>'", topHalf.StartTag.Name);
+
+ AXmlParser.Log("Constructed {0}", topHalf);
+ trackedSegments.AddParsedObject(topHalf, null);
+ yield return topHalf;
+ for(int i = lastAccepted + 1; i < elem.Children.Count; i++) {
+ yield return elem.Children[i];
+ }
+ } else {
+ yield return elem;
+ }
+ }
+
+ int GetIndentLevel(AXmlObject obj)
+ {
+ int offset = obj.StartOffset - 1;
+ int level = 0;
+ while(true) {
+ if (offset < 0) break;
+ char c = input[offset];
+ if (c == ' ') {
+ level++;
+ } else if (c == '\t') {
+ level += 4;
+ } else if (c == '\r' || c == '\n') {
+ break;
+ } else {
+ return -1;
+ }
+ offset--;
+ }
+ return level;
+ }
+
+ /// <summary>
+ /// Stack of still unmatched start tags.
+ /// It includes the cost and backtack information.
+ /// </summary>
+ class Configuration
+ {
+ /// <summary> Unmatched start tags </summary>
+ public ImmutableStack<AXmlTag> StartTags { get; set; }
+ /// <summary> Properly nested tags </summary>
+ public ImmutableStack<AXmlObject> Document { get; set; }
+ /// <summary> Number of needed modificaitons to the document </summary>
+ public int Cost { get; set; }
+ }
+
+ /// <summary>
+ /// Dictionary which stores the cheapest configuration
+ /// </summary>
+ class Configurations: Dictionary<ImmutableStack<AXmlTag>, Configuration>
+ {
+ public Configurations()
+ {
+ }
+
+ public Configurations(IEnumerable<Configuration> configs)
+ {
+ foreach(Configuration config in configs) {
+ this.Add(config);
+ }
+ }
+
+ /// <summary> Overwrite only if cheaper </summary>
+ public void Add(Configuration newConfig)
+ {
+ Configuration oldConfig;
+ if (this.TryGetValue(newConfig.StartTags, out oldConfig)) {
+ if (newConfig.Cost < oldConfig.Cost) {
+ this[newConfig.StartTags] = newConfig;
+ }
+ } else {
+ base.Add(newConfig.StartTags, newConfig);
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach(var kvp in this) {
+ sb.Append("\n - '");
+ foreach(AXmlTag startTag in kvp.Value.StartTags.Reverse()) {
+ sb.Append('<');
+ sb.Append(startTag.Name);
+ sb.Append('>');
+ }
+ sb.AppendFormat("' = {0}", kvp.Value.Cost);
+ }
+ return sb.ToString();
+ }
+ }
+
+ // Tags used to guide the element creation
+ readonly AXmlTag StartTagPlaceholder = new AXmlTag();
+ readonly AXmlTag EndTagPlaceholder = new AXmlTag();
+
+ /// <summary>
+ /// Add start or end tag placeholders so that the documment is properly nested
+ /// </summary>
+ List<AXmlObject> MatchTags(IEnumerable<AXmlObject> objs)
+ {
+ Configurations configurations = new Configurations();
+ configurations.Add(new Configuration {
+ StartTags = ImmutableStack<AXmlTag>.Empty,
+ Document = ImmutableStack<AXmlObject>.Empty,
+ Cost = 0,
+ });
+ foreach(AXmlObject obj in objs) {
+ configurations = ProcessObject(configurations, obj);
+ }
+ // Close any remaining start tags
+ foreach(Configuration conifg in configurations.Values) {
+ while(!conifg.StartTags.IsEmpty) {
+ conifg.StartTags = conifg.StartTags.Pop();
+ conifg.Document = conifg.Document.Push(EndTagPlaceholder);
+ conifg.Cost += 1;
+ }
+ }
+ // AXmlParser.Log("Configurations after closing all remaining tags:" + configurations.ToString());
+ Configuration bestConfig = configurations.Values.OrderBy(v => v.Cost).First();
+ AXmlParser.Log("Best configuration has cost {0}", bestConfig.Cost);
+
+ return bestConfig.Document.Reverse().ToList();
+ }
+
+ /// <summary> Get posible configurations after considering given object </summary>
+ Configurations ProcessObject(Configurations oldConfigs, AXmlObject obj)
+ {
+ AXmlParser.Log("Processing {0}", obj);
+
+ AXmlTag objAsTag = obj as AXmlTag;
+ AXmlElement objAsElement = obj as AXmlElement;
+ AXmlParser.DebugAssert(objAsTag != null || objAsElement != null || obj is AXmlText, obj.GetType().Name + " not expected");
+ if (objAsElement != null)
+ AXmlParser.Assert(objAsElement.IsProperlyNested, "Element not properly nested");
+
+ Configurations newConfigs = new Configurations();
+
+ foreach(var kvp in oldConfigs) {
+ Configuration oldConfig = kvp.Value;
+ var oldStartTags = oldConfig.StartTags;
+ var oldDocument = oldConfig.Document;
+ int oldCost = oldConfig.Cost;
+
+ if (objAsTag != null && objAsTag.IsStartTag) {
+ newConfigs.Add(new Configuration { // Push start-tag (cost 0)
+ StartTags = oldStartTags.Push(objAsTag),
+ Document = oldDocument.Push(objAsTag),
+ Cost = oldCost,
+ });
+ } else if (objAsTag != null && objAsTag.IsEndTag) {
+ newConfigs.Add(new Configuration { // Ignore (cost 1)
+ StartTags = oldStartTags,
+ Document = oldDocument.Push(StartTagPlaceholder).Push(objAsTag),
+ Cost = oldCost + 1,
+ });
+ if (!oldStartTags.IsEmpty && oldStartTags.Peek().Name != objAsTag.Name) {
+ newConfigs.Add(new Configuration { // Pop 1 item (cost 1) - not mathcing
+ StartTags = oldStartTags.Pop(),
+ Document = oldDocument.Push(objAsTag),
+ Cost = oldCost + 1,
+ });
+ }
+ int popedCount = 0;
+ var startTags = oldStartTags;
+ var doc = oldDocument;
+ foreach(AXmlTag poped in oldStartTags) {
+ popedCount++;
+ if (poped.Name == objAsTag.Name) {
+ newConfigs.Add(new Configuration { // Pop 'x' items (cost x-1) - last one is matching
+ StartTags = startTags.Pop(),
+ Document = doc.Push(objAsTag),
+ Cost = oldCost + popedCount - 1,
+ });
+ }
+ startTags = startTags.Pop();
+ doc = doc.Push(EndTagPlaceholder);
+ }
+ } else {
+ // Empty tag or other tag type or text or properly nested element
+ newConfigs.Add(new Configuration { // Ignore (cost 0)
+ StartTags = oldStartTags,
+ Document = oldDocument.Push(obj),
+ Cost = oldCost,
+ });
+ }
+ }
+
+ // Log("New configurations:" + newConfigs.ToString());
+
+ Configurations bestNewConfigurations = new Configurations(
+ newConfigs.Values.OrderBy(v => v.Cost).Take(maxConfigurationCount)
+ );
+
+ // AXmlParser.Log("Best new configurations:" + bestNewConfigurations.ToString());
+
+ return bestNewConfigurations;
+ }
+
+ #region Helper methods
+ /*
+ string PrintObjects(IEnumerable<AXmlObject> objs)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach(AXmlObject obj in objs) {
+ if (obj is AXmlTag) {
+ if (obj == StartTagPlaceholder) {
+ sb.Append("#StartTag#");
+ } else if (obj == EndTagPlaceholder) {
+ sb.Append("#EndTag#");
+ } else {
+ sb.Append(((AXmlTag)obj).OpeningBracket);
+ sb.Append(((AXmlTag)obj).Name);
+ sb.Append(((AXmlTag)obj).ClosingBracket);
+ }
+ } else if (obj is AXmlElement) {
+ sb.Append('[');
+ sb.Append(PrintObjects(((AXmlElement)obj).Children));
+ sb.Append(']');
+ } else if (obj is AXmlText) {
+ sb.Append('~');
+ } else {
+ throw new InternalException("Should not be here: " + obj);
+ }
+ }
+ return sb.ToString();
+ }
+ */
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagReader.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagReader.cs
new file mode 100644
index 000000000..1a1a34f6b
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TagReader.cs
@@ -0,0 +1,740 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ class TagReader: TokenReader
+ {
+ AXmlParser parser;
+ TrackedSegmentCollection trackedSegments;
+ string input;
+
+ public TagReader(AXmlParser parser, string input): base(input)
+ {
+ this.parser = parser;
+ this.trackedSegments = parser.TrackedSegments;
+ this.input = input;
+ }
+
+ bool TryReadFromCacheOrNew<T>(out T res) where T: AXmlObject, new()
+ {
+ return TryReadFromCacheOrNew(out res, t => true);
+ }
+
+ bool TryReadFromCacheOrNew<T>(out T res, Predicate<T> condition) where T: AXmlObject, new()
+ {
+ T cached = trackedSegments.GetCachedObject<T>(this.CurrentLocation, 0, condition);
+ if (cached != null) {
+ Skip(cached.Length);
+ AXmlParser.Assert(cached.Length > 0, "cached elements must not have zero length");
+ res = cached;
+ return true;
+ } else {
+ res = new T();
+ return false;
+ }
+ }
+
+ void OnParsed(AXmlObject obj)
+ {
+ AXmlParser.Log("Parsed {0}", obj);
+ trackedSegments.AddParsedObject(obj, this.MaxTouchedLocation > this.CurrentLocation ? (int?)this.MaxTouchedLocation : null);
+ }
+
+ /// <summary>
+ /// Read all tags in the document in a flat sequence.
+ /// It also includes the text between tags and possibly some properly nested Elements from cache.
+ /// </summary>
+ public List<AXmlObject> ReadAllTags()
+ {
+ List<AXmlObject> stream = new List<AXmlObject>();
+
+ while(true) {
+ if (IsEndOfFile()) {
+ break;
+ } else if (TryPeek('<')) {
+ AXmlElement elem;
+ if (TryReadFromCacheOrNew(out elem, e => e.IsProperlyNested)) {
+ stream.Add(elem);
+ } else {
+ stream.Add(ReadTag());
+ }
+ } else {
+ stream.AddRange(ReadText(TextType.CharacterData));
+ }
+ }
+
+ return stream;
+ }
+
+ /// <summary>
+ /// Context: "&lt;"
+ /// </summary>
+ AXmlTag ReadTag()
+ {
+ AssertHasMoreData();
+
+ AXmlTag tag;
+ if (TryReadFromCacheOrNew(out tag)) return tag;
+
+ tag.StartOffset = this.CurrentLocation;
+
+ // Read the opening bracket
+ // It identifies the type of tag and parsing behavior for the rest of it
+ tag.OpeningBracket = ReadOpeningBracket();
+
+ if (tag.IsUnknownBang && !TryPeekWhiteSpace())
+ OnSyntaxError(tag, tag.StartOffset, this.CurrentLocation, "Unknown tag");
+
+ if (tag.IsStartOrEmptyTag || tag.IsEndTag || tag.IsProcessingInstruction) {
+ // Read the name
+ string name;
+ if (TryReadName(out name)) {
+ if (!IsValidName(name)) {
+ OnSyntaxError(tag, this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
+ }
+ } else {
+ OnSyntaxError(tag, "Element name expected");
+ }
+ tag.Name = name;
+ } else {
+ tag.Name = string.Empty;
+ }
+
+ bool isXmlDeclr = tag.StartOffset == 0 && tag.Name == "xml";
+
+ if (tag.IsStartOrEmptyTag || tag.IsEndTag || isXmlDeclr) {
+ // Read attributes for the tag
+ while(true) {
+ // Chech for all forbiden 'name' charcters first - see ReadName
+ if (IsEndOfFile()) break;
+ if (TryPeekWhiteSpace()) {
+ tag.AddChildren(ReadText(TextType.WhiteSpace));
+ continue; // End of file might be next
+ }
+ if (TryPeek('<')) break;
+ string endBr;
+ int endBrStart = this.CurrentLocation; // Just peek
+ if (TryReadClosingBracket(out endBr)) { // End tag
+ GoBack(endBrStart);
+ break;
+ }
+
+ // We have "=\'\"" or name - read attribute
+ AXmlAttribute attr = ReadAttribulte();
+ tag.AddChild(attr);
+ if (tag.IsEndTag)
+ OnSyntaxError(tag, attr.StartOffset, attr.EndOffset, "Attribute not allowed in end tag.");
+ }
+ } else if (tag.IsDocumentType) {
+ tag.AddChildren(ReadContentOfDTD());
+ } else {
+ int start = this.CurrentLocation;
+ IEnumerable<AXmlObject> text;
+ if (tag.IsComment) {
+ text = ReadText(TextType.Comment);
+ } else if (tag.IsCData) {
+ text = ReadText(TextType.CData);
+ } else if (tag.IsProcessingInstruction) {
+ text = ReadText(TextType.ProcessingInstruction);
+ } else if (tag.IsUnknownBang) {
+ text = ReadText(TextType.UnknownBang);
+ } else {
+ throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
+ }
+ // Enumerate
+ text = text.ToList();
+ // Backtrack at complete start
+ if (IsEndOfFile() || (tag.IsUnknownBang && TryPeek('<'))) {
+ GoBack(start);
+ } else {
+ tag.AddChildren(text);
+ }
+ }
+
+ // Read closing bracket
+ string bracket;
+ TryReadClosingBracket(out bracket);
+ tag.ClosingBracket = bracket;
+
+ // Error check
+ int brStart = this.CurrentLocation - (tag.ClosingBracket ?? string.Empty).Length;
+ int brEnd = this.CurrentLocation;
+ if (tag.Name == null) {
+ // One error was reported already
+ } else if (tag.IsStartOrEmptyTag) {
+ if (tag.ClosingBracket != ">" && tag.ClosingBracket != "/>") OnSyntaxError(tag, brStart, brEnd, "'>' or '/>' expected");
+ } else if (tag.IsEndTag) {
+ if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, brEnd, "'>' expected");
+ } else if (tag.IsComment) {
+ if (tag.ClosingBracket != "-->") OnSyntaxError(tag, brStart, brEnd, "'-->' expected");
+ } else if (tag.IsCData) {
+ if (tag.ClosingBracket != "]]>") OnSyntaxError(tag, brStart, brEnd, "']]>' expected");
+ } else if (tag.IsProcessingInstruction) {
+ if (tag.ClosingBracket != "?>") OnSyntaxError(tag, brStart, brEnd, "'?>' expected");
+ } else if (tag.IsUnknownBang) {
+ if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, brEnd, "'>' expected");
+ } else if (tag.IsDocumentType) {
+ if (tag.ClosingBracket != ">") OnSyntaxError(tag, brStart, brEnd, "'>' expected");
+ } else {
+ throw new InternalException(string.Format(CultureInfo.InvariantCulture, "Unknown opening bracket '{0}'", tag.OpeningBracket));
+ }
+
+ // Attribute name may not apper multiple times
+ var duplicates = tag.Children.OfType<AXmlAttribute>().GroupBy(attr => attr.Name).SelectMany(g => g.Skip(1));
+ foreach(AXmlAttribute attr in duplicates) {
+ OnSyntaxError(tag, attr.StartOffset, attr.EndOffset, "Attribute with name '{0}' already exists", attr.Name);
+ }
+
+ tag.EndOffset = this.CurrentLocation;
+
+ OnParsed(tag);
+ return tag;
+ }
+
+ /// <summary>
+ /// Reads any of the know opening brackets. (only full bracket)
+ /// Context: "&lt;"
+ /// </summary>
+ string ReadOpeningBracket()
+ {
+ // We are using a lot of string literals so that the memory instances are shared
+ //int start = this.CurrentLocation;
+ if (TryRead('<')) {
+ if (TryRead('/')) {
+ return "</";
+ } else if (TryRead('?')) {
+ return "<?";
+ } else if (TryRead('!')) {
+ if (TryRead("--")) {
+ return "<!--";
+ } else if (TryRead("[CDATA[")) {
+ return "<![CDATA[";
+ } else {
+ foreach(string dtdName in AXmlTag.DtdNames) {
+ // the dtdName includes "<!"
+ if (TryRead(dtdName.Remove(0, 2))) return dtdName;
+ }
+ return "<!";
+ }
+ } else {
+ return "<";
+ }
+ } else {
+ throw new InternalException("'<' expected");
+ }
+ }
+
+ /// <summary>
+ /// Reads any of the know closing brackets. (only full bracket)
+ /// Context: any
+ /// </summary>
+ bool TryReadClosingBracket(out string bracket)
+ {
+ // We are using a lot of string literals so that the memory instances are shared
+ if (TryRead('>')) {
+ bracket = ">";
+ } else if (TryRead("/>")) {
+ bracket = "/>";
+ } else if (TryRead("?>")) {
+ bracket = "?>";
+ } else if (TryRead("-->")) {
+ bracket = "-->";
+ } else if (TryRead("]]>")) {
+ bracket = "]]>";
+ } else {
+ bracket = string.Empty;
+ return false;
+ }
+ return true;
+ }
+
+ IEnumerable<AXmlObject> ReadContentOfDTD()
+ {
+ int start = this.CurrentLocation;
+ while(true) {
+ if (IsEndOfFile()) break; // End of file
+ TryMoveToNonWhiteSpace(); // Skip whitespace
+ if (TryRead('\'')) TryMoveTo('\''); // Skip single quoted string TODO: Bug
+ if (TryRead('\"')) TryMoveTo('\"'); // Skip single quoted string
+ if (TryRead('[')) { // Start of nested infoset
+ // Reading infoset
+ while(true) {
+ if (IsEndOfFile()) break;
+ TryMoveToAnyOf('<', ']');
+ if (TryPeek('<')) {
+ if (start != this.CurrentLocation) { // Two following tags
+ yield return MakeText(start, this.CurrentLocation);
+ }
+ yield return ReadTag();
+ start = this.CurrentLocation;
+ }
+ if (TryPeek(']')) break;
+ }
+ }
+ TryRead(']'); // End of nested infoset
+ if (TryPeek('>')) break; // Proper closing
+ if (TryPeek('<')) break; // Malformed XML
+ TryMoveNext(); // Skip anything else
+ }
+ if (start != this.CurrentLocation) {
+ yield return MakeText(start, this.CurrentLocation);
+ }
+ }
+
+ /// <summary>
+ /// Context: name or "=\'\""
+ /// </summary>
+ AXmlAttribute ReadAttribulte()
+ {
+ AssertHasMoreData();
+
+ AXmlAttribute attr;
+ if (TryReadFromCacheOrNew(out attr)) return attr;
+
+ attr.StartOffset = this.CurrentLocation;
+
+ // Read name
+ string name;
+ if (TryReadName(out name)) {
+ if (!IsValidName(name)) {
+ OnSyntaxError(attr, this.CurrentLocation - name.Length, this.CurrentLocation, "The name '{0}' is invalid", name);
+ }
+ } else {
+ OnSyntaxError(attr, "Attribute name expected");
+ }
+ attr.Name = name;
+
+ // Read equals sign and surrounding whitespace
+ int checkpoint = this.CurrentLocation;
+ TryMoveToNonWhiteSpace();
+ if (TryRead('=')) {
+ int chk2 = this.CurrentLocation;
+ TryMoveToNonWhiteSpace();
+ if (!TryPeek('"') && !TryPeek('\'')) {
+ // Do not read whitespace if quote does not follow
+ GoBack(chk2);
+ }
+ attr.EqualsSign = GetText(checkpoint, this.CurrentLocation);
+ } else {
+ GoBack(checkpoint);
+ OnSyntaxError(attr, "'=' expected");
+ attr.EqualsSign = string.Empty;
+ }
+
+ // Read attribute value
+ int start = this.CurrentLocation;
+ char quoteChar = TryPeek('"') ? '"' : '\'';
+ bool startsWithQuote;
+ if (TryRead(quoteChar)) {
+ startsWithQuote = true;
+ int valueStart = this.CurrentLocation;
+ TryMoveToAnyOf(quoteChar, '<');
+ if (TryRead(quoteChar)) {
+ if (!TryPeekAnyOf(' ', '\t', '\n', '\r', '/', '>', '?')) {
+ if (TryPeekPrevious('=', 2) || (TryPeekPrevious('=', 3) && TryPeekPrevious(' ', 2))) {
+ // This actually most likely means that we are in the next attribute value
+ GoBack(valueStart);
+ ReadAttributeValue(quoteChar);
+ if (TryRead(quoteChar)) {
+ OnSyntaxError(attr, "White space or end of tag expected");
+ } else {
+ OnSyntaxError(attr, "Quote {0} expected (or add whitespace after the following one)", quoteChar);
+ }
+ } else {
+ OnSyntaxError(attr, "White space or end of tag expected");
+ }
+ }
+ } else {
+ // '<' or end of file
+ GoBack(valueStart);
+ ReadAttributeValue(quoteChar);
+ OnSyntaxError(attr, "Quote {0} expected", quoteChar);
+ }
+ } else {
+ startsWithQuote = false;
+ int valueStart = this.CurrentLocation;
+ ReadAttributeValue(null);
+ TryRead('\"');
+ TryRead('\'');
+ if (valueStart == this.CurrentLocation) {
+ OnSyntaxError(attr, "Attribute value expected");
+ } else {
+ OnSyntaxError(attr, valueStart, this.CurrentLocation, "Attribute value must be quoted");
+ }
+ }
+ attr.QuotedValue = GetText(start, this.CurrentLocation);
+ attr.Value = Unquote(attr.QuotedValue);
+ attr.Value = Dereference(attr, attr.Value, startsWithQuote ? start + 1 : start);
+
+ attr.EndOffset = this.CurrentLocation;
+
+ OnParsed(attr);
+ return attr;
+ }
+
+ /// <summary>
+ /// Read everything up to quote (excluding), opening/closing tag or attribute signature
+ /// </summary>
+ void ReadAttributeValue(char? quote)
+ {
+ while(true) {
+ if (IsEndOfFile()) return;
+ // What is next?
+ int start = this.CurrentLocation;
+ TryMoveToNonWhiteSpace(); // Read white space (if any)
+ if (quote.HasValue) {
+ if (TryPeek(quote.Value)) return;
+ } else {
+ if (TryPeek('"') || TryPeek('\'')) return;
+ }
+ // Opening/closing tag
+ string endBr;
+ if (TryPeek('<') || TryReadClosingBracket(out endBr)) {
+ GoBack(start);
+ return;
+ }
+ // Try reading attribute signature
+ string name;
+ if (TryReadName(out name)) {
+ int nameEnd = this.CurrentLocation;
+ if (TryMoveToNonWhiteSpace() && TryRead("=") &&
+ TryMoveToNonWhiteSpace() && TryPeekAnyOf('"', '\''))
+ {
+ // Start of attribute. Great
+ GoBack(start);
+ return; // Done
+ } else {
+ // Just some gargabe - make it part of the value
+ GoBack(nameEnd);
+ continue; // Read more
+ }
+ }
+ TryMoveNext(); // Accept everyting else
+ }
+ }
+
+ AXmlText MakeText(int start, int end)
+ {
+ AXmlParser.DebugAssert(end > start, "Empty text");
+
+ AXmlText text = new AXmlText() {
+ StartOffset = start,
+ EndOffset = end,
+ EscapedValue = GetText(start, end),
+ Type = TextType.Other
+ };
+
+ OnParsed(text);
+ return text;
+ }
+
+ const int maxEntityLength = 16; // The longest build-in one is 10 ("&#1114111;")
+ const int maxTextFragmentSize = 64;
+ const int lookAheadLength = (3 * maxTextFragmentSize) / 2; // More so that we do not get small "what was inserted" fragments
+
+ /// <summary>
+ /// Reads text and optionaly separates it into fragments.
+ /// It can also return empty set for no appropriate text input.
+ /// Make sure you enumerate it only once
+ /// </summary>
+ IEnumerable<AXmlObject> ReadText(TextType type)
+ {
+ bool lookahead = false;
+ while(true) {
+ AXmlText text;
+ if (TryReadFromCacheOrNew(out text, t => t.Type == type)) {
+ // Cached text found
+ yield return text;
+ continue; // Read next fragment; the method can handle "no text left"
+ }
+ text.Type = type;
+
+ // Limit the reading to just a few characters
+ // (the first character not to be read)
+ int fragmentEnd = Math.Min(this.CurrentLocation + maxTextFragmentSize, this.InputLength);
+
+ // Look if some futher text has been already processed and align so that
+ // we hit that chache point. It is expensive so it is off for the first run
+ if (lookahead) {
+ // Note: Must fit entity
+ AXmlObject nextFragment = trackedSegments.GetCachedObject<AXmlText>(this.CurrentLocation + maxEntityLength, lookAheadLength - maxEntityLength, t => t.Type == type);
+ if (nextFragment != null) {
+ fragmentEnd = Math.Min(nextFragment.StartOffset, this.InputLength);
+ AXmlParser.Log("Parsing only text ({0}-{1}) because later text was already processed", this.CurrentLocation, fragmentEnd);
+ }
+ }
+ lookahead = true;
+
+ text.StartOffset = this.CurrentLocation;
+ int start = this.CurrentLocation;
+
+ // Whitespace would be skipped anyway by any operation
+ TryMoveToNonWhiteSpace(fragmentEnd);
+ int wsEnd = this.CurrentLocation;
+
+ // Try move to the terminator given by the context
+ if (type == TextType.WhiteSpace) {
+ TryMoveToNonWhiteSpace(fragmentEnd);
+ } else if (type == TextType.CharacterData) {
+ while(true) {
+ if (!TryMoveToAnyOf(new char[] {'<', ']'}, fragmentEnd)) break; // End of fragment
+ if (TryPeek('<')) break;
+ if (TryPeek(']')) {
+ if (TryPeek("]]>")) {
+ OnSyntaxError(text, this.CurrentLocation, this.CurrentLocation + 3, "']]>' is not allowed in text");
+ }
+ TryMoveNext();
+ continue;
+ }
+ throw new Exception("Infinite loop");
+ }
+ } else if (type == TextType.Comment) {
+ // Do not report too many errors
+ bool errorReported = false;
+ while(true) {
+ if (!TryMoveTo('-', fragmentEnd)) break; // End of fragment
+ if (TryPeek("-->")) break;
+ if (TryPeek("--") && !errorReported) {
+ OnSyntaxError(text, this.CurrentLocation, this.CurrentLocation + 2, "'--' is not allowed in comment");
+ errorReported = true;
+ }
+ TryMoveNext();
+ }
+ } else if (type == TextType.CData) {
+ while(true) {
+ // We can not use use TryMoveTo("]]>", fragmentEnd) because it may incorectly accept "]" at the end of fragment
+ if (!TryMoveTo(']', fragmentEnd)) break; // End of fragment
+ if (TryPeek("]]>")) break;
+ TryMoveNext();
+ }
+ } else if (type == TextType.ProcessingInstruction) {
+ while(true) {
+ if (!TryMoveTo('?', fragmentEnd)) break; // End of fragment
+ if (TryPeek("?>")) break;
+ TryMoveNext();
+ }
+ } else if (type == TextType.UnknownBang) {
+ TryMoveToAnyOf(new char[] {'<', '>'}, fragmentEnd);
+ } else {
+ throw new Exception("Uknown type " + type);
+ }
+
+ text.ContainsOnlyWhitespace = (wsEnd == this.CurrentLocation);
+
+ // Terminal found or real end was reached;
+ bool finished = this.CurrentLocation < fragmentEnd || IsEndOfFile();
+
+ if (!finished) {
+ // We have to continue reading more text fragments
+
+ // If there is entity reference, make sure the next segment starts with it to prevent framentation
+ int entitySearchStart = Math.Max(start + 1 /* data for us */, this.CurrentLocation - maxEntityLength);
+ int entitySearchLength = this.CurrentLocation - entitySearchStart;
+ if (entitySearchLength > 0) {
+ // Note that LastIndexOf works backward
+ int entityIndex = input.LastIndexOf('&', this.CurrentLocation - 1, entitySearchLength);
+ if (entityIndex != -1) {
+ GoBack(entityIndex);
+ }
+ }
+ }
+
+ text.EscapedValue = GetText(start, this.CurrentLocation);
+ if (type == TextType.CharacterData) {
+ // Normalize end of line first
+ text.Value = Dereference(text, NormalizeEndOfLine(text.EscapedValue), start);
+ } else {
+ text.Value = text.EscapedValue;
+ }
+ text.EndOffset = this.CurrentLocation;
+
+ if (text.EscapedValue.Length > 0) {
+ OnParsed(text);
+ yield return text;
+ }
+
+ if (finished) {
+ yield break;
+ }
+ }
+ }
+
+ #region Helper methods
+
+ void OnSyntaxError(AXmlObject obj, string message, params object[] args)
+ {
+ OnSyntaxError(obj, this.CurrentLocation, this.CurrentLocation + 1, message, args);
+ }
+
+ public static void OnSyntaxError(AXmlObject obj, int start, int end, string message, params object[] args)
+ {
+ if (end <= start) end = start + 1;
+ string formattedMessage = string.Format(CultureInfo.InvariantCulture, message, args);
+ AXmlParser.Log("Syntax error ({0}-{1}): {2}", start, end, formattedMessage);
+ obj.AddSyntaxError(new SyntaxError() {
+ Object = obj,
+ StartOffset = start,
+ EndOffset = end,
+ Message = formattedMessage,
+ });
+ }
+
+ static bool IsValidName(string name)
+ {
+ try {
+ System.Xml.XmlConvert.VerifyName(name);
+ return true;
+ } catch (System.Xml.XmlException) {
+ return false;
+ }
+ }
+
+ /// <summary> Remove quoting from the given string </summary>
+ static string Unquote(string quoted)
+ {
+ if (string.IsNullOrEmpty(quoted)) return string.Empty;
+ char first = quoted[0];
+ if (quoted.Length == 1) return (first == '"' || first == '\'') ? string.Empty : quoted;
+ char last = quoted[quoted.Length - 1];
+ if (first == '"' || first == '\'') {
+ if (first == last) {
+ // Remove both quotes
+ return quoted.Substring(1, quoted.Length - 2);
+ } else {
+ // Remove first quote
+ return quoted.Remove(0, 1);
+ }
+ } else {
+ if (last == '"' || last == '\'') {
+ // Remove last quote
+ return quoted.Substring(0, quoted.Length - 1);
+ } else {
+ // Keep whole string
+ return quoted;
+ }
+ }
+ }
+
+ static string NormalizeEndOfLine(string text)
+ {
+ return text.Replace("\r\n", "\n").Replace("\r", "\n");
+ }
+
+ string Dereference(AXmlObject owner, string text, int textLocation)
+ {
+ StringBuilder sb = null; // The dereferenced text so far (all up to 'curr')
+ int curr = 0;
+ while(true) {
+ // Reached end of input
+ if (curr == text.Length) {
+ if (sb != null) {
+ return sb.ToString();
+ } else {
+ return text;
+ }
+ }
+
+ // Try to find reference
+ int start = text.IndexOf('&', curr);
+
+ // No more references found
+ if (start == -1) {
+ if (sb != null) {
+ sb.Append(text, curr, text.Length - curr); // Add rest
+ return sb.ToString();
+ } else {
+ return text;
+ }
+ }
+
+ // Append text before the enitiy reference
+ if (sb == null) sb = new StringBuilder(text.Length);
+ sb.Append(text, curr, start - curr);
+ curr = start;
+
+ // Process the entity
+ int errorLoc = textLocation + sb.Length;
+
+ // Find entity name
+ int end = text.IndexOfAny(new char[] {'&', ';'}, start + 1, Math.Min(maxEntityLength, text.Length - (start + 1)));
+ if (end == -1 || text[end] == '&') {
+ // Not found
+ OnSyntaxError(owner, errorLoc, errorLoc + 1, "Entity reference must be terminated with ';'");
+ // Keep '&'
+ sb.Append('&');
+ curr++;
+ continue; // Restart and next character location
+ }
+ string name = text.Substring(start + 1, end - (start + 1));
+
+ // Resolve the name
+ string replacement;
+ if (name.Length == 0) {
+ replacement = null;
+ OnSyntaxError(owner, errorLoc + 1, errorLoc + 1, "Entity name expected");
+ } else if (name == "amp") {
+ replacement = "&";
+ } else if (name == "lt") {
+ replacement = "<";
+ } else if (name == "gt") {
+ replacement = ">";
+ } else if (name == "apos") {
+ replacement = "'";
+ } else if (name == "quot") {
+ replacement = "\"";
+ } else if (name.Length > 0 && name[0] == '#') {
+ int num;
+ if (name.Length > 1 && name[1] == 'x') {
+ if (!int.TryParse(name.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat, out num)) {
+ num = -1;
+ OnSyntaxError(owner, errorLoc + 3, errorLoc + 1 + name.Length, "Hexadecimal code of unicode character expected");
+ }
+ } else {
+ if (!int.TryParse(name.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out num)) {
+ num = -1;
+ OnSyntaxError(owner, errorLoc + 2, errorLoc + 1 + name.Length, "Numeric code of unicode character expected");
+ }
+ }
+ if (num != -1) {
+ try {
+ replacement = char.ConvertFromUtf32(num);
+ } catch (ArgumentOutOfRangeException) {
+ replacement = null;
+ OnSyntaxError(owner, errorLoc + 2, errorLoc + 1 + name.Length, "Invalid unicode character U+{0:X} ({0})", num);
+ }
+ } else {
+ replacement = null;
+ }
+ } else if (!IsValidName(name)) {
+ replacement = null;
+ OnSyntaxError(owner, errorLoc + 1, errorLoc + 1, "Invalid entity name");
+ } else {
+ replacement = null;
+ if (parser.UnknownEntityReferenceIsError) {
+ OnSyntaxError(owner, errorLoc, errorLoc + 1 + name.Length + 1, "Unknown entity reference '{0}'", name);
+ }
+ }
+
+ // Append the replacement to output
+ if (replacement != null) {
+ sb.Append(replacement);
+ } else {
+ sb.Append('&');
+ sb.Append(name);
+ sb.Append(';');
+ }
+ curr = end + 1;
+ continue;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TextType.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TextType.cs
new file mode 100644
index 000000000..965185925
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TextType.cs
@@ -0,0 +1,39 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary> Identifies the context in which the text occured </summary>
+ enum TextType
+ {
+ /// <summary> Ends with non-whitespace </summary>
+ WhiteSpace,
+
+ /// <summary> Ends with "&lt;"; "]]&gt;" is error </summary>
+ CharacterData,
+
+ /// <summary> Ends with "-->"; "--" is error </summary>
+ Comment,
+
+ /// <summary> Ends with "]]&gt;" </summary>
+ CData,
+
+ /// <summary> Ends with "?>" </summary>
+ ProcessingInstruction,
+
+ /// <summary> Ends with "&lt;" or ">" </summary>
+ UnknownBang,
+
+ /// <summary> Unknown </summary>
+ Other
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TokenReader.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TokenReader.cs
new file mode 100644
index 000000000..83d3315ed
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TokenReader.cs
@@ -0,0 +1,309 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ class TokenReader
+ {
+ string input;
+ int inputLength;
+ int currentLocation;
+
+ // CurrentLocation is assumed to be touched and the fact does not
+ // have to be recorded in this variable.
+ // This stores any value bigger than that if applicable.
+ // Acutal value is max(currentLocation, maxTouchedLocation).
+ int maxTouchedLocation;
+
+ public int InputLength {
+ get { return inputLength; }
+ }
+
+ public int CurrentLocation {
+ get { return currentLocation; }
+ }
+
+ public int MaxTouchedLocation {
+ get { return Math.Max(currentLocation, maxTouchedLocation); }
+ }
+
+ public TokenReader(string input)
+ {
+ this.input = input;
+ this.inputLength = input.Length;
+ }
+
+ protected bool IsEndOfFile()
+ {
+ return currentLocation == inputLength;
+ }
+
+ protected bool HasMoreData()
+ {
+ return currentLocation < inputLength;
+ }
+
+ protected void AssertHasMoreData()
+ {
+ AXmlParser.Assert(HasMoreData(), "Unexpected end of file");
+ }
+
+ protected bool TryMoveNext()
+ {
+ if (currentLocation == inputLength) return false;
+
+ currentLocation++;
+ return true;
+ }
+
+ protected void Skip(int count)
+ {
+ AXmlParser.Assert(currentLocation + count <= inputLength, "Skipping after the end of file");
+ currentLocation += count;
+ }
+
+ protected void GoBack(int oldLocation)
+ {
+ AXmlParser.Assert(oldLocation <= currentLocation, "Trying to move forward");
+ maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation);
+ currentLocation = oldLocation;
+ }
+
+ protected bool TryRead(char c)
+ {
+ if (currentLocation == inputLength) return false;
+
+ if (input[currentLocation] == c) {
+ currentLocation++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected bool TryReadAnyOf(params char[] c)
+ {
+ if (currentLocation == inputLength) return false;
+
+ if (c.Contains(input[currentLocation])) {
+ currentLocation++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected bool TryRead(string text)
+ {
+ if (TryPeek(text)) {
+ currentLocation += text.Length;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected bool TryPeekPrevious(char c, int back)
+ {
+ if (currentLocation - back == inputLength) return false;
+ if (currentLocation - back < 0 ) return false;
+
+ return input[currentLocation - back] == c;
+ }
+
+ protected bool TryPeek(char c)
+ {
+ if (currentLocation == inputLength) return false;
+
+ return input[currentLocation] == c;
+ }
+
+ protected bool TryPeekAnyOf(params char[] chars)
+ {
+ if (currentLocation == inputLength) return false;
+
+ return chars.Contains(input[currentLocation]);
+ }
+
+ protected bool TryPeek(string text)
+ {
+ if (!TryPeek(text[0])) return false; // Early exit
+
+ maxTouchedLocation = Math.Max(maxTouchedLocation, currentLocation + (text.Length - 1));
+ // The following comparison 'touches' the end of file - it does depend on the end being there
+ if (currentLocation + text.Length > inputLength) return false;
+
+ return input.Substring(currentLocation, text.Length) == text;
+ }
+
+ protected bool TryPeekWhiteSpace()
+ {
+ if (currentLocation == inputLength) return false;
+
+ char c = input[currentLocation];
+ return ((int)c <= 0x20) && (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+ }
+
+ // The move functions do not have to move if already at target
+ // The move functions allow 'overriding' of the document length
+
+ protected bool TryMoveTo(char c)
+ {
+ return TryMoveTo(c, inputLength);
+ }
+
+ protected bool TryMoveTo(char c, int inputLength)
+ {
+ if (currentLocation == inputLength) return false;
+ int index = input.IndexOf(c, currentLocation, inputLength - currentLocation);
+ if (index != -1) {
+ currentLocation = index;
+ return true;
+ } else {
+ currentLocation = inputLength;
+ return false;
+ }
+ }
+
+ protected bool TryMoveToAnyOf(params char[] c)
+ {
+ return TryMoveToAnyOf(c, inputLength);
+ }
+
+ protected bool TryMoveToAnyOf(char[] c, int inputLength)
+ {
+ if (currentLocation == inputLength) return false;
+ int index = input.IndexOfAny(c, currentLocation, inputLength - currentLocation);
+ if (index != -1) {
+ currentLocation = index;
+ return true;
+ } else {
+ currentLocation = inputLength;
+ return false;
+ }
+ }
+
+ protected bool TryMoveTo(string text)
+ {
+ return TryMoveTo(text, inputLength);
+ }
+
+ protected bool TryMoveTo(string text, int inputLength)
+ {
+ if (currentLocation == inputLength) return false;
+ int index = input.IndexOf(text, currentLocation, inputLength - currentLocation, StringComparison.Ordinal);
+ if (index != -1) {
+ maxTouchedLocation = index + text.Length - 1;
+ currentLocation = index;
+ return true;
+ } else {
+ currentLocation = inputLength;
+ return false;
+ }
+ }
+
+ protected bool TryMoveToNonWhiteSpace()
+ {
+ return TryMoveToNonWhiteSpace(inputLength);
+ }
+
+ protected bool TryMoveToNonWhiteSpace(int inputLength)
+ {
+ while(true) {
+ if (currentLocation == inputLength) return false; // Reject end of file
+ char c = input[currentLocation];
+ if (((int)c <= 0x20) && (c == ' ' || c == '\t' || c == '\n' || c == '\r')) {
+ currentLocation++; // Accept white-space
+ continue;
+ } else {
+ return true; // Found non-white-space
+ }
+ }
+ }
+
+ /// <summary>
+ /// Read a name token.
+ /// The following characters are not allowed:
+ /// "" End of file
+ /// " \n\r\t" Whitesapce
+ /// "=\'\"" Attribute value
+ /// "&lt;>/?" Tags
+ /// </summary>
+ /// <returns> True if read at least one character </returns>
+ protected bool TryReadName(out string res)
+ {
+ int start = currentLocation;
+ // Keep reading up to invalid character
+ while(true) {
+ if (currentLocation == inputLength) break; // Reject end of file
+ char c = input[currentLocation];
+ if (0x41 <= (int)c) { // Accpet from 'A' onwards
+ currentLocation++;
+ continue;
+ }
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || // Reject whitesapce
+ c == '=' || c == '\'' || c == '"' || // Reject attributes
+ c == '<' || c == '>' || c == '/' || c == '?') { // Reject tags
+ break;
+ } else {
+ currentLocation++;
+ continue; // Accept other character
+ }
+ }
+ if (start == currentLocation) {
+ res = string.Empty;
+ return false;
+ } else {
+ res = GetText(start, currentLocation);
+ return true;
+ }
+ }
+
+ protected string GetText(int start, int end)
+ {
+ AXmlParser.Assert(end <= currentLocation, "Reading ahead of current location");
+ if (start == inputLength && end == inputLength) {
+ return string.Empty;
+ } else {
+ return GetCachedString(input.Substring(start, end - start));
+ }
+ }
+
+ Dictionary<string, string> stringCache = new Dictionary<string, string>();
+ int stringCacheRequestedCount;
+ int stringCacheRequestedSize;
+ int stringCacheStoredCount;
+ int stringCacheStoredSize;
+
+ string GetCachedString(string cached)
+ {
+ stringCacheRequestedCount += 1;
+ stringCacheRequestedSize += 8 + 2 * cached.Length;
+ // Do not bother with long strings
+ if (cached.Length > 32) {
+ stringCacheStoredCount += 1;
+ stringCacheStoredSize += 8 + 2 * cached.Length;
+ return cached;
+ }
+ if (stringCache.ContainsKey(cached)) {
+ // Get the instance from the cache instead
+ return stringCache[cached];
+ } else {
+ // Add to cache
+ stringCacheStoredCount += 1;
+ stringCacheStoredSize += 8 + 2 * cached.Length;
+ stringCache.Add(cached, cached);
+ return cached;
+ }
+ }
+
+ public void PrintStringCacheStats()
+ {
+ AXmlParser.Log("String cache: Requested {0} ({1} bytes); Actaully stored {2} ({3} bytes); {4}% stored", stringCacheRequestedCount, stringCacheRequestedSize, stringCacheStoredCount, stringCacheStoredSize, stringCacheRequestedSize == 0 ? 0 : stringCacheStoredSize * 100 / stringCacheRequestedSize);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TrackedSegmentCollection.cs b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TrackedSegmentCollection.cs
new file mode 100644
index 000000000..f8e516591
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/Xml/TrackedSegmentCollection.cs
@@ -0,0 +1,165 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+using Tango.Scripting.Editors.Document;
+
+namespace Tango.Scripting.Editors.Xml
+{
+ /// <summary>
+ /// Holds all objects that need to keep offsets up to date.
+ /// </summary>
+ class TrackedSegmentCollection
+ {
+ /// <summary>
+ /// Holds all types of objects in one collection.
+ /// </summary>
+ TextSegmentCollection<TextSegment> segments = new TextSegmentCollection<TextSegment>();
+
+ /// <summary>
+ /// Is used to identify what memory range was touched by object
+ /// The default is (StartOffset, EndOffset + 1) which is not stored
+ /// </summary>
+ class TouchedRange: TextSegment
+ {
+ public AXmlObject TouchedByObject { get; set; }
+ }
+
+ public void UpdateOffsetsAndInvalidate(IEnumerable<DocumentChangeEventArgs> changes)
+ {
+ foreach(DocumentChangeEventArgs change in changes) {
+ // Update offsets of all items
+ segments.UpdateOffsets(change);
+
+ // Remove any items affected by the change
+ AXmlParser.Log("Changed {0}-{1}", change.Offset, change.Offset + change.InsertionLength);
+ // Removing will cause one of the ends to be set to change.Offset
+ // FindSegmentsContaining includes any segments touching
+ // so that conviniently takes care of the +1 byte
+ var segmentsContainingOffset = segments.FindOverlappingSegments(change.Offset, change.InsertionLength);
+ foreach(AXmlObject obj in segmentsContainingOffset.OfType<AXmlObject>().Where(o => o.IsCached)) {
+ InvalidateCache(obj, false);
+ }
+ foreach(TouchedRange range in segmentsContainingOffset.OfType<TouchedRange>()) {
+ AXmlParser.Log("Found that {0} dependeds on ({1}-{2})", range.TouchedByObject, range.StartOffset, range.EndOffset);
+ InvalidateCache(range.TouchedByObject, true);
+ segments.Remove(range);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Invlidates all objects. That is, the whole document has changed.
+ /// </summary>
+ /// <remarks> We still have to keep the items becuase they might be in the document </remarks>
+ public void InvalidateAll()
+ {
+ AXmlParser.Log("Invalidating all objects");
+ foreach(AXmlObject obj in segments.OfType<AXmlObject>()) {
+ obj.IsCached = false;
+ }
+ }
+
+ /// <summary> Add object to cache, optionally adding extra memory tracking </summary>
+ public void AddParsedObject(AXmlObject obj, int? maxTouchedLocation)
+ {
+ if (!(obj.Length > 0 || obj is AXmlDocument))
+ AXmlParser.Assert(false, string.Format(CultureInfo.InvariantCulture, "Invalid object {0}. It has zero length.", obj));
+// // Expensive check
+// if (obj is AXmlContainer) {
+// int objStartOffset = obj.StartOffset;
+// int objEndOffset = obj.EndOffset;
+// foreach(AXmlObject child in ((AXmlContainer)obj).Children) {
+// AXmlParser.Assert(objStartOffset <= child.StartOffset && child.EndOffset <= objEndOffset, "Wrong nesting");
+// }
+// }
+ segments.Add(obj);
+ AddSyntaxErrorsOf(obj);
+ obj.IsCached = true;
+ if (maxTouchedLocation != null) {
+ // location is assumed to be read so the range ends at (location + 1)
+ // For example eg for "a_" it is (0-2)
+ TouchedRange range = new TouchedRange() {
+ StartOffset = obj.StartOffset,
+ EndOffset = maxTouchedLocation.Value + 1,
+ TouchedByObject = obj
+ };
+ segments.Add(range);
+ AXmlParser.Log("{0} touched range ({1}-{2})", obj, range.StartOffset, range.EndOffset);
+ }
+ }
+
+ /// <summary> Removes object with all of its non-cached children </summary>
+ public void RemoveParsedObject(AXmlObject obj)
+ {
+ // Cached objects may be used in the future - do not remove them
+ if (obj.IsCached) return;
+ segments.Remove(obj);
+ RemoveSyntaxErrorsOf(obj);
+ AXmlParser.Log("Stopped tracking {0}", obj);
+
+ AXmlContainer container = obj as AXmlContainer;
+ if (container != null) {
+ foreach (AXmlObject child in container.Children) {
+ RemoveParsedObject(child);
+ }
+ }
+ }
+
+ public void AddSyntaxErrorsOf(AXmlObject obj)
+ {
+ foreach(SyntaxError syntaxError in obj.MySyntaxErrors) {
+ segments.Add(syntaxError);
+ }
+ }
+
+ public void RemoveSyntaxErrorsOf(AXmlObject obj)
+ {
+ foreach(SyntaxError syntaxError in obj.MySyntaxErrors) {
+ segments.Remove(syntaxError);
+ }
+ }
+
+ IEnumerable<AXmlObject> FindParents(AXmlObject child)
+ {
+ int childStartOffset = child.StartOffset;
+ int childEndOffset = child.EndOffset;
+ foreach(AXmlObject parent in segments.FindSegmentsContaining(child.StartOffset).OfType<AXmlObject>()) {
+ // Parent is anyone wholy containg the child
+ if (parent.StartOffset <= childStartOffset && childEndOffset <= parent.EndOffset && parent != child) {
+ yield return parent;
+ }
+ }
+ }
+
+ /// <summary> Invalidates items, but keeps tracking them </summary>
+ /// <remarks> Can be called redundantly (from range tacking) </remarks>
+ void InvalidateCache(AXmlObject obj, bool includeParents)
+ {
+ if (includeParents) {
+ foreach(AXmlObject parent in FindParents(obj)) {
+ parent.IsCached = false;
+ AXmlParser.Log("Invalidating cached item {0} (it is parent)", parent);
+ }
+ }
+ obj.IsCached = false;
+ AXmlParser.Log("Invalidating cached item {0}", obj);
+ }
+
+ public T GetCachedObject<T>(int offset, int lookaheadCount, Predicate<T> conditon) where T: AXmlObject, new()
+ {
+ TextSegment obj = segments.FindFirstSegmentWithStartAfter(offset);
+ while(obj != null && offset <= obj.StartOffset && obj.StartOffset <= offset + lookaheadCount) {
+ if (obj is T && ((AXmlObject)obj).IsCached && conditon((T)obj)) {
+ return (T)obj;
+ }
+ obj = segments.GetNextSegment(obj);
+ }
+ return null;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/app.config b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/app.config
new file mode 100644
index 000000000..6033f322c
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/app.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+
+</configuration> \ No newline at end of file
diff --git a/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/packages.config b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/packages.config
new file mode 100644
index 000000000..f69b3c48f
--- /dev/null
+++ b/Software/Visual_Studio/TEMP/Tango.Scripting/Tango.Scripting.Editors/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="FontAwesome.WPF" version="4.7.0.9" targetFramework="net461" />
+</packages> \ No newline at end of file