Code Coverage Statistics for Source File

c:\Tools\SD3\src\Libraries\ICSharpCode.TextEditor\Project\Src\Gui\Caret.cs

Sequence Point Coverage
N/A
0 of 0
Branch Coverage
N/A
0 of 0
Lines
338
Highlight: Uncovered Code Covered Code
L V Source
1
// <file>
2
//     <copyright see="prj:///doc/copyright.txt"/>
3
//     <license see="prj:///doc/license.txt"/>
4
//     <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
5
//     <version>$Revision: 2683 $</version>
6
// </file>
7
8
using System;
9
using System.Collections.Generic;
10
using System.Drawing;
11
using System.Runtime.InteropServices;
12
13
using ICSharpCode.TextEditor.Document;
14
15
namespace ICSharpCode.TextEditor
16
{
17
	/// <summary>
18
	/// In this enumeration are all caret modes listed.
19
	/// </summary>
20
	public enum CaretMode {
21
		/// <summary>
22
		/// If the caret is in insert mode typed characters will be
23
		/// inserted at the caret position
24
		/// </summary>
25
		InsertMode,
26
		
27
		/// <summary>
28
		/// If the caret is in overwirte mode typed characters will
29
		/// overwrite the character at the caret position
30
		/// </summary>
31
		OverwriteMode
32
	}
33
	
34
	
35
	public class Caret : System.IDisposable
36
	{
37
		int       line          = 0;
38
		int       column        = 0;
39
		int       desiredXPos   = 0;
40
		CaretMode caretMode;
41
		
42
		static bool     caretCreated = false;
43
		bool     hidden       = true;
44
		TextArea textArea;
45
		Point    currentPos   = new Point(-1, -1);
46
		Ime      ime          = null;
47
		
48
		/// <value>
49
		/// The 'prefered' xPos in which the caret moves, when it is moved
50
		/// up/down.
51
		/// </value>
52
		public int DesiredColumn {
53
			get {
54
				return desiredXPos;
55
			}
56
			set {
57
				desiredXPos = value;
58
			}
59
		}
60
		
61
		/// <value>
62
		/// The current caret mode.
63
		/// </value>
64
		public CaretMode CaretMode {
65
			get {
66
				return caretMode;
67
			}
68
			set {
69
				caretMode = value;
70
				OnCaretModeChanged(EventArgs.Empty);
71
			}
72
		}
73
		
74
		public int Line {
75
			get {
76
				return line;
77
			}
78
			set {
79
				line = value;
80
				ValidateCaretPos();
81
				UpdateCaretPosition();
82
				OnPositionChanged(EventArgs.Empty);
83
			}
84
		}
85
		
86
		public int Column {
87
			get {
88
				return column;
89
			}
90
			set {
91
				column = value;
92
				ValidateCaretPos();
93
				UpdateCaretPosition();
94
				OnPositionChanged(EventArgs.Empty);
95
			}
96
		}
97
		
98
		public TextLocation Position {
99
			get {
100
				return new TextLocation(column, line);
101
			}
102
			set {
103
				line   = value.Y;
104
				column = value.X;
105
				ValidateCaretPos();
106
				UpdateCaretPosition();
107
				OnPositionChanged(EventArgs.Empty);
108
			}
109
		}
110
		
111
		public int Offset {
112
			get {
113
				return textArea.Document.PositionToOffset(Position);
114
			}
115
		}
116
		
117
		public Caret(TextArea textArea)
118
		{
119
			this.textArea = textArea;
120
			textArea.GotFocus  += new EventHandler(GotFocus);
121
			textArea.LostFocus += new EventHandler(LostFocus);
122
		}
123
		
124
		public void Dispose()
125
		{
126
			textArea.GotFocus  -= new EventHandler(GotFocus);
127
			textArea.LostFocus -= new EventHandler(LostFocus);
128
			textArea = null;
129
//			DestroyCaret();
130
//			caretCreated = false;
131
		}
132
		
133
		public TextLocation ValidatePosition(TextLocation pos)
134
		{
135
			int line   = Math.Max(0, Math.Min(textArea.Document.TotalNumberOfLines - 1, pos.Y));
136
			int column = Math.Max(0, pos.X);
137
			
138
			if (column == int.MaxValue || !textArea.TextEditorProperties.AllowCaretBeyondEOL) {
139
				LineSegment lineSegment = textArea.Document.GetLineSegment(line);
140
				column = Math.Min(column, lineSegment.Length);
141
			}
142
			return new TextLocation(column, line);
143
		}
144
		
145
		/// <remarks>
146
		/// If the caret position is outside the document text bounds
147
		/// it is set to the correct position by calling ValidateCaretPos.
148
		/// </remarks>
149
		public void ValidateCaretPos()
150
		{
151
			line = Math.Max(0, Math.Min(textArea.Document.TotalNumberOfLines - 1, line));
152
			column = Math.Max(0, column);
153
			
154
			if (column == int.MaxValue || !textArea.TextEditorProperties.AllowCaretBeyondEOL) {
155
				LineSegment lineSegment = textArea.Document.GetLineSegment(line);
156
				column = Math.Min(column, lineSegment.Length);
157
			}
158
		}
159
		
160
		void CreateCaret()
161
		{
162
			while (!caretCreated) {
163
				switch (caretMode) {
164
					case CaretMode.InsertMode:
165
						caretCreated = CreateCaret(textArea.Handle, 0, 2, textArea.TextView.FontHeight);
166
						break;
167
					case CaretMode.OverwriteMode:
168
						caretCreated = CreateCaret(textArea.Handle, 0, (int)textArea.TextView.SpaceWidth, textArea.TextView.FontHeight);
169
						break;
170
				}
171
			}
172
			if (currentPos.X  < 0) {
173
				ValidateCaretPos();
174
				currentPos = ScreenPosition;
175
			}
176
			SetCaretPos(currentPos.X, currentPos.Y);
177
			ShowCaret(textArea.Handle);
178
		}
179
		
180
		public void RecreateCaret()
181
		{
182
			DisposeCaret();
183
			if (!hidden) {
184
				CreateCaret();
185
			}
186
		}
187
		
188
		void DisposeCaret()
189
		{
190
			caretCreated = false;
191
			HideCaret(textArea.Handle);
192
			DestroyCaret();
193
		}
194
		
195
		void GotFocus(object sender, EventArgs e)
196
		{
197
			hidden = false;
198
			if (!textArea.MotherTextEditorControl.IsInUpdate) {
199
				CreateCaret();
200
				UpdateCaretPosition();
201
			}
202
		}
203
		
204
		void LostFocus(object sender, EventArgs e)
205
		{
206
			hidden       = true;
207
			DisposeCaret();
208
		}
209
		
210
		public Point ScreenPosition {
211
			get {
212
				int xpos = textArea.TextView.GetDrawingXPos(this.line, this.column);
213
				return new Point(textArea.TextView.DrawingPosition.X + xpos,
214
				                 textArea.TextView.DrawingPosition.Y
215
				                 + (textArea.Document.GetVisibleLine(this.line)) * textArea.TextView.FontHeight
216
				                 - textArea.TextView.TextArea.VirtualTop.Y);
217
			}
218
		}
219
		int oldLine = -1;
220
		public void UpdateCaretPosition()
221
		{
222
			if (textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow && oldLine != line) {
223
				textArea.UpdateLine(oldLine);
224
				textArea.UpdateLine(line);
225
			}
226
			oldLine = line;
227
			
228
			
229
			if (hidden || textArea.MotherTextEditorControl.IsInUpdate) {
230
				return;
231
			}
232
			if (!caretCreated) {
233
				CreateCaret();
234
			}
235
			if (caretCreated) {
236
				ValidateCaretPos();
237
				int lineNr = this.line;
238
				int xpos = textArea.TextView.GetDrawingXPos(lineNr, this.column);
239
				//LineSegment lineSegment = textArea.Document.GetLineSegment(lineNr);
240
				Point pos = ScreenPosition;
241
				if (xpos >= 0) {
242
					bool success = SetCaretPos(pos.X, pos.Y);
243
					if (!success) {
244
						DestroyCaret();
245
						caretCreated = false;
246
						UpdateCaretPosition();
247
					}
248
				}
249
				// set the input method editor location
250
				if (ime == null) {
251
					ime = new Ime(textArea.Handle, textArea.Document.TextEditorProperties.Font);
252
				} else {
253
					ime.HWnd = textArea.Handle;
254
					ime.Font = textArea.Document.TextEditorProperties.Font;
255
				}
256
				ime.SetIMEWindowLocation(pos.X, pos.Y);
257
				
258
				currentPos = pos;
259
			}
260
		}
261
		
262
		#region Native caret functions
263
		[DllImport("User32.dll")]
264
		static extern bool CreateCaret(IntPtr hWnd, int hBitmap, int nWidth, int nHeight);
265
		
266
		[DllImport("User32.dll")]
267
		static extern bool SetCaretPos(int x, int y);
268
		
269
		[DllImport("User32.dll")]
270
		static extern bool DestroyCaret();
271
		
272
		[DllImport("User32.dll")]
273
		static extern bool ShowCaret(IntPtr hWnd);
274
		
275
		[DllImport("User32.dll")]
276
		static extern bool HideCaret(IntPtr hWnd);
277
		#endregion
278
		
279
		bool firePositionChangedAfterUpdateEnd;
280
		
281
		void FirePositionChangedAfterUpdateEnd(object sender, EventArgs e)
282
		{
283
			OnPositionChanged(EventArgs.Empty);
284
		}
285
		
286
		protected virtual void OnPositionChanged(EventArgs e)
287
		{
288
			if (this.textArea.MotherTextEditorControl.IsInUpdate) {
289
				if (firePositionChangedAfterUpdateEnd == false) {
290
					firePositionChangedAfterUpdateEnd = true;
291
					this.textArea.Document.UpdateCommited += FirePositionChangedAfterUpdateEnd;
292
				}
293
				return;
294
			} else if (firePositionChangedAfterUpdateEnd) {
295
				this.textArea.Document.UpdateCommited -= FirePositionChangedAfterUpdateEnd;
296
				firePositionChangedAfterUpdateEnd = false;
297
			}
298
			
299
			List<FoldMarker> foldings = textArea.Document.FoldingManager.GetFoldingsFromPosition(line, column);
300
			bool  shouldUpdate = false;
301
			foreach (FoldMarker foldMarker in foldings) {
302
				shouldUpdate |= foldMarker.IsFolded;
303
				foldMarker.IsFolded = false;
304
			}
305
			
306
			if (shouldUpdate) {
307
				textArea.Document.FoldingManager.NotifyFoldingsChanged(EventArgs.Empty);
308
			}
309
			
310
			if (PositionChanged != null) {
311
				PositionChanged(this, e);
312
			}
313
			textArea.ScrollToCaret();
314
		}
315
		
316
		protected virtual void OnCaretModeChanged(EventArgs e)
317
		{
318
			if (CaretModeChanged != null) {
319
				CaretModeChanged(this, e);
320
			}
321
			HideCaret(textArea.Handle);
322
			DestroyCaret();
323
			caretCreated = false;
324
			CreateCaret();
325
			ShowCaret(textArea.Handle);
326
		}
327
		
328
		/// <remarks>
329
		/// Is called each time the caret is moved.
330
		/// </remarks>
331
		public event EventHandler PositionChanged;
332
		
333
		/// <remarks>
334
		/// Is called each time the CaretMode has changed.
335
		/// </remarks>
336
		public event EventHandler CaretModeChanged;
337
	}
338
}