Code Coverage Statistics for Source File
c:\Tools\SD3\src\Libraries\ICSharpCode.TextEditor\Project\Src\Gui\TextArea.cs
|
Sequence Point Coverage
N/A
0 of 0
|
Branch Coverage
N/A
0 of 0
|
Lines
948
|
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.ComponentModel; |
|
11 |
using System.Diagnostics; |
|
12 |
using System.Drawing; |
|
13 |
using System.Drawing.Text; |
|
14 |
using System.Text; |
|
15 |
using System.Windows.Forms; |
|
16 |
||
17 |
using ICSharpCode.TextEditor.Actions; |
|
18 |
using ICSharpCode.TextEditor.Document; |
|
19 |
using ICSharpCode.TextEditor.Gui.CompletionWindow; |
|
20 |
||
21 |
namespace ICSharpCode.TextEditor |
|
22 |
{ |
|
23 |
public delegate bool KeyEventHandler(char ch); |
|
24 |
public delegate bool DialogKeyProcessor(Keys keyData); |
|
25 |
||
26 |
/// <summary> |
|
27 |
/// This class paints the textarea. |
|
28 |
/// </summary> |
|
29 |
[ToolboxItem(false)] |
|
30 |
public class TextArea : Control |
|
31 |
{ |
|
32 |
bool hiddenMouseCursor = false; |
|
33 |
/// <summary> |
|
34 |
/// The position where the mouse cursor was when it was hidden. Sometimes the text editor gets MouseMove |
|
35 |
/// events when typing text even if the mouse is not moved. |
|
36 |
/// </summary> |
|
37 |
Point mouseCursorHidePosition; |
|
38 |
||
39 |
Point virtualTop = new Point(0, 0); |
|
40 |
TextAreaControl motherTextAreaControl; |
|
41 |
TextEditorControl motherTextEditorControl; |
|
42 |
||
43 |
List<BracketHighlightingSheme> bracketshemes = new List<BracketHighlightingSheme>(); |
|
44 |
TextAreaClipboardHandler textAreaClipboardHandler; |
|
45 |
bool autoClearSelection = false; |
|
46 |
||
47 |
List<AbstractMargin> leftMargins = new List<AbstractMargin>(); |
|
48 |
||
49 |
TextView textView; |
|
50 |
GutterMargin gutterMargin; |
|
51 |
FoldMargin foldMargin; |
|
52 |
IconBarMargin iconBarMargin; |
|
53 |
||
54 |
SelectionManager selectionManager; |
|
55 |
Caret caret; |
|
56 |
||
57 |
internal Point mousepos = new Point(0, 0); |
|
58 |
//public Point selectionStartPos = new Point(0,0); |
|
59 |
||
60 |
bool disposed; |
|
61 |
||
62 |
[Browsable(false)] |
|
63 |
public IList<AbstractMargin> LeftMargins { |
|
64 |
get { |
|
65 |
return leftMargins.AsReadOnly(); |
|
66 |
} |
|
67 |
} |
|
68 |
||
69 |
public void InsertLeftMargin(int index, AbstractMargin margin) |
|
70 |
{ |
|
71 |
leftMargins.Insert(index, margin); |
|
72 |
Refresh(); |
|
73 |
} |
|
74 |
||
75 |
public TextEditorControl MotherTextEditorControl { |
|
76 |
get { |
|
77 |
return motherTextEditorControl; |
|
78 |
} |
|
79 |
} |
|
80 |
||
81 |
public TextAreaControl MotherTextAreaControl { |
|
82 |
get { |
|
83 |
return motherTextAreaControl; |
|
84 |
} |
|
85 |
} |
|
86 |
||
87 |
public SelectionManager SelectionManager { |
|
88 |
get { |
|
89 |
return selectionManager; |
|
90 |
} |
|
91 |
} |
|
92 |
||
93 |
public Caret Caret { |
|
94 |
get { |
|
95 |
return caret; |
|
96 |
} |
|
97 |
} |
|
98 |
||
99 |
public TextView TextView { |
|
100 |
get { |
|
101 |
return textView; |
|
102 |
} |
|
103 |
} |
|
104 |
||
105 |
public GutterMargin GutterMargin { |
|
106 |
get { |
|
107 |
return gutterMargin; |
|
108 |
} |
|
109 |
} |
|
110 |
||
111 |
public FoldMargin FoldMargin { |
|
112 |
get { |
|
113 |
return foldMargin; |
|
114 |
} |
|
115 |
} |
|
116 |
||
117 |
public IconBarMargin IconBarMargin { |
|
118 |
get { |
|
119 |
return iconBarMargin; |
|
120 |
} |
|
121 |
} |
|
122 |
||
123 |
public Encoding Encoding { |
|
124 |
get { |
|
125 |
return motherTextEditorControl.Encoding; |
|
126 |
} |
|
127 |
} |
|
128 |
public int MaxVScrollValue { |
|
129 |
get { |
|
130 |
return (Document.GetVisibleLine(Document.TotalNumberOfLines - 1) + 1 + TextView.VisibleLineCount * 2 / 3) * TextView.FontHeight; |
|
131 |
} |
|
132 |
} |
|
133 |
||
134 |
public Point VirtualTop { |
|
135 |
get { |
|
136 |
return virtualTop; |
|
137 |
} |
|
138 |
set { |
|
139 |
Point newVirtualTop = new Point(value.X, Math.Min(MaxVScrollValue, Math.Max(0, value.Y))); |
|
140 |
if (virtualTop != newVirtualTop) { |
|
141 |
virtualTop = newVirtualTop; |
|
142 |
motherTextAreaControl.VScrollBar.Value = virtualTop.Y; |
|
143 |
Invalidate(); |
|
144 |
} |
|
145 |
} |
|
146 |
} |
|
147 |
||
148 |
public bool AutoClearSelection { |
|
149 |
get { |
|
150 |
return autoClearSelection; |
|
151 |
} |
|
152 |
set { |
|
153 |
autoClearSelection = value; |
|
154 |
} |
|
155 |
} |
|
156 |
||
157 |
[Browsable(false)] |
|
158 |
public IDocument Document { |
|
159 |
get { |
|
160 |
return motherTextEditorControl.Document; |
|
161 |
} |
|
162 |
} |
|
163 |
||
164 |
public TextAreaClipboardHandler ClipboardHandler { |
|
165 |
get { |
|
166 |
return textAreaClipboardHandler; |
|
167 |
} |
|
168 |
} |
|
169 |
||
170 |
||
171 |
public ITextEditorProperties TextEditorProperties { |
|
172 |
get { |
|
173 |
return motherTextEditorControl.TextEditorProperties; |
|
174 |
} |
|
175 |
} |
|
176 |
||
177 |
public TextArea(TextEditorControl motherTextEditorControl, TextAreaControl motherTextAreaControl) |
|
178 |
{ |
|
179 |
this.motherTextAreaControl = motherTextAreaControl; |
|
180 |
this.motherTextEditorControl = motherTextEditorControl; |
|
181 |
||
182 |
caret = new Caret(this); |
|
183 |
selectionManager = new SelectionManager(Document, this); |
|
184 |
||
185 |
this.textAreaClipboardHandler = new TextAreaClipboardHandler(this); |
|
186 |
||
187 |
ResizeRedraw = true; |
|
188 |
||
189 |
SetStyle(ControlStyles.OptimizedDoubleBuffer, true); |
|
190 |
// SetStyle(ControlStyles.AllPaintingInWmPaint, true); |
|
191 |
// SetStyle(ControlStyles.UserPaint, true); |
|
192 |
SetStyle(ControlStyles.Opaque, false); |
|
193 |
SetStyle(ControlStyles.ResizeRedraw, true); |
|
194 |
SetStyle(ControlStyles.Selectable, true); |
|
195 |
||
196 |
textView = new TextView(this); |
|
197 |
||
198 |
gutterMargin = new GutterMargin(this); |
|
199 |
foldMargin = new FoldMargin(this); |
|
200 |
iconBarMargin = new IconBarMargin(this); |
|
201 |
leftMargins.AddRange(new AbstractMargin[] { iconBarMargin, gutterMargin, foldMargin }); |
|
202 |
OptionsChanged(); |
|
203 |
||
204 |
||
205 |
new TextAreaMouseHandler(this).Attach(); |
|
206 |
new TextAreaDragDropHandler().Attach(this); |
|
207 |
||
208 |
bracketshemes.Add(new BracketHighlightingSheme('{', '}')); |
|
209 |
bracketshemes.Add(new BracketHighlightingSheme('(', ')')); |
|
210 |
bracketshemes.Add(new BracketHighlightingSheme('[', ']')); |
|
211 |
||
212 |
caret.PositionChanged += new EventHandler(SearchMatchingBracket); |
|
213 |
Document.TextContentChanged += new EventHandler(TextContentChanged); |
|
214 |
Document.FoldingManager.FoldingsChanged += new EventHandler(DocumentFoldingsChanged); |
|
215 |
} |
|
216 |
||
217 |
public void UpdateMatchingBracket() |
|
218 |
{ |
|
219 |
SearchMatchingBracket(null, null); |
|
220 |
} |
|
221 |
||
222 |
void TextContentChanged(object sender, EventArgs e) |
|
223 |
{ |
|
224 |
Caret.Position = new TextLocation(0, 0); |
|
225 |
SelectionManager.SelectionCollection.Clear(); |
|
226 |
} |
|
227 |
void SearchMatchingBracket(object sender, EventArgs e) |
|
228 |
{ |
|
229 |
if (!TextEditorProperties.ShowMatchingBracket) { |
|
230 |
textView.Highlight = null; |
|
231 |
return; |
|
232 |
} |
|
233 |
int oldLine1 = -1, oldLine2 = -1; |
|
234 |
if (textView.Highlight != null && textView.Highlight.OpenBrace.Y >=0 && textView.Highlight.OpenBrace.Y < Document.TotalNumberOfLines) { |
|
235 |
oldLine1 = textView.Highlight.OpenBrace.Y; |
|
236 |
} |
|
237 |
if (textView.Highlight != null && textView.Highlight.CloseBrace.Y >=0 && textView.Highlight.CloseBrace.Y < Document.TotalNumberOfLines) { |
|
238 |
oldLine2 = textView.Highlight.CloseBrace.Y; |
|
239 |
} |
|
240 |
textView.Highlight = FindMatchingBracketHighlight(); |
|
241 |
if (oldLine1 >= 0) |
|
242 |
UpdateLine(oldLine1); |
|
243 |
if (oldLine2 >= 0 && oldLine2 != oldLine1) |
|
244 |
UpdateLine(oldLine2); |
|
245 |
if (textView.Highlight != null) { |
|
246 |
int newLine1 = textView.Highlight.OpenBrace.Y; |
|
247 |
int newLine2 = textView.Highlight.CloseBrace.Y; |
|
248 |
if (newLine1 != oldLine1 && newLine1 != oldLine2) |
|
249 |
UpdateLine(newLine1); |
|
250 |
if (newLine2 != oldLine1 && newLine2 != oldLine2 && newLine2 != newLine1) |
|
251 |
UpdateLine(newLine2); |
|
252 |
} |
|
253 |
} |
|
254 |
||
255 |
public Highlight FindMatchingBracketHighlight() |
|
256 |
{ |
|
257 |
if (Caret.Offset == 0) |
|
258 |
return null; |
|
259 |
foreach (BracketHighlightingSheme bracketsheme in bracketshemes) { |
|
260 |
Highlight highlight = bracketsheme.GetHighlight(Document, Caret.Offset - 1); |
|
261 |
if (highlight != null) { |
|
262 |
return highlight; |
|
263 |
} |
|
264 |
} |
|
265 |
return null; |
|
266 |
} |
|
267 |
||
268 |
public void SetDesiredColumn() |
|
269 |
{ |
|
270 |
Caret.DesiredColumn = TextView.GetDrawingXPos(Caret.Line, Caret.Column) + VirtualTop.X; |
|
271 |
} |
|
272 |
||
273 |
public void SetCaretToDesiredColumn() |
|
274 |
{ |
|
275 |
FoldMarker dummy; |
|
276 |
Caret.Position = textView.GetLogicalColumn(Caret.Line, Caret.DesiredColumn + VirtualTop.X, out dummy); |
|
277 |
} |
|
278 |
||
279 |
public void OptionsChanged() |
|
280 |
{ |
|
281 |
UpdateMatchingBracket(); |
|
282 |
textView.OptionsChanged(); |
|
283 |
caret.RecreateCaret(); |
|
284 |
caret.UpdateCaretPosition(); |
|
285 |
Refresh(); |
|
286 |
} |
|
287 |
||
288 |
AbstractMargin lastMouseInMargin; |
|
289 |
||
290 |
protected override void OnMouseLeave(System.EventArgs e) |
|
291 |
{ |
|
292 |
base.OnMouseLeave(e); |
|
293 |
this.Cursor = Cursors.Default; |
|
294 |
if (lastMouseInMargin != null) { |
|
295 |
lastMouseInMargin.HandleMouseLeave(EventArgs.Empty); |
|
296 |
lastMouseInMargin = null; |
|
297 |
} |
|
298 |
CloseToolTip(); |
|
299 |
} |
|
300 |
||
301 |
protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) |
|
302 |
{ |
|
303 |
// this corrects weird problems when text is selected, |
|
304 |
// then a menu item is selected, then the text is |
|
305 |
// clicked again - it correctly synchronises the |
|
306 |
// click position |
|
307 |
mousepos = new Point(e.X, e.Y); |
|
308 |
||
309 |
base.OnMouseDown(e); |
|
310 |
CloseToolTip(); |
|
311 |
||
312 |
foreach (AbstractMargin margin in leftMargins) { |
|
313 |
if (margin.DrawingPosition.Contains(e.X, e.Y)) { |
|
314 |
margin.HandleMouseDown(new Point(e.X, e.Y), e.Button); |
|
315 |
} |
|
316 |
} |
|
317 |
} |
|
318 |
||
319 |
/// <summary> |
|
320 |
/// Shows the mouse cursor if it has been hidden. |
|
321 |
/// </summary> |
|
322 |
/// <param name="forceShow"><c>true</c> to always show the cursor or <c>false</c> to show it only if it has been moved since it was hidden.</param> |
|
323 |
internal void ShowHiddenCursor(bool forceShow) |
|
324 |
{ |
|
325 |
if (hiddenMouseCursor) { |
|
326 |
if (mouseCursorHidePosition != Cursor.Position || forceShow) { |
|
327 |
Cursor.Show(); |
|
328 |
hiddenMouseCursor = false; |
|
329 |
} |
|
330 |
} |
|
331 |
} |
|
332 |
||
333 |
||
334 |
// static because the mouse can only be in one text area and we don't want to have |
|
335 |
// tooltips of text areas from inactive tabs floating around. |
|
336 |
static DeclarationViewWindow toolTip; |
|
337 |
static string oldToolTip; |
|
338 |
||
339 |
void SetToolTip(string text, int lineNumber) |
|
340 |
{ |
|
341 |
if (toolTip == null || toolTip.IsDisposed) |
|
342 |
toolTip = new DeclarationViewWindow(this.FindForm()); |
|
343 |
if (oldToolTip == text) |
|
344 |
return; |
|
345 |
if (text == null) { |
|
346 |
toolTip.Hide(); |
|
347 |
} else { |
|
348 |
Point p = Control.MousePosition; |
|
349 |
Point cp = PointToClient(p); |
|
350 |
if (lineNumber >= 0) { |
|
351 |
lineNumber = this.Document.GetVisibleLine(lineNumber); |
|
352 |
p.Y = (p.Y - cp.Y) + (lineNumber * this.TextView.FontHeight) - this.virtualTop.Y; |
|
353 |
} |
|
354 |
p.Offset(3, 3); |
|
355 |
toolTip.Location = p; |
|
356 |
toolTip.Description = text; |
|
357 |
toolTip.HideOnClick = true; |
|
358 |
toolTip.Show(); |
|
359 |
} |
|
360 |
oldToolTip = text; |
|
361 |
} |
|
362 |
||
363 |
public event ToolTipRequestEventHandler ToolTipRequest; |
|
364 |
||
365 |
protected virtual void OnToolTipRequest(ToolTipRequestEventArgs e) |
|
366 |
{ |
|
367 |
if (ToolTipRequest != null) { |
|
368 |
ToolTipRequest(this, e); |
|
369 |
} |
|
370 |
} |
|
371 |
||
372 |
bool toolTipActive; |
|
373 |
/// <summary> |
|
374 |
/// Rectangle in text area that caused the current tool tip. |
|
375 |
/// Prevents tooltip from re-showing when it was closed because of a click or keyboard |
|
376 |
/// input and the mouse was not used. |
|
377 |
/// </summary> |
|
378 |
Rectangle toolTipRectangle; |
|
379 |
||
380 |
void CloseToolTip() |
|
381 |
{ |
|
382 |
if (toolTipActive) { |
|
383 |
//Console.WriteLine("Closing tooltip"); |
|
384 |
toolTipActive = false; |
|
385 |
SetToolTip(null, -1); |
|
386 |
} |
|
387 |
ResetMouseEventArgs(); |
|
388 |
} |
|
389 |
||
390 |
protected override void OnMouseHover(EventArgs e) |
|
391 |
{ |
|
392 |
base.OnMouseHover(e); |
|
393 |
//Console.WriteLine("Hover raised at " + PointToClient(Control.MousePosition)); |
|
394 |
if (MouseButtons == MouseButtons.None) { |
|
395 |
RequestToolTip(PointToClient(Control.MousePosition)); |
|
396 |
} else { |
|
397 |
CloseToolTip(); |
|
398 |
} |
|
399 |
} |
|
400 |
||
401 |
protected void RequestToolTip(Point mousePos) |
|
402 |
{ |
|
403 |
if (toolTipRectangle.Contains(mousePos)) { |
|
404 |
if (!toolTipActive) |
|
405 |
ResetMouseEventArgs(); |
|
406 |
return; |
|
407 |
} |
|
408 |
||
409 |
//Console.WriteLine("Request tooltip for " + mousePos); |
|
410 |
||
411 |
toolTipRectangle = new Rectangle(mousePos.X - 4, mousePos.Y - 4, 8, 8); |
|
412 |
||
413 |
TextLocation logicPos = textView.GetLogicalPosition(mousePos.X - textView.DrawingPosition.Left, |
|
414 |
mousePos.Y - textView.DrawingPosition.Top); |
|
415 |
bool inDocument = textView.DrawingPosition.Contains(mousePos) |
|
416 |
&& logicPos.Y >= 0 && logicPos.Y < Document.TotalNumberOfLines; |
|
417 |
ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(mousePos, logicPos, inDocument); |
|
418 |
OnToolTipRequest(args); |
|
419 |
if (args.ToolTipShown) { |
|
420 |
//Console.WriteLine("Set tooltip to " + args.toolTipText); |
|
421 |
toolTipActive = true; |
|
422 |
SetToolTip(args.toolTipText, inDocument ? logicPos.Y + 1 : -1); |
|
423 |
} else { |
|
424 |
CloseToolTip(); |
|
425 |
} |
|
426 |
} |
|
427 |
||
428 |
// external interface to the attached event |
|
429 |
internal void RaiseMouseMove(MouseEventArgs e) |
|
430 |
{ |
|
431 |
OnMouseMove(e); |
|
432 |
} |
|
433 |
||
434 |
protected override void OnMouseMove(MouseEventArgs e) |
|
435 |
{ |
|
436 |
base.OnMouseMove(e); |
|
437 |
if (!toolTipRectangle.Contains(e.Location)) { |
|
438 |
toolTipRectangle = Rectangle.Empty; |
|
439 |
if (toolTipActive) |
|
440 |
RequestToolTip(e.Location); |
|
441 |
} |
|
442 |
foreach (AbstractMargin margin in leftMargins) { |
|
443 |
if (margin.DrawingPosition.Contains(e.X, e.Y)) { |
|
444 |
this.Cursor = margin.Cursor; |
|
445 |
margin.HandleMouseMove(new Point(e.X, e.Y), e.Button); |
|
446 |
if (lastMouseInMargin != margin) { |
|
447 |
if (lastMouseInMargin != null) { |
|
448 |
lastMouseInMargin.HandleMouseLeave(EventArgs.Empty); |
|
449 |
} |
|
450 |
lastMouseInMargin = margin; |
|
451 |
} |
|
452 |
return; |
|
453 |
} |
|
454 |
} |
|
455 |
if (lastMouseInMargin != null) { |
|
456 |
lastMouseInMargin.HandleMouseLeave(EventArgs.Empty); |
|
457 |
lastMouseInMargin = null; |
|
458 |
} |
|
459 |
if (textView.DrawingPosition.Contains(e.X, e.Y)) { |
|
460 |
TextLocation realmousepos = TextView.GetLogicalPosition(e.X - TextView.DrawingPosition.X, e.Y - TextView.DrawingPosition.Y); |
|
461 |
if(SelectionManager.IsSelected(Document.PositionToOffset(realmousepos)) && MouseButtons == MouseButtons.None) { |
|
462 |
// mouse is hovering over a selection, so show default mouse |
|
463 |
this.Cursor = Cursors.Default; |
|
464 |
} else { |
|
465 |
// mouse is hovering over text area, not a selection, so show the textView cursor |
|
466 |
this.Cursor = textView.Cursor; |
|
467 |
} |
|
468 |
return; |
|
469 |
} |
|
470 |
this.Cursor = Cursors.Default; |
|
471 |
} |
|
472 |
AbstractMargin updateMargin = null; |
|
473 |
||
474 |
public void Refresh(AbstractMargin margin) |
|
475 |
{ |
|
476 |
updateMargin = margin; |
|
477 |
Invalidate(updateMargin.DrawingPosition); |
|
478 |
Update(); |
|
479 |
updateMargin = null; |
|
480 |
} |
|
481 |
||
482 |
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent) |
|
483 |
{ |
|
484 |
} |
|
485 |
||
486 |
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) |
|
487 |
{ |
|
488 |
int currentXPos = 0; |
|
489 |
int currentYPos = 0; |
|
490 |
bool adjustScrollBars = false; |
|
491 |
Graphics g = e.Graphics; |
|
492 |
Rectangle clipRectangle = e.ClipRectangle; |
|
493 |
||
494 |
bool isFullRepaint = clipRectangle.X == 0 && clipRectangle.Y == 0 |
|
495 |
&& clipRectangle.Width == this.Width && clipRectangle.Height == this.Height; |
|
496 |
||
497 |
g.TextRenderingHint = this.TextEditorProperties.TextRenderingHint; |
|
498 |
||
499 |
if (updateMargin != null) { |
|
500 |
updateMargin.Paint(g, updateMargin.DrawingPosition); |
|
501 |
// clipRectangle.Intersect(updateMargin.DrawingPosition); |
|
502 |
} |
|
503 |
||
504 |
if (clipRectangle.Width <= 0 || clipRectangle.Height <= 0) { |
|
505 |
return; |
|
506 |
} |
|
507 |
||
508 |
foreach (AbstractMargin margin in leftMargins) { |
|
509 |
if (margin.IsVisible) { |
|
510 |
Rectangle marginRectangle = new Rectangle(currentXPos , currentYPos, margin.Size.Width, Height - currentYPos); |
|
511 |
if (marginRectangle != margin.DrawingPosition) { |
|
512 |
// margin changed size |
|
513 |
if (!isFullRepaint && !clipRectangle.Contains(marginRectangle)) { |
|
514 |
Invalidate(); // do a full repaint |
|
515 |
} |
|
516 |
adjustScrollBars = true; |
|
517 |
margin.DrawingPosition = marginRectangle; |
|
518 |
} |
|
519 |
currentXPos += margin.DrawingPosition.Width; |
|
520 |
if (clipRectangle.IntersectsWith(marginRectangle)) { |
|
521 |
marginRectangle.Intersect(clipRectangle); |
|
522 |
if (!marginRectangle.IsEmpty) { |
|
523 |
margin.Paint(g, marginRectangle); |
|
524 |
} |
|
525 |
} |
|
526 |
} |
|
527 |
} |
|
528 |
||
529 |
Rectangle textViewArea = new Rectangle(currentXPos, currentYPos, Width - currentXPos, Height - currentYPos); |
|
530 |
if (textViewArea != textView.DrawingPosition) { |
|
531 |
adjustScrollBars = true; |
|
532 |
textView.DrawingPosition = textViewArea; |
|
533 |
} |
|
534 |
if (clipRectangle.IntersectsWith(textViewArea)) { |
|
535 |
textViewArea.Intersect(clipRectangle); |
|
536 |
if (!textViewArea.IsEmpty) { |
|
537 |
textView.Paint(g, textViewArea); |
|
538 |
} |
|
539 |
} |
|
540 |
||
541 |
if (adjustScrollBars) { |
|
542 |
this.motherTextAreaControl.AdjustScrollBars(); |
|
543 |
} |
|
544 |
||
545 |
Caret.UpdateCaretPosition(); |
|
546 |
||
547 |
base.OnPaint(e); |
|
548 |
} |
|
549 |
void DocumentFoldingsChanged(object sender, EventArgs e) |
|
550 |
{ |
|
551 |
Invalidate(); |
|
552 |
this.motherTextAreaControl.AdjustScrollBars(); |
|
553 |
} |
|
554 |
||
555 |
#region keyboard handling methods |
|
556 |
||
557 |
/// <summary> |
|
558 |
/// This method is called on each Keypress |
|
559 |
/// </summary> |
|
560 |
/// <returns> |
|
561 |
/// True, if the key is handled by this method and should NOT be |
|
562 |
/// inserted in the textarea. |
|
563 |
/// </returns> |
|
564 |
protected internal virtual bool HandleKeyPress(char ch) |
|
565 |
{ |
|
566 |
if (KeyEventHandler != null) { |
|
567 |
return KeyEventHandler(ch); |
|
568 |
} |
|
569 |
return false; |
|
570 |
} |
|
571 |
||
572 |
// Fixes SD2-747: Form containing the text editor and a button with a shortcut |
|
573 |
protected override bool IsInputChar(char charCode) |
|
574 |
{ |
|
575 |
return true; |
|
576 |
} |
|
577 |
||
578 |
public void SimulateKeyPress(char ch) |
|
579 |
{ |
|
580 |
if (Document.ReadOnly) { |
|
581 |
return; |
|
582 |
} |
|
583 |
||
584 |
if (TextEditorProperties.UseCustomLine == true) { |
|
585 |
if (SelectionManager.HasSomethingSelected) { |
|
586 |
if (SelectionManager.SelectionIsReadonly) |
|
587 |
return; |
|
588 |
} else if (Document.CustomLineManager.IsReadOnly(Caret.Line, false) == true) |
|
589 |
return; |
|
590 |
} |
|
591 |
||
592 |
if (ch < ' ') { |
|
593 |
return; |
|
594 |
} |
|
595 |
||
596 |
if (!hiddenMouseCursor && TextEditorProperties.HideMouseCursor) { |
|
597 |
if (this.ClientRectangle.Contains(PointToClient(Cursor.Position))) { |
|
598 |
mouseCursorHidePosition = Cursor.Position; |
|
599 |
hiddenMouseCursor = true; |
|
600 |
Cursor.Hide(); |
|
601 |
} |
|
602 |
} |
|
603 |
CloseToolTip(); |
|
604 |
||
605 |
motherTextEditorControl.BeginUpdate(); |
|
606 |
Document.UndoStack.StartUndoGroup(); |
|
607 |
// INSERT char |
|
608 |
if (!HandleKeyPress(ch)) { |
|
609 |
switch (Caret.CaretMode) { |
|
610 |
case CaretMode.InsertMode: |
|
611 |
InsertChar(ch); |
|
612 |
break; |
|
613 |
case CaretMode.OverwriteMode: |
|
614 |
ReplaceChar(ch); |
|
615 |
break; |
|
616 |
default: |
|
617 |
Debug.Assert(false, "Unknown caret mode " + Caret.CaretMode); |
|
618 |
break; |
|
619 |
} |
|
620 |
} |
|
621 |
||
622 |
int currentLineNr = Caret.Line; |
|
623 |
Document.FormattingStrategy.FormatLine(this, currentLineNr, Document.PositionToOffset(Caret.Position), ch); |
|
624 |
||
625 |
motherTextEditorControl.EndUpdate(); |
|
626 |
Document.UndoStack.EndUndoGroup(); |
|
627 |
} |
|
628 |
||
629 |
protected override void OnKeyPress(KeyPressEventArgs e) |
|
630 |
{ |
|
631 |
base.OnKeyPress(e); |
|
632 |
SimulateKeyPress(e.KeyChar); |
|
633 |
e.Handled = true; |
|
634 |
} |
|
635 |
||
636 |
/// <summary> |
|
637 |
/// This method executes a dialog key |
|
638 |
/// </summary> |
|
639 |
public bool ExecuteDialogKey(Keys keyData) |
|
640 |
{ |
|
641 |
// try, if a dialog key processor was set to use this |
|
642 |
if (DoProcessDialogKey != null && DoProcessDialogKey(keyData)) { |
|
643 |
return true; |
|
644 |
} |
|
645 |
||
646 |
if (keyData == Keys.Back || keyData == Keys.Delete || keyData == Keys.Enter) { |
|
647 |
if (TextEditorProperties.UseCustomLine == true) { |
|
648 |
if (SelectionManager.HasSomethingSelected) { |
|
649 |
if (SelectionManager.SelectionIsReadonly) |
|
650 |
return true; |
|
651 |
} else { |
|
652 |
int curLineNr = Document.GetLineNumberForOffset(Caret.Offset); |
|
653 |
if (Document.CustomLineManager.IsReadOnly(curLineNr, false) == true) |
|
654 |
return true; |
|
655 |
if ((Caret.Column == 0) && (curLineNr - 1 >= 0) && keyData == Keys.Back && |
|
656 |
Document.CustomLineManager.IsReadOnly(curLineNr - 1, false) == true) |
|
657 |
return true; |
|
658 |
if (keyData == Keys.Delete) { |
|
659 |
LineSegment curLine = Document.GetLineSegment(curLineNr); |
|
660 |
if (curLine.Offset + curLine.Length == Caret.Offset && |
|
661 |
Document.CustomLineManager.IsReadOnly(curLineNr + 1, false) == true) { |
|
662 |
return true; |
|
663 |
} |
|
664 |
} |
|
665 |
} |
|
666 |
} |
|
667 |
} |
|
668 |
||
669 |
// if not (or the process was 'silent', use the standard edit actions |
|
670 |
IEditAction action = motherTextEditorControl.GetEditAction(keyData); |
|
671 |
AutoClearSelection = true; |
|
672 |
if (action != null) { |
|
673 |
motherTextEditorControl.BeginUpdate(); |
|
674 |
try { |
|
675 |
lock (Document) { |
|
676 |
action.Execute(this); |
|
677 |
if (SelectionManager.HasSomethingSelected && AutoClearSelection /*&& caretchanged*/) { |
|
678 |
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal) { |
|
679 |
SelectionManager.ClearSelection(); |
|
680 |
} |
|
681 |
} |
|
682 |
} |
|
683 |
} finally { |
|
684 |
motherTextEditorControl.EndUpdate(); |
|
685 |
Caret.UpdateCaretPosition(); |
|
686 |
} |
|
687 |
return true; |
|
688 |
} |
|
689 |
return false; |
|
690 |
} |
|
691 |
||
692 |
protected override bool ProcessDialogKey(Keys keyData) |
|
693 |
{ |
|
694 |
return ExecuteDialogKey(keyData) || base.ProcessDialogKey(keyData); |
|
695 |
} |
|
696 |
#endregion |
|
697 |
||
698 |
public void ScrollToCaret() |
|
699 |
{ |
|
700 |
motherTextAreaControl.ScrollToCaret(); |
|
701 |
} |
|
702 |
||
703 |
public void ScrollTo(int line) |
|
704 |
{ |
|
705 |
motherTextAreaControl.ScrollTo(line); |
|
706 |
} |
|
707 |
||
708 |
public void BeginUpdate() |
|
709 |
{ |
|
710 |
motherTextEditorControl.BeginUpdate(); |
|
711 |
} |
|
712 |
||
713 |
public void EndUpdate() |
|
714 |
{ |
|
715 |
motherTextEditorControl.EndUpdate(); |
|
716 |
} |
|
717 |
||
718 |
public bool EnableCutOrPaste { |
|
719 |
get { |
|
720 |
if (motherTextAreaControl == null) |
|
721 |
return false; |
|
722 |
if (TextEditorProperties.UseCustomLine == true) { |
|
723 |
if (SelectionManager.HasSomethingSelected == true) { |
|
724 |
if (SelectionManager.SelectionIsReadonly) |
|
725 |
return false; |
|
726 |
} |
|
727 |
if (Document.CustomLineManager.IsReadOnly(Caret.Line, false) == true) |
|
728 |
return false; |
|
729 |
} |
|
730 |
return true; |
|
731 |
||
732 |
} |
|
733 |
} |
|
734 |
||
735 |
string GenerateWhitespaceString(int length) |
|
736 |
{ |
|
737 |
return new String(' ', length); |
|
738 |
} |
|
739 |
/// <remarks> |
|
740 |
/// Inserts a single character at the caret position |
|
741 |
/// </remarks> |
|
742 |
public void InsertChar(char ch) |
|
743 |
{ |
|
744 |
bool updating = motherTextEditorControl.IsInUpdate; |
|
745 |
if (!updating) { |
|
746 |
BeginUpdate(); |
|
747 |
} |
|
748 |
||
749 |
// filter out forgein whitespace chars and replace them with standard space (ASCII 32) |
|
750 |
if (Char.IsWhiteSpace(ch) && ch != '\t' && ch != '\n') { |
|
751 |
ch = ' '; |
|
752 |
} |
|
753 |
||
754 |
Document.UndoStack.StartUndoGroup(); |
|
755 |
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && |
|
756 |
SelectionManager.SelectionCollection.Count > 0) { |
|
757 |
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition; |
|
758 |
SelectionManager.RemoveSelectedText(); |
|
759 |
} |
|
760 |
LineSegment caretLine = Document.GetLineSegment(Caret.Line); |
|
761 |
int offset = Caret.Offset; |
|
762 |
// use desired column for generated whitespaces |
|
763 |
int dc=Math.Min(Caret.Column,Caret.DesiredColumn); |
|
764 |
if (caretLine.Length < dc && ch != '\n') { |
|
765 |
Document.Insert(offset, GenerateWhitespaceString(dc - caretLine.Length) + ch); |
|
766 |
} else { |
|
767 |
Document.Insert(offset, ch.ToString()); |
|
768 |
} |
|
769 |
Document.UndoStack.EndUndoGroup(); |
|
770 |
++Caret.Column; |
|
771 |
||
772 |
if (!updating) { |
|
773 |
EndUpdate(); |
|
774 |
UpdateLineToEnd(Caret.Line, Caret.Column); |
|
775 |
} |
|
776 |
||
777 |
// I prefer to set NOT the standard column, if you type something |
|
778 |
// ++Caret.DesiredColumn; |
|
779 |
} |
|
780 |
||
781 |
/// <remarks> |
|
782 |
/// Inserts a whole string at the caret position |
|
783 |
/// </remarks> |
|
784 |
public void InsertString(string str) |
|
785 |
{ |
|
786 |
bool updating = motherTextEditorControl.IsInUpdate; |
|
787 |
if (!updating) { |
|
788 |
BeginUpdate(); |
|
789 |
} |
|
790 |
try { |
|
791 |
Document.UndoStack.StartUndoGroup(); |
|
792 |
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && |
|
793 |
SelectionManager.SelectionCollection.Count > 0) { |
|
794 |
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition; |
|
795 |
SelectionManager.RemoveSelectedText(); |
|
796 |
} |
|
797 |
||
798 |
int oldOffset = Document.PositionToOffset(Caret.Position); |
|
799 |
int oldLine = Caret.Line; |
|
800 |
LineSegment caretLine = Document.GetLineSegment(Caret.Line); |
|
801 |
if (caretLine.Length < Caret.Column) { |
|
802 |
int whiteSpaceLength = Caret.Column - caretLine.Length; |
|
803 |
Document.Insert(oldOffset, GenerateWhitespaceString(whiteSpaceLength) + str); |
|
804 |
Caret.Position = Document.OffsetToPosition(oldOffset + str.Length + whiteSpaceLength); |
|
805 |
} else { |
|
806 |
Document.Insert(oldOffset, str); |
|
807 |
Caret.Position = Document.OffsetToPosition(oldOffset + str.Length); |
|
808 |
} |
|
809 |
Document.UndoStack.EndUndoGroup(); |
|
810 |
if (oldLine != Caret.Line) { |
|
811 |
UpdateToEnd(oldLine); |
|
812 |
} else { |
|
813 |
UpdateLineToEnd(Caret.Line, Caret.Column); |
|
814 |
} |
|
815 |
} finally { |
|
816 |
if (!updating) { |
|
817 |
EndUpdate(); |
|
818 |
} |
|
819 |
} |
|
820 |
} |
|
821 |
||
822 |
/// <remarks> |
|
823 |
/// Replaces a char at the caret position |
|
824 |
/// </remarks> |
|
825 |
public void ReplaceChar(char ch) |
|
826 |
{ |
|
827 |
bool updating = motherTextEditorControl.IsInUpdate; |
|
828 |
if (!updating) { |
|
829 |
BeginUpdate(); |
|
830 |
} |
|
831 |
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && SelectionManager.SelectionCollection.Count > 0) { |
|
832 |
Caret.Position = SelectionManager.SelectionCollection[0].StartPosition; |
|
833 |
SelectionManager.RemoveSelectedText(); |
|
834 |
} |
|
835 |
||
836 |
int lineNr = Caret.Line; |
|
837 |
LineSegment line = Document.GetLineSegment(lineNr); |
|
838 |
int offset = Document.PositionToOffset(Caret.Position); |
|
839 |
if (offset < line.Offset + line.Length) { |
|
840 |
Document.Replace(offset, 1, ch.ToString()); |
|
841 |
} else { |
|
842 |
Document.Insert(offset, ch.ToString()); |
|
843 |
} |
|
844 |
if (!updating) { |
|
845 |
EndUpdate(); |
|
846 |
UpdateLineToEnd(lineNr, Caret.Column); |
|
847 |
} |
|
848 |
++Caret.Column; |
|
849 |
// ++Caret.DesiredColumn; |
|
850 |
} |
|
851 |
||
852 |
protected override void Dispose(bool disposing) |
|
853 |
{ |
|
854 |
base.Dispose(disposing); |
|
855 |
if (disposing) { |
|
856 |
if (!disposed) { |
|
857 |
disposed = true; |
|
858 |
if (caret != null) { |
|
859 |
caret.PositionChanged -= new EventHandler(SearchMatchingBracket); |
|
860 |
caret.Dispose(); |
|
861 |
} |
|
862 |
if (selectionManager != null) { |
|
863 |
selectionManager.Dispose(); |
|
864 |
} |
|
865 |
Document.TextContentChanged -= new EventHandler(TextContentChanged); |
|
866 |
Document.FoldingManager.FoldingsChanged -= new EventHandler(DocumentFoldingsChanged); |
|
867 |
motherTextAreaControl = null; |
|
868 |
motherTextEditorControl = null; |
|
869 |
foreach (AbstractMargin margin in leftMargins) { |
|
870 |
if (margin is IDisposable) |
|
871 |
(margin as IDisposable).Dispose(); |
|
872 |
} |
|
873 |
textView.Dispose(); |
|
874 |
} |
|
875 |
} |
|
876 |
} |
|
877 |
||
878 |
#region UPDATE Commands |
|
879 |
internal void UpdateLine(int line) |
|
880 |
{ |
|
881 |
UpdateLines(0, line, line); |
|
882 |
} |
|
883 |
||
884 |
internal void UpdateLines(int lineBegin, int lineEnd) |
|
885 |
{ |
|
886 |
UpdateLines(0, lineBegin, lineEnd); |
|
887 |
} |
|
888 |
||
889 |
internal void UpdateToEnd(int lineBegin) |
|
890 |
{ |
|
891 |
// if (lineBegin > FirstPhysicalLine + textView.VisibleLineCount) { |
|
892 |
// return; |
|
893 |
// } |
|
894 |
||
895 |
lineBegin = Document.GetVisibleLine(lineBegin); |
|
896 |
int y = Math.Max( 0, (int)(lineBegin * textView.FontHeight)); |
|
897 |
y = Math.Max(0, y - this.virtualTop.Y); |
|
898 |
Rectangle r = new Rectangle(0, |
|
899 |
y, |
|
900 |
Width, |
|
901 |
Height - y); |
|
902 |
Invalidate(r); |
|
903 |
} |
|
904 |
||
905 |
internal void UpdateLineToEnd(int lineNr, int xStart) |
|
906 |
{ |
|
907 |
UpdateLines(xStart, lineNr, lineNr); |
|
908 |
} |
|
909 |
||
910 |
internal void UpdateLine(int line, int begin, int end) |
|
911 |
{ |
|
912 |
UpdateLines(line, line); |
|
913 |
} |
|
914 |
int FirstPhysicalLine { |
|
915 |
get { |
|
916 |
return VirtualTop.Y / textView.FontHeight; |
|
917 |
} |
|
918 |
} |
|
919 |
internal void UpdateLines(int xPos, int lineBegin, int lineEnd) |
|
920 |
{ |
|
921 |
// if (lineEnd < FirstPhysicalLine || lineBegin > FirstPhysicalLine + textView.VisibleLineCount) { |
|
922 |
// return; |
|
923 |
// } |
|
924 |
||
925 |
InvalidateLines((int)(xPos * this.TextView.WideSpaceWidth), lineBegin, lineEnd); |
|
926 |
} |
|
927 |
||
928 |
void InvalidateLines(int xPos, int lineBegin, int lineEnd) |
|
929 |
{ |
|
930 |
lineBegin = Math.Max(Document.GetVisibleLine(lineBegin), FirstPhysicalLine); |
|
931 |
lineEnd = Math.Min(Document.GetVisibleLine(lineEnd), FirstPhysicalLine + textView.VisibleLineCount); |
|
932 |
int y = Math.Max( 0, (int)(lineBegin * textView.FontHeight)); |
|
933 |
int height = Math.Min(textView.DrawingPosition.Height, (int)((1 + lineEnd - lineBegin) * (textView.FontHeight + 1))); |
|
934 |
||
935 |
Rectangle r = new Rectangle(0, |
|
936 |
y - 1 - this.virtualTop.Y, |
|
937 |
Width, |
|
938 |
height + 3); |
|
939 |
||
940 |
Invalidate(r); |
|
941 |
} |
|
942 |
#endregion |
|
943 |
public event KeyEventHandler KeyEventHandler; |
|
944 |
public event DialogKeyProcessor DoProcessDialogKey; |
|
945 |
||
946 |
//internal void |
|
947 |
} |
|
948 |
} |