Code Coverage Statistics for Source File

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

Sequence Point Coverage
N/A
0 of 0
Branch Coverage
N/A
0 of 0
Lines
485
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: 2680 $</version>
6
// </file>
7
8
using System;
9
using System.Collections.Generic;
10
using System.Drawing;
11
using System.Text;
12
using System.Windows.Forms;
13
14
using ICSharpCode.TextEditor.Document;
15
16
namespace ICSharpCode.TextEditor
17
{
18
	/// <summary>
19
	/// This class handles all mouse stuff for a textArea.
20
	/// </summary>
21
	public class TextAreaMouseHandler
22
	{
23
		TextArea  textArea;
24
		bool      doubleclick = false;
25
		bool      clickedOnSelectedText = false;
26
		
27
		MouseButtons button;
28
		
29
		static readonly Point nilPoint = new Point(-1, -1);
30
		Point mousedownpos       = nilPoint;
31
		Point lastmousedownpos   = nilPoint;
32
		
33
		bool gotmousedown = false;
34
		bool dodragdrop = false;
35
		
36
		public TextAreaMouseHandler(TextArea ttextArea)
37
		{
38
			textArea = ttextArea;
39
		}
40
		
41
		public void Attach()
42
		{
43
			textArea.Click       += new EventHandler(TextAreaClick);
44
			textArea.MouseMove   += new MouseEventHandler(TextAreaMouseMove);
45
			
46
			textArea.MouseDown   += new MouseEventHandler(OnMouseDown);
47
			textArea.DoubleClick += new EventHandler(OnDoubleClick);
48
			textArea.MouseLeave  += new EventHandler(OnMouseLeave);
49
			textArea.MouseUp     += new MouseEventHandler(OnMouseUp);
50
			textArea.LostFocus   += new EventHandler(TextAreaLostFocus);
51
			textArea.ToolTipRequest += new ToolTipRequestEventHandler(OnToolTipRequest);
52
		}
53
		
54
		void OnToolTipRequest(object sender, ToolTipRequestEventArgs e)
55
		{
56
			if (e.ToolTipShown)
57
				return;
58
			Point mousepos = e.MousePosition;
59
			FoldMarker marker = textArea.TextView.GetFoldMarkerFromPosition(mousepos.X - textArea.TextView.DrawingPosition.X,
60
			                                                                mousepos.Y - textArea.TextView.DrawingPosition.Y);
61
			if (marker != null && marker.IsFolded) {
62
				StringBuilder sb = new StringBuilder(marker.InnerText);
63
				
64
				// max 10 lines
65
				int endLines = 0;
66
				for (int i = 0; i < sb.Length; ++i) {
67
					if (sb[i] == '\n') {
68
						++endLines;
69
						if (endLines >= 10) {
70
							sb.Remove(i + 1, sb.Length - i - 1);
71
							sb.Append(Environment.NewLine);
72
							sb.Append("...");
73
							break;
74
							
75
						}
76
					}
77
				}
78
				sb.Replace("\t", "    ");
79
				e.ShowToolTip(sb.ToString());
80
				return;
81
			}
82
			
83
			List<TextMarker> markers = textArea.Document.MarkerStrategy.GetMarkers(e.LogicalPosition);
84
			foreach (TextMarker tm in markers) {
85
				if (tm.ToolTip != null) {
86
					e.ShowToolTip(tm.ToolTip.Replace("\t", "    "));
87
					return;
88
				}
89
			}
90
		}
91
		
92
		void ShowHiddenCursorIfMovedOrLeft()
93
		{
94
			textArea.ShowHiddenCursor(!textArea.Focused ||
95
			                          !textArea.ClientRectangle.Contains(textArea.PointToClient(Cursor.Position)));
96
		}
97
		
98
		void TextAreaLostFocus(object sender, EventArgs e)
99
		{
100
			// The call to ShowHiddenCursorIfMovedOrLeft is delayed
101
			// until pending messages have been processed
102
			// so that it can properly detect whether the TextArea
103
			// has really lost focus.
104
			// For example, the CodeCompletionWindow gets focus when it is shown,
105
			// but immediately gives back focus to the TextArea.
106
			textArea.BeginInvoke(new MethodInvoker(ShowHiddenCursorIfMovedOrLeft));
107
		}
108
		
109
		void OnMouseLeave(object sender, EventArgs e)
110
		{
111
			ShowHiddenCursorIfMovedOrLeft();
112
			gotmousedown = false;
113
			mousedownpos = nilPoint;
114
		}
115
		
116
		void OnMouseUp(object sender, MouseEventArgs e)
117
		{
118
			textArea.SelectionManager.selectFrom.where = WhereFrom.None;
119
			gotmousedown = false;
120
			mousedownpos = nilPoint;
121
		}
122
		
123
		void TextAreaClick(object sender, EventArgs e)
124
		{
125
			Point mousepos;
126
			mousepos = textArea.mousepos;
127
			
128
			if (dodragdrop)
129
			{
130
				return;
131
			}
132
133
			if (clickedOnSelectedText && textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y))
134
			{
135
				textArea.SelectionManager.ClearSelection();
136
137
				TextLocation clickPosition = textArea.TextView.GetLogicalPosition(
138
					mousepos.X - textArea.TextView.DrawingPosition.X,
139
					mousepos.Y - textArea.TextView.DrawingPosition.Y);
140
				textArea.Caret.Position = clickPosition;
141
				textArea.SetDesiredColumn();
142
			}
143
		}
144
		
145
		
146
		void TextAreaMouseMove(object sender, MouseEventArgs e)
147
		{
148
			textArea.mousepos = e.Location;
149
150
			// honour the starting selection strategy
151
			switch (textArea.SelectionManager.selectFrom.where)
152
			{
153
				case WhereFrom.Gutter:
154
					ExtendSelectionToMouse();
155
					return;
156
157
				case WhereFrom.TArea:
158
					break;
159
160
			}
161
			textArea.ShowHiddenCursor(false);
162
			if (dodragdrop) {
163
				dodragdrop = false;
164
				return;
165
			}
166
			
167
			doubleclick = false;
168
			textArea.mousepos = new Point(e.X, e.Y);
169
			
170
			if (clickedOnSelectedText) {
171
				if (Math.Abs(mousedownpos.X - e.X) >= SystemInformation.DragSize.Width / 2 ||
172
				    Math.Abs(mousedownpos.Y - e.Y) >= SystemInformation.DragSize.Height / 2)
173
				{
174
					clickedOnSelectedText = false;
175
					ISelection selection = textArea.SelectionManager.GetSelectionAt(textArea.Caret.Offset);
176
					if (selection != null) {
177
						string text = selection.SelectedText;
178
						if (text != null && text.Length > 0) {
179
							DataObject dataObject = new DataObject ();
180
							dataObject.SetData(DataFormats.UnicodeText, true, text);
181
							dataObject.SetData(selection);
182
							dodragdrop = true;
183
							textArea.DoDragDrop(dataObject, DragDropEffects.All);
184
						}
185
					}
186
				}
187
				
188
				return;
189
			}
190
			
191
			if (e.Button == MouseButtons.Left) {
192
				if (gotmousedown && textArea.SelectionManager.selectFrom.where == WhereFrom.TArea)
193
				{
194
					ExtendSelectionToMouse();
195
				}
196
			}
197
		}
198
		
199
		void ExtendSelectionToMouse()
200
		{
201
			Point mousepos;
202
			mousepos = textArea.mousepos;
203
			TextLocation realmousepos = textArea.TextView.GetLogicalPosition(
204
				Math.Max(0, mousepos.X - textArea.TextView.DrawingPosition.X),
205
				mousepos.Y - textArea.TextView.DrawingPosition.Y);
206
			int y = realmousepos.Y;
207
			realmousepos = textArea.Caret.ValidatePosition(realmousepos);
208
			TextLocation oldPos = textArea.Caret.Position;
209
			if (oldPos == realmousepos && textArea.SelectionManager.selectFrom.where != WhereFrom.Gutter)
210
			{
211
				return;
212
			}
213
214
			// the selection is from the gutter
215
			if (textArea.SelectionManager.selectFrom.where == WhereFrom.Gutter) {
216
				if(realmousepos.Y < textArea.SelectionManager.SelectionStart.Y) {
217
					// the selection has moved above the startpoint
218
					textArea.Caret.Position = new TextLocation(0, realmousepos.Y);
219
				} else {
220
					// the selection has moved below the startpoint
221
					textArea.Caret.Position = textArea.SelectionManager.NextValidPosition(realmousepos.Y);
222
				}
223
			} else {
224
				textArea.Caret.Position = realmousepos;
225
			}
226
227
			// moves selection across whole words for double-click initiated selection
228
			if (!minSelection.IsEmpty && textArea.SelectionManager.SelectionCollection.Count > 0 && textArea.SelectionManager.selectFrom.where == WhereFrom.TArea) {
229
				// Extend selection when selection was started with double-click
230
				ISelection selection = textArea.SelectionManager.SelectionCollection[0];
231
				TextLocation min = textArea.SelectionManager.GreaterEqPos(minSelection, maxSelection) ? maxSelection : minSelection;
232
				TextLocation max = textArea.SelectionManager.GreaterEqPos(minSelection, maxSelection) ? minSelection : maxSelection;
233
				if (textArea.SelectionManager.GreaterEqPos(max, realmousepos) && textArea.SelectionManager.GreaterEqPos(realmousepos, min)) {
234
					textArea.SelectionManager.SetSelection(min, max);
235
				} else if (textArea.SelectionManager.GreaterEqPos(max, realmousepos)) {
236
					int moff = textArea.Document.PositionToOffset(realmousepos);
237
					min = textArea.Document.OffsetToPosition(FindWordStart(textArea.Document, moff));
238
					textArea.SelectionManager.SetSelection(min, max);
239
				} else {
240
					int moff = textArea.Document.PositionToOffset(realmousepos);
241
					max = textArea.Document.OffsetToPosition(FindWordEnd(textArea.Document, moff));
242
					textArea.SelectionManager.SetSelection(min, max);
243
				}
244
			} else {
245
				textArea.SelectionManager.ExtendSelection(oldPos, textArea.Caret.Position);
246
			}
247
			textArea.SetDesiredColumn();
248
		}
249
		
250
		void DoubleClickSelectionExtend()
251
		{
252
			Point mousepos;
253
			mousepos = textArea.mousepos;
254
			
255
			textArea.SelectionManager.ClearSelection();
256
			if (textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y))
257
			{
258
				FoldMarker marker = textArea.TextView.GetFoldMarkerFromPosition(mousepos.X - textArea.TextView.DrawingPosition.X,
259
				                                                                mousepos.Y - textArea.TextView.DrawingPosition.Y);
260
				if (marker != null && marker.IsFolded) {
261
					marker.IsFolded = false;
262
					textArea.MotherTextAreaControl.AdjustScrollBars();
263
				}
264
				if (textArea.Caret.Offset < textArea.Document.TextLength) {
265
					switch (textArea.Document.GetCharAt(textArea.Caret.Offset)) {
266
						case '"':
267
							if (textArea.Caret.Offset < textArea.Document.TextLength) {
268
								int next = FindNext(textArea.Document, textArea.Caret.Offset + 1, '"');
269
								minSelection = textArea.Caret.Position;
270
								if (next > textArea.Caret.Offset && next < textArea.Document.TextLength)
271
									next += 1;
272
								maxSelection = textArea.Document.OffsetToPosition(next);
273
							}
274
							break;
275
						default:
276
							minSelection = textArea.Document.OffsetToPosition(FindWordStart(textArea.Document, textArea.Caret.Offset));
277
							maxSelection = textArea.Document.OffsetToPosition(FindWordEnd(textArea.Document, textArea.Caret.Offset));
278
							break;
279
							
280
					}
281
					textArea.Caret.Position = maxSelection;
282
					textArea.SelectionManager.ExtendSelection(minSelection, maxSelection);
283
				}
284
285
				if (textArea.SelectionManager.selectionCollection.Count > 0) {
286
					ISelection selection = textArea.SelectionManager.selectionCollection[0];
287
					
288
					selection.StartPosition = minSelection;
289
					selection.EndPosition = maxSelection;
290
					textArea.SelectionManager.SelectionStart = minSelection;
291
				}
292
293
				// after a double-click selection, the caret is placed correctly,
294
				// but it is not positioned internally.  The effect is when the cursor
295
				// is moved up or down a line, the caret will take on the column first
296
				// clicked on for the double-click
297
				textArea.SetDesiredColumn();
298
299
				// HACK WARNING !!!
300
				// must refresh here, because when a error tooltip is showed and the underlined
301
				// code is double clicked the textArea don't update corrctly, updateline doesn't
302
				// work ... but the refresh does.
303
				// Mike
304
				textArea.Refresh();
305
			}
306
		}
307
308
		void OnMouseDown(object sender, MouseEventArgs e)
309
		{
310
			Point mousepos;
311
			textArea.mousepos = e.Location;
312
			mousepos = e.Location;
313
314
			if (dodragdrop)
315
			{
316
				return;
317
			}
318
			
319
			if (doubleclick) {
320
				doubleclick = false;
321
				return;
322
			}
323
			
324
			if (textArea.TextView.DrawingPosition.Contains(mousepos.X, mousepos.Y)) {
325
				gotmousedown = true;
326
				textArea.SelectionManager.selectFrom.where = WhereFrom.TArea;
327
				button = e.Button;
328
				
329
				// double-click
330
				if (button == MouseButtons.Left && e.Clicks == 2) {
331
					int deltaX   = Math.Abs(lastmousedownpos.X - e.X);
332
					int deltaY   = Math.Abs(lastmousedownpos.Y - e.Y);
333
					if (deltaX <= SystemInformation.DoubleClickSize.Width &&
334
					    deltaY <= SystemInformation.DoubleClickSize.Height) {
335
						DoubleClickSelectionExtend();
336
						lastmousedownpos = new Point(e.X, e.Y);
337
338
						if (textArea.SelectionManager.selectFrom.where == WhereFrom.Gutter) {
339
							if (!minSelection.IsEmpty && !maxSelection.IsEmpty && textArea.SelectionManager.SelectionCollection.Count > 0) {
340
								textArea.SelectionManager.SelectionCollection[0].StartPosition = minSelection;
341
								textArea.SelectionManager.SelectionCollection[0].EndPosition = maxSelection;
342
								textArea.SelectionManager.SelectionStart = minSelection;
343
344
								minSelection = TextLocation.Empty;
345
								maxSelection = TextLocation.Empty;
346
							}
347
						}
348
						return;
349
					}
350
				}
351
				minSelection = TextLocation.Empty;
352
				maxSelection = TextLocation.Empty;
353
				
354
				lastmousedownpos = mousedownpos = new Point(e.X, e.Y);
355
				
356
				if (button == MouseButtons.Left) {
357
					FoldMarker marker = textArea.TextView.GetFoldMarkerFromPosition(mousepos.X - textArea.TextView.DrawingPosition.X,
358
					                                                                mousepos.Y - textArea.TextView.DrawingPosition.Y);
359
					if (marker != null && marker.IsFolded) {
360
						if (textArea.SelectionManager.HasSomethingSelected) {
361
							clickedOnSelectedText = true;
362
						}
363
						
364
						textArea.SelectionManager.SetSelection(new DefaultSelection(textArea.TextView.Document, new TextLocation(marker.StartColumn, marker.StartLine), new TextLocation(marker.EndColumn, marker.EndLine)));
365
						textArea.Focus();
366
						return;
367
					}
368
369
					if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift) {
370
						ExtendSelectionToMouse();
371
					} else {
372
						TextLocation realmousepos = textArea.TextView.GetLogicalPosition(mousepos.X - textArea.TextView.DrawingPosition.X, mousepos.Y - textArea.TextView.DrawingPosition.Y);
373
						clickedOnSelectedText = false;
374
						
375
						int offset = textArea.Document.PositionToOffset(realmousepos);
376
						
377
						if (textArea.SelectionManager.HasSomethingSelected &&
378
						    textArea.SelectionManager.IsSelected(offset)) {
379
							clickedOnSelectedText = true;
380
						} else {
381
							textArea.SelectionManager.ClearSelection();
382
							if (mousepos.Y > 0 && mousepos.Y < textArea.TextView.DrawingPosition.Height) {
383
								TextLocation pos = new TextLocation();
384
								pos.Y = Math.Min(textArea.Document.TotalNumberOfLines - 1,  realmousepos.Y);
385
								pos.X = realmousepos.X;
386
								textArea.Caret.Position = pos;
387
								textArea.SetDesiredColumn();
388
							}
389
						}
390
					}
391
				} else if (button == MouseButtons.Right) {
392
					// Rightclick sets the cursor to the click position unless
393
					// the previous selection was clicked
394
					TextLocation realmousepos = textArea.TextView.GetLogicalPosition(mousepos.X - textArea.TextView.DrawingPosition.X, mousepos.Y - textArea.TextView.DrawingPosition.Y);
395
					int offset = textArea.Document.PositionToOffset(realmousepos);
396
					if (!textArea.SelectionManager.HasSomethingSelected ||
397
					    !textArea.SelectionManager.IsSelected(offset))
398
					{
399
						textArea.SelectionManager.ClearSelection();
400
						if (mousepos.Y > 0 && mousepos.Y < textArea.TextView.DrawingPosition.Height) {
401
							TextLocation pos = new TextLocation();
402
							pos.Y = Math.Min(textArea.Document.TotalNumberOfLines - 1,  realmousepos.Y);
403
							pos.X = realmousepos.X;
404
							textArea.Caret.Position = pos;
405
							textArea.SetDesiredColumn();
406
						}
407
					}
408
				}
409
			}
410
			textArea.Focus();
411
		}
412
		
413
		int FindNext(IDocument document, int offset, char ch)
414
		{
415
			LineSegment line = document.GetLineSegmentForOffset(offset);
416
			int         endPos = line.Offset + line.Length;
417
			
418
			while (offset < endPos && document.GetCharAt(offset) != ch) {
419
				++offset;
420
			}
421
			return offset;
422
		}
423
		
424
		bool IsSelectableChar(char ch)
425
		{
426
			return Char.IsLetterOrDigit(ch) || ch=='_';
427
		}
428
		
429
		int FindWordStart(IDocument document, int offset)
430
		{
431
			LineSegment line = document.GetLineSegmentForOffset(offset);
432
			
433
			if (offset > 0 && Char.IsWhiteSpace(document.GetCharAt(offset - 1)) && Char.IsWhiteSpace(document.GetCharAt(offset))) {
434
				while (offset > line.Offset && Char.IsWhiteSpace(document.GetCharAt(offset - 1))) {
435
					--offset;
436
				}
437
			} else  if (IsSelectableChar(document.GetCharAt(offset)) || (offset > 0 && Char.IsWhiteSpace(document.GetCharAt(offset)) && IsSelectableChar(document.GetCharAt(offset - 1))))  {
438
				while (offset > line.Offset && IsSelectableChar(document.GetCharAt(offset - 1))) {
439
					--offset;
440
				}
441
			} else {
442
				if (offset > 0 && !Char.IsWhiteSpace(document.GetCharAt(offset - 1)) && !IsSelectableChar(document.GetCharAt(offset - 1)) ) {
443
					return Math.Max(0, offset - 1);
444
				}
445
			}
446
			return offset;
447
		}
448
		
449
		int FindWordEnd(IDocument document, int offset)
450
		{
451
			LineSegment line   = document.GetLineSegmentForOffset(offset);
452
			int         endPos = line.Offset + line.Length;
453
			offset = Math.Min(offset, endPos - 1);
454
			
455
			if (IsSelectableChar(document.GetCharAt(offset)))  {
456
				while (offset < endPos && IsSelectableChar(document.GetCharAt(offset))) {
457
					++offset;
458
				}
459
			} else if (Char.IsWhiteSpace(document.GetCharAt(offset))) {
460
				if (offset > 0 && Char.IsWhiteSpace(document.GetCharAt(offset - 1))) {
461
					while (offset < endPos && Char.IsWhiteSpace(document.GetCharAt(offset))) {
462
						++offset;
463
					}
464
				}
465
			} else {
466
				return Math.Max(0, offset + 1);
467
			}
468
			
469
			return offset;
470
		}
471
		TextLocation minSelection = TextLocation.Empty;
472
		TextLocation maxSelection = TextLocation.Empty;
473
		
474
		void OnDoubleClick(object sender, System.EventArgs e)
475
		{
476
			if (dodragdrop) {
477
				return;
478
			}
479
			
480
			textArea.SelectionManager.selectFrom.where = WhereFrom.TArea;
481
			doubleclick = true;
482
			
483
		}
484
	}
485
}