Code Coverage Statistics for Source File

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

Sequence Point Coverage
N/A
0 of 0
Branch Coverage
N/A
0 of 0
Lines
464
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: 2681 $</version>
6
// </file>
7
8
using System;
9
using System.ComponentModel;
10
using System.Drawing;
11
using System.Windows.Forms;
12
13
using ICSharpCode.TextEditor.Document;
14
15
namespace ICSharpCode.TextEditor
16
{
17
	/// <summary>
18
	/// This class paints the textarea.
19
	/// </summary>
20
	[ToolboxItem(false)]
21
	public class TextAreaControl : Panel
22
	{
23
		TextEditorControl motherTextEditorControl;
24
		
25
		HRuler     hRuler     = null;
26
		
27
		VScrollBar vScrollBar = new VScrollBar();
28
		HScrollBar hScrollBar = new HScrollBar();
29
		TextArea   textArea;
30
		bool       doHandleMousewheel = true;
31
		bool       disposed;
32
		
33
		public TextArea TextArea {
34
			get {
35
				return textArea;
36
			}
37
		}
38
		
39
		public SelectionManager SelectionManager {
40
			get {
41
				return textArea.SelectionManager;
42
			}
43
		}
44
		
45
		public Caret Caret {
46
			get {
47
				return textArea.Caret;
48
			}
49
		}
50
		
51
		[Browsable(false)]
52
		public IDocument Document {
53
			get {
54
				if (motherTextEditorControl != null)
55
					return motherTextEditorControl.Document;
56
				return null;
57
			}
58
		}
59
		
60
		public ITextEditorProperties TextEditorProperties {
61
			get {
62
				if (motherTextEditorControl != null)
63
					return motherTextEditorControl.TextEditorProperties;
64
				return null;
65
			}
66
		}
67
		
68
		public VScrollBar VScrollBar {
69
			get {
70
				return vScrollBar;
71
			}
72
		}
73
		
74
		public HScrollBar HScrollBar {
75
			get {
76
				return hScrollBar;
77
			}
78
		}
79
		
80
		public bool DoHandleMousewheel {
81
			get {
82
				return doHandleMousewheel;
83
			}
84
			set {
85
				doHandleMousewheel = value;
86
			}
87
		}
88
		
89
		public TextAreaControl(TextEditorControl motherTextEditorControl)
90
		{
91
			this.motherTextEditorControl = motherTextEditorControl;
92
			
93
			this.textArea                = new TextArea(motherTextEditorControl, this);
94
			Controls.Add(textArea);
95
			
96
			vScrollBar.ValueChanged += new EventHandler(VScrollBarValueChanged);
97
			Controls.Add(this.vScrollBar);
98
			
99
			hScrollBar.ValueChanged += new EventHandler(HScrollBarValueChanged);
100
			Controls.Add(this.hScrollBar);
101
			ResizeRedraw = true;
102
			
103
			Document.TextContentChanged += DocumentTextContentChanged;
104
			Document.DocumentChanged += AdjustScrollBarsOnDocumentChange;
105
			Document.UpdateCommited  += AdjustScrollBarsOnCommittedUpdate;
106
		}
107
		
108
		protected override void Dispose(bool disposing)
109
		{
110
			if (disposing) {
111
				if (!disposed) {
112
					disposed = true;
113
					Document.TextContentChanged -= DocumentTextContentChanged;
114
					Document.DocumentChanged -= AdjustScrollBarsOnDocumentChange;
115
					Document.UpdateCommited  -= AdjustScrollBarsOnCommittedUpdate;
116
					motherTextEditorControl = null;
117
					if (vScrollBar != null) {
118
						vScrollBar.Dispose();
119
						vScrollBar = null;
120
					}
121
					if (hScrollBar != null) {
122
						hScrollBar.Dispose();
123
						hScrollBar = null;
124
					}
125
					if (hRuler != null) {
126
						hRuler.Dispose();
127
						hRuler = null;
128
					}
129
				}
130
			}
131
			base.Dispose(disposing);
132
		}
133
		
134
		void DocumentTextContentChanged(object sender, EventArgs e)
135
		{
136
			// after the text content is changed abruptly, we need to validate the
137
			// caret position - otherwise the caret position is invalid for a short amount
138
			// of time, which can break client code that expects that the caret position is always valid
139
			Caret.ValidateCaretPos();
140
		}
141
		
142
		protected override void OnResize(System.EventArgs e)
143
		{
144
			base.OnResize(e);
145
			ResizeTextArea();
146
		}
147
		
148
		public void ResizeTextArea()
149
		{
150
			int y = 0;
151
			int h = 0;
152
			if (hRuler != null) {
153
				hRuler.Bounds = new Rectangle(0,
154
				                              0,
155
				                              Width - SystemInformation.HorizontalScrollBarArrowWidth,
156
				                              textArea.TextView.FontHeight);
157
				
158
				y = hRuler.Bounds.Bottom;
159
				h = hRuler.Bounds.Height;
160
			}
161
			
162
			textArea.Bounds = new Rectangle(0, y,
163
			                                Width - SystemInformation.HorizontalScrollBarArrowWidth,
164
			                                Height - SystemInformation.VerticalScrollBarArrowHeight - h);
165
			SetScrollBarBounds();
166
		}
167
		
168
		public void SetScrollBarBounds()
169
		{
170
			vScrollBar.Bounds = new Rectangle(textArea.Bounds.Right, 0, SystemInformation.HorizontalScrollBarArrowWidth, Height - SystemInformation.VerticalScrollBarArrowHeight);
171
			hScrollBar.Bounds = new Rectangle(0,
172
			                                  textArea.Bounds.Bottom,
173
			                                  Width - SystemInformation.HorizontalScrollBarArrowWidth,
174
			                                  SystemInformation.VerticalScrollBarArrowHeight);
175
		}
176
		
177
		bool adjustScrollBarsOnNextUpdate;
178
		Point scrollToPosOnNextUpdate;
179
		
180
		void AdjustScrollBarsOnDocumentChange(object sender, DocumentEventArgs e)
181
		{
182
			if (motherTextEditorControl.IsInUpdate == false) {
183
				AdjustScrollBarsClearCache();
184
				AdjustScrollBars();
185
			} else {
186
				adjustScrollBarsOnNextUpdate = true;
187
			}
188
		}
189
		
190
		void AdjustScrollBarsOnCommittedUpdate(object sender, EventArgs e)
191
		{
192
			if (motherTextEditorControl.IsInUpdate == false) {
193
				if (!scrollToPosOnNextUpdate.IsEmpty) {
194
					ScrollTo(scrollToPosOnNextUpdate.Y, scrollToPosOnNextUpdate.X);
195
				}
196
				if (adjustScrollBarsOnNextUpdate) {
197
					AdjustScrollBarsClearCache();
198
					AdjustScrollBars();
199
				}
200
			}
201
		}
202
		
203
		int[] lineLengthCache;
204
		const int LineLengthCacheAdditionalSize = 100;
205
		
206
		void AdjustScrollBarsClearCache()
207
		{
208
			if (lineLengthCache != null) {
209
				if (lineLengthCache.Length < this.Document.TotalNumberOfLines + 2 * LineLengthCacheAdditionalSize) {
210
					lineLengthCache = null;
211
				} else {
212
					Array.Clear(lineLengthCache, 0, lineLengthCache.Length);
213
				}
214
			}
215
		}
216
		
217
		public void AdjustScrollBars()
218
		{
219
			adjustScrollBarsOnNextUpdate = false;
220
			vScrollBar.Minimum = 0;
221
			// number of visible lines in document (folding!)
222
			vScrollBar.Maximum = textArea.MaxVScrollValue;
223
			int max = 0;
224
			
225
			int firstLine = textArea.TextView.FirstVisibleLine;
226
			int lastLine = this.Document.GetFirstLogicalLine(textArea.TextView.FirstPhysicalLine + textArea.TextView.VisibleLineCount);
227
			if (lastLine >= this.Document.TotalNumberOfLines)
228
				lastLine = this.Document.TotalNumberOfLines - 1;
229
			
230
			if (lineLengthCache == null || lineLengthCache.Length <= lastLine) {
231
				lineLengthCache = new int[lastLine + LineLengthCacheAdditionalSize];
232
			}
233
			
234
			for (int lineNumber = firstLine; lineNumber <= lastLine; lineNumber++) {
235
				LineSegment lineSegment = this.Document.GetLineSegment(lineNumber);
236
				if (Document.FoldingManager.IsLineVisible(lineNumber)) {
237
					if (lineLengthCache[lineNumber] > 0) {
238
						max = Math.Max(max, lineLengthCache[lineNumber]);
239
					} else {
240
						int visualLength = textArea.TextView.GetVisualColumnFast(lineSegment, lineSegment.Length);
241
						lineLengthCache[lineNumber] = Math.Max(1, visualLength);
242
						max = Math.Max(max, visualLength);
243
					}
244
				}
245
			}
246
			hScrollBar.Minimum = 0;
247
			hScrollBar.Maximum = (Math.Max(max + 20, textArea.TextView.VisibleColumnCount - 1));
248
			
249
			vScrollBar.LargeChange = Math.Max(0, textArea.TextView.DrawingPosition.Height);
250
			vScrollBar.SmallChange = Math.Max(0, textArea.TextView.FontHeight);
251
			
252
			hScrollBar.LargeChange = Math.Max(0, textArea.TextView.VisibleColumnCount - 1);
253
			hScrollBar.SmallChange = Math.Max(0, (int)textArea.TextView.SpaceWidth);
254
		}
255
		
256
		public void OptionsChanged()
257
		{
258
			textArea.OptionsChanged();
259
			
260
			if (textArea.TextEditorProperties.ShowHorizontalRuler) {
261
				if (hRuler == null) {
262
					hRuler = new HRuler(textArea);
263
					Controls.Add(hRuler);
264
					ResizeTextArea();
265
				} else {
266
					hRuler.Invalidate();
267
				}
268
			} else {
269
				if (hRuler != null) {
270
					Controls.Remove(hRuler);
271
					hRuler.Dispose();
272
					hRuler = null;
273
					ResizeTextArea();
274
				}
275
			}
276
			
277
			AdjustScrollBars();
278
		}
279
		
280
		void VScrollBarValueChanged(object sender, EventArgs e)
281
		{
282
			textArea.VirtualTop = new Point(textArea.VirtualTop.X, vScrollBar.Value);
283
			textArea.Invalidate();
284
			AdjustScrollBars();
285
		}
286
		
287
		void HScrollBarValueChanged(object sender, EventArgs e)
288
		{
289
			textArea.VirtualTop = new Point(hScrollBar.Value * textArea.TextView.WideSpaceWidth, textArea.VirtualTop.Y);
290
			textArea.Invalidate();
291
		}
292
		
293
		public void HandleMouseWheel(MouseEventArgs e)
294
		{
295
			if ((Control.ModifierKeys & Keys.Control) != 0 && TextEditorProperties.MouseWheelTextZoom) {
296
				if (e.Delta > 0) {
297
					motherTextEditorControl.Font = new Font(motherTextEditorControl.Font.Name,
298
					                                        motherTextEditorControl.Font.Size + 1);
299
					
300
				} else {
301
					motherTextEditorControl.Font = new Font(motherTextEditorControl.Font.Name,
302
					                                        Math.Max(6, motherTextEditorControl.Font.Size - 1));
303
					
304
					
305
				}
306
			} else {
307
				int MAX_DELTA  = 120; // basically it's constant now, but could be changed later by MS
308
				int multiplier = Math.Abs(e.Delta) / MAX_DELTA;
309
				
310
				int newValue;
311
				if (System.Windows.Forms.SystemInformation.MouseWheelScrollLines > 0) {
312
					newValue = this.vScrollBar.Value - (TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * Math.Sign(e.Delta) * System.Windows.Forms.SystemInformation.MouseWheelScrollLines * vScrollBar.SmallChange * multiplier;
313
				} else {
314
					newValue = this.vScrollBar.Value - (TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * Math.Sign(e.Delta) * vScrollBar.LargeChange;
315
				}
316
				vScrollBar.Value = Math.Max(vScrollBar.Minimum, Math.Min(vScrollBar.Maximum, newValue));
317
			}
318
		}
319
		
320
		protected override void OnMouseWheel(MouseEventArgs e)
321
		{
322
			base.OnMouseWheel(e);
323
			if (DoHandleMousewheel) {
324
				HandleMouseWheel(e);
325
			}
326
		}
327
		
328
		public void ScrollToCaret()
329
		{
330
			ScrollTo(textArea.Caret.Line, textArea.Caret.Column);
331
		}
332
		
333
		public void ScrollTo(int line, int column)
334
		{
335
			if (motherTextEditorControl.IsInUpdate) {
336
				scrollToPosOnNextUpdate = new Point(column, line);
337
				return;
338
			} else {
339
				scrollToPosOnNextUpdate = Point.Empty;
340
			}
341
			
342
			ScrollTo(line);
343
			
344
			int curCharMin  = (int)(this.hScrollBar.Value - this.hScrollBar.Minimum);
345
			int curCharMax  = curCharMin + textArea.TextView.VisibleColumnCount;
346
			
347
			int pos = textArea.TextView.GetVisualColumn(line, column);
348
			
349
			if (textArea.TextView.VisibleColumnCount < 0) {
350
				hScrollBar.Value = 0;
351
			} else {
352
				if (pos < curCharMin) {
353
					hScrollBar.Value = (int)(Math.Max(0, pos - scrollMarginHeight));
354
				} else {
355
					if (pos > curCharMax) {
356
						hScrollBar.Value = (int)Math.Max(0, Math.Min(hScrollBar.Maximum, (pos - textArea.TextView.VisibleColumnCount + scrollMarginHeight)));
357
					}
358
				}
359
			}
360
		}
361
		
362
		int scrollMarginHeight  = 3;
363
		
364
		/// <summary>
365
		/// Ensure that <paramref name="line"/> is visible.
366
		/// </summary>
367
		public void ScrollTo(int line)
368
		{
369
			line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line));
370
			line = Document.GetVisibleLine(line);
371
			int curLineMin = textArea.TextView.FirstPhysicalLine;
372
			if (textArea.TextView.LineHeightRemainder > 0) {
373
				curLineMin ++;
374
			}
375
			
376
			if (line - scrollMarginHeight + 3 < curLineMin) {
377
				this.vScrollBar.Value =  Math.Max(0, Math.Min(this.vScrollBar.Maximum, (line - scrollMarginHeight + 3) * textArea.TextView.FontHeight)) ;
378
				VScrollBarValueChanged(this, EventArgs.Empty);
379
			} else {
380
				int curLineMax = curLineMin + this.textArea.TextView.VisibleLineCount;
381
				if (line + scrollMarginHeight - 1 > curLineMax) {
382
					if (this.textArea.TextView.VisibleLineCount == 1) {
383
						this.vScrollBar.Value =  Math.Max(0, Math.Min(this.vScrollBar.Maximum, (line - scrollMarginHeight - 1) * textArea.TextView.FontHeight)) ;
384
					} else {
385
						this.vScrollBar.Value = Math.Min(this.vScrollBar.Maximum,
386
						                                 (line - this.textArea.TextView.VisibleLineCount + scrollMarginHeight - 1)* textArea.TextView.FontHeight) ;
387
					}
388
					VScrollBarValueChanged(this, EventArgs.Empty);
389
				}
390
			}
391
		}
392
		
393
		/// <summary>
394
		/// Scroll so that the specified line is centered.
395
		/// </summary>
396
		/// <param name="line">Line to center view on</param>
397
		/// <param name="treshold">If this action would cause scrolling by less than or equal to
398
		/// <paramref name="treshold"/> lines in any direction, don't scroll.
399
		/// Use -1 to always center the view.</param>
400
		public void CenterViewOn(int line, int treshold)
401
		{
402
			line = Math.Max(0, Math.Min(Document.TotalNumberOfLines - 1, line));
403
			// convert line to visible line:
404
			line = Document.GetVisibleLine(line);
405
			// subtract half the visible line count
406
			line -= textArea.TextView.VisibleLineCount / 2;
407
			
408
			int curLineMin = textArea.TextView.FirstPhysicalLine;
409
			if (textArea.TextView.LineHeightRemainder > 0) {
410
				curLineMin ++;
411
			}
412
			if (Math.Abs(curLineMin - line) > treshold) {
413
				// scroll:
414
				this.vScrollBar.Value =  Math.Max(0, Math.Min(this.vScrollBar.Maximum, (line - scrollMarginHeight + 3) * textArea.TextView.FontHeight)) ;
415
				VScrollBarValueChanged(this, EventArgs.Empty);
416
			}
417
		}
418
		
419
		public void JumpTo(int line)
420
		{
421
			line = Math.Min(line, Document.TotalNumberOfLines - 1);
422
			string text = Document.GetText(Document.GetLineSegment(line));
423
			JumpTo(line, text.Length - text.TrimStart().Length);
424
		}
425
		
426
		public void JumpTo(int line, int column)
427
		{
428
			textArea.Focus();
429
			textArea.SelectionManager.ClearSelection();
430
			textArea.Caret.Position = new TextLocation(column, line);
431
			textArea.SetDesiredColumn();
432
			ScrollToCaret();
433
		}
434
		
435
		public event MouseEventHandler ShowContextMenu;
436
		
437
		protected override void WndProc(ref Message m)
438
		{
439
			if (m.Msg == 0x007B) { // handle WM_CONTEXTMENU
440
				if (ShowContextMenu != null) {
441
					long lParam = m.LParam.ToInt64();
442
					int x = unchecked((short)(lParam & 0xffff));
443
					int y = unchecked((short)((lParam & 0xffff0000) >> 16));
444
					if (x == -1 && y == -1) {
445
						Point pos = Caret.ScreenPosition;
446
						ShowContextMenu(this, new MouseEventArgs(MouseButtons.None, 0, pos.X, pos.Y + textArea.TextView.FontHeight, 0));
447
					} else {
448
						Point pos = PointToClient(new Point(x, y));
449
						ShowContextMenu(this, new MouseEventArgs(MouseButtons.Right, 1, pos.X, pos.Y, 0));
450
					}
451
				}
452
			}
453
			base.WndProc(ref m);
454
		}
455
		
456
		protected override void OnEnter(EventArgs e)
457
		{
458
			// SD2-1072 - Make sure the caret line is valid if anyone
459
			// has handlers for the Enter event.
460
			Caret.ValidateCaretPos();
461
			base.OnEnter(e);
462
		}
463
	}
464
}