aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Editing/AbstractMargin.cs
blob: c82ff94a09e3b984b08276dbe959d0cc87ffaa72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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;
		}
	}
}
ntPtr 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); } }