diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
index 4fa4a0c07e..8c86cdfb8e 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
@@ -112,8 +112,11 @@
+
+
+
@@ -217,6 +220,7 @@
SharpDevelopCompletionWindow.cs
+
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}
ICSharpCode.AvalonEdit
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
index a7af205bd2..f9c81e0316 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
@@ -59,19 +59,32 @@ namespace ICSharpCode.AvalonEdit.AddIn
const string contextMenuPath = "/SharpDevelop/ViewContent/AvalonEdit/ContextMenu";
QuickClassBrowser quickClassBrowser;
- readonly CodeEditorView primaryTextEditor;
- readonly CodeEditorAdapter primaryTextEditorAdapter;
+ private CodeEditorView primaryTextEditor;
+ private CodeEditorView secondaryTextEditor;
+ private CodeEditorAdapter primaryTextEditorAdapter;
readonly IconBarManager iconBarManager;
readonly TextMarkerService textMarkerService;
readonly IChangeWatcher changeWatcher;
ErrorPainter errorPainter;
+ private EditorSplitContainer _splitContainer;
public CodeEditorView PrimaryTextEditor {
get { return primaryTextEditor; }
}
+ public CodeEditorView SecondaryTextEditor{
+ get{ return secondaryTextEditor; }
+ }
+
public CodeEditorView ActiveTextEditor {
- get { return primaryTextEditor; }
+ get
+ {
+ // we return the editor that has keyboard focus in it.
+ if (secondaryTextEditor != null && secondaryTextEditor.IsKeyboardFocusWithin)
+ return secondaryTextEditor;
+
+ return primaryTextEditor;
+ }
}
TextDocument document;
@@ -129,23 +142,26 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (changeWatcher != null) {
changeWatcher.Initialize(this.Document, value);
}
- UpdateSyntaxHighlighting(value);
+
+ UpdateSyntaxHighlighting(PrimaryTextEditor, value);
+
+
FetchParseInformation();
}
}
}
- void UpdateSyntaxHighlighting(FileName fileName)
+ void UpdateSyntaxHighlighting(CodeEditorView editorView, FileName fileName)
{
- var oldHighlighter = primaryTextEditor.GetService();
+ var oldHighlighter = editorView.GetService();
var highlighting = HighlightingManager.Instance.GetDefinitionByExtension(Path.GetExtension(fileName));
var highlighter = SD.EditorControlService.CreateHighlighter(document);
- primaryTextEditor.SyntaxHighlighting = highlighting;
- primaryTextEditor.TextArea.TextView.LineTransformers.RemoveAll(t => t is HighlightingColorizer);
- primaryTextEditor.TextArea.TextView.LineTransformers.Insert(0, new HighlightingColorizer(highlighter));
- primaryTextEditor.UpdateCustomizedHighlighting();
+ editorView.SyntaxHighlighting = highlighting;
+ editorView.TextArea.TextView.LineTransformers.RemoveAll(t => t is HighlightingColorizer);
+ editorView.TextArea.TextView.LineTransformers.Insert(0, new HighlightingColorizer(highlighter));
+ editorView.UpdateCustomizedHighlighting();
// Dispose the old highlighter; necessary to avoid memory leaks as
// semantic highlighters might attach to global parser events.
@@ -161,6 +177,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
const double minRowHeight = 40;
+ static CodeEditor()
+ {
+ InitEventHandlers();
+ }
+
public CodeEditor()
{
CodeEditorOptions.Instance.PropertyChanged += CodeEditorOptions_Instance_PropertyChanged;
@@ -187,9 +208,14 @@ namespace ICSharpCode.AvalonEdit.AddIn
this.ColumnDefinitions.Add(new ColumnDefinition());
this.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
this.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star), MinHeight = minRowHeight });
- SetRow(primaryTextEditor, 1);
- this.Children.Add(primaryTextEditor);
+ _splitContainer = new EditorSplitContainer
+ {
+ PrimaryView = primaryTextEditor
+ };
+ SetRow(_splitContainer, 1);
+
+ this.Children.Add(_splitContainer);
}
void CodeEditorOptions_Instance_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
@@ -252,6 +278,65 @@ namespace ICSharpCode.AvalonEdit.AddIn
return codeEditorView;
}
+ #region "EditorSplitContainer integration"
+
+ private static void InitEventHandlers()
+ {
+ EventManager.RegisterClassHandler(
+ typeof (CodeEditor),
+ EditorSplitContainer.RequestSecondaryViewEvent,
+ new RequestSecondaryViewEventHandler(
+ (sender, args) =>
+ {
+ var codeEditor = sender as CodeEditor;
+ if (codeEditor == null)
+ return;
+
+ codeEditor.OnRequestSecondaryView(args);
+ }));
+
+ EventManager.RegisterClassHandler(
+ typeof (CodeEditor),
+ EditorSplitContainer.DisposeSecondaryViewEvent,
+ new DisposeSecondaryViewEventHandler(
+ (sender, args) =>
+ {
+ var codeEditor = sender as CodeEditor;
+ if (codeEditor == null)
+ return;
+
+ codeEditor.OnDisposeOfSecondaryView(args);
+ }));
+ }
+
+ private void OnRequestSecondaryView(RequestSecondaryViewEventArgs e)
+ {
+ secondaryTextEditor = CreateTextEditor();
+ UpdateSyntaxHighlighting(secondaryTextEditor, FileName);
+ e.Container = secondaryTextEditor;
+ e.Handled = true;
+ }
+
+ private void OnDisposeOfSecondaryView(DisposeSecondaryViewEventArgs e)
+ {
+ var container = e.Container;
+ if (container == null)
+ return;
+
+ // if the primary editor is being disposed of, the user chose to keep the secondary,
+ // swap the editors.
+ if (container == PrimaryTextEditor && SecondaryTextEditor != null) {
+ primaryTextEditor = secondaryTextEditor;
+ primaryTextEditorAdapter = (CodeEditorAdapter)primaryTextEditor.TextArea.GetService(typeof(ITextEditor));
+ Debug.Assert(primaryTextEditorAdapter != null);
+ }
+
+ secondaryTextEditor = null;
+ e.Handled = true;
+ }
+
+ #endregion
+
void textEditor_TextArea_TextCopied(object sender, TextEventArgs e)
{
ICSharpCode.SharpDevelop.Gui.TextEditorSideBar.Instance.PutInClipboardRing(e.Text);
@@ -711,6 +796,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
changeWatcher.Dispose();
this.Document = null;
DisposeTextEditor(primaryTextEditor);
+
+ //TODO: dispose of secondary editor is necessary
+ if (secondaryTextEditor != null) {
+ DisposeTextEditor(secondaryTextEditor);
+ }
}
}
}
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/EditorSplitContainer.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/EditorSplitContainer.cs
new file mode 100644
index 0000000000..6b1913ea80
--- /dev/null
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/EditorSplitContainer.cs
@@ -0,0 +1,833 @@
+/*
+ * Created by SharpDevelop.
+ * User: Abdelkarim
+ * Date: 9/22/2014
+ * Time: 8:07 AM
+ *
+ */
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Markup;
+using System.Windows.Media;
+using System.Windows.Threading;
+
+namespace ICSharpCode.AvalonEdit.AddIn
+{
+ [ContentProperty("PrimaryView")]
+ public class EditorSplitContainer : FrameworkElement
+ {
+ #region "Fields"
+
+ private readonly Rect _emptyRect = new Rect();
+ private UIElement _secondaryViewContainer;
+ private UIElement _primaryViewContainer;
+ private SpliterGrip _grip;
+ private readonly UIElementCollection _visualChildren;
+ private double _gripOffset;
+ // in percent
+ private bool _isSecondaryViewCollapsing;
+ private bool _isPrimaryViewCollapsing;
+ private bool _hideSpliterGrip;
+ private bool _isGripDragging;
+
+ #endregion
+
+ #region "Constructors"
+
+ ///
+ /// Initializes static members of the class.
+ ///
+ static EditorSplitContainer()
+ {
+ ClipToBoundsProperty.OverrideMetadata(typeof(EditorSplitContainer), new FrameworkPropertyMetadata(BooleanBoxes.True));
+ InitCommands();
+ InitEventHandlers();
+ }
+
+ ///
+ /// Initializes instance members of the class.
+ ///
+ public EditorSplitContainer()
+ {
+ InitSplitterHandle();
+ _visualChildren = new UIElementCollection(this, this) { _grip };
+ LoadInInitialLayout();
+ }
+
+ #endregion
+
+ #region "Events"
+
+ #region RequestSecondaryView
+
+ ///
+ /// RequestSecondaryView Routed Event
+ ///
+ public static readonly RoutedEvent RequestSecondaryViewEvent = EventManager.RegisterRoutedEvent("RequestSecondaryView",
+ RoutingStrategy.Bubble, typeof(RequestSecondaryViewEventHandler), typeof(EditorSplitContainer));
+
+ ///
+ /// Occurs when ...
+ ///
+ public event RequestSecondaryViewEventHandler RequestSecondaryView {
+ add { AddHandler(RequestSecondaryViewEvent, value); }
+ remove { RemoveHandler(RequestSecondaryViewEvent, value); }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected RequestSecondaryViewEventArgs RaiseRequestSecondaryViewEvent()
+ {
+ var args = new RequestSecondaryViewEventArgs { RoutedEvent = RequestSecondaryViewEvent };
+ this.RaiseEvent(args);
+ return args;
+ }
+
+ #endregion
+
+ #region DisposeSecondaryView
+
+ ///
+ /// DisposeSecondaryView Routed Event
+ ///
+ public static readonly RoutedEvent DisposeSecondaryViewEvent = EventManager.RegisterRoutedEvent(
+ "DisposeSecondaryView",
+ RoutingStrategy.Bubble,
+ typeof(DisposeSecondaryViewEventHandler),
+ typeof(EditorSplitContainer));
+
+ ///
+ /// Occurs when ...
+ ///
+ public event DisposeSecondaryViewEventHandler DisposeSecondaryView {
+ add { AddHandler(DisposeSecondaryViewEvent, value); }
+ remove { RemoveHandler(DisposeSecondaryViewEvent, value); }
+ }
+
+ ///
+ /// A helper method to raise the event.
+ ///
+ protected void RaiseDisposeSecondaryViewEvent(object container)
+ {
+ // It is not immediately raised, but when idle.
+ // In case the dispose operation is expensive.
+
+ this.Dispatcher.BeginInvoke(
+ DispatcherPriority.ApplicationIdle,
+ new Action