Code Coverage Statistics for Source File
c:\Tools\SD3\src\Libraries\ICSharpCode.TextEditor\Project\Src\Gui\CompletionWindow\CodeCompletionWindow.cs
|
Sequence Point Coverage
N/A
0 of 0
|
Branch Coverage
N/A
0 of 0
|
Lines
295
|
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: 2533 $</version> |
|
6 |
// </file> |
|
7 |
||
8 |
using System; |
|
9 |
using System.Drawing; |
|
10 |
using System.Diagnostics; |
|
11 |
using System.Windows.Forms; |
|
12 |
using ICSharpCode.TextEditor.Document; |
|
13 |
||
14 |
namespace ICSharpCode.TextEditor.Gui.CompletionWindow |
|
15 |
{ |
|
16 |
public class CodeCompletionWindow : AbstractCompletionWindow |
|
17 |
{ |
|
18 |
ICompletionData[] completionData; |
|
19 |
CodeCompletionListView codeCompletionListView; |
|
20 |
VScrollBar vScrollBar = new VScrollBar(); |
|
21 |
ICompletionDataProvider dataProvider; |
|
22 |
IDocument document; |
|
23 |
||
24 |
int startOffset; |
|
25 |
int endOffset; |
|
26 |
DeclarationViewWindow declarationViewWindow = null; |
|
27 |
Rectangle workingScreen; |
|
28 |
||
29 |
public static CodeCompletionWindow ShowCompletionWindow(Form parent, TextEditorControl control, string fileName, ICompletionDataProvider completionDataProvider, char firstChar) |
|
30 |
{ |
|
31 |
ICompletionData[] completionData = completionDataProvider.GenerateCompletionData(fileName, control.ActiveTextAreaControl.TextArea, firstChar); |
|
32 |
if (completionData == null || completionData.Length == 0) { |
|
33 |
return null; |
|
34 |
} |
|
35 |
CodeCompletionWindow codeCompletionWindow = new CodeCompletionWindow(completionDataProvider, completionData, parent, control); |
|
36 |
codeCompletionWindow.ShowCompletionWindow(); |
|
37 |
return codeCompletionWindow; |
|
38 |
} |
|
39 |
||
40 |
CodeCompletionWindow(ICompletionDataProvider completionDataProvider, ICompletionData[] completionData, Form parentForm, TextEditorControl control) : base(parentForm, control) |
|
41 |
{ |
|
42 |
this.dataProvider = completionDataProvider; |
|
43 |
this.completionData = completionData; |
|
44 |
this.document = control.Document; |
|
45 |
||
46 |
workingScreen = Screen.GetWorkingArea(Location); |
|
47 |
startOffset = control.ActiveTextAreaControl.Caret.Offset + 1; |
|
48 |
endOffset = startOffset; |
|
49 |
if (completionDataProvider.PreSelection != null) { |
|
50 |
startOffset -= completionDataProvider.PreSelection.Length + 1; |
|
51 |
endOffset--; |
|
52 |
} |
|
53 |
||
54 |
codeCompletionListView = new CodeCompletionListView(completionData); |
|
55 |
codeCompletionListView.ImageList = completionDataProvider.ImageList; |
|
56 |
codeCompletionListView.Dock = DockStyle.Fill; |
|
57 |
codeCompletionListView.SelectedItemChanged += new EventHandler(CodeCompletionListViewSelectedItemChanged); |
|
58 |
codeCompletionListView.DoubleClick += new EventHandler(CodeCompletionListViewDoubleClick); |
|
59 |
codeCompletionListView.Click += new EventHandler(CodeCompletionListViewClick); |
|
60 |
Controls.Add(codeCompletionListView); |
|
61 |
||
62 |
const int MaxListLength = 10; |
|
63 |
if (completionData.Length > MaxListLength) { |
|
64 |
vScrollBar.Dock = DockStyle.Right; |
|
65 |
vScrollBar.Minimum = 0; |
|
66 |
vScrollBar.Maximum = completionData.Length - 1; |
|
67 |
vScrollBar.SmallChange = 1; |
|
68 |
vScrollBar.LargeChange = MaxListLength; |
|
69 |
codeCompletionListView.FirstItemChanged += new EventHandler(CodeCompletionListViewFirstItemChanged); |
|
70 |
Controls.Add(vScrollBar); |
|
71 |
} |
|
72 |
||
73 |
this.drawingSize = new Size(codeCompletionListView.ItemHeight * 10, |
|
74 |
codeCompletionListView.ItemHeight * Math.Min(MaxListLength, completionData.Length)); |
|
75 |
SetLocation(); |
|
76 |
||
77 |
if (declarationViewWindow == null) { |
|
78 |
declarationViewWindow = new DeclarationViewWindow(parentForm); |
|
79 |
} |
|
80 |
SetDeclarationViewLocation(); |
|
81 |
declarationViewWindow.ShowDeclarationViewWindow(); |
|
82 |
declarationViewWindow.MouseMove += ControlMouseMove; |
|
83 |
control.Focus(); |
|
84 |
CodeCompletionListViewSelectedItemChanged(this, EventArgs.Empty); |
|
85 |
||
86 |
if (completionDataProvider.DefaultIndex >= 0) { |
|
87 |
codeCompletionListView.SelectIndex(completionDataProvider.DefaultIndex); |
|
88 |
} |
|
89 |
||
90 |
if (completionDataProvider.PreSelection != null) { |
|
91 |
CaretOffsetChanged(this, EventArgs.Empty); |
|
92 |
} |
|
93 |
||
94 |
vScrollBar.ValueChanged += VScrollBarValueChanged; |
|
95 |
document.DocumentAboutToBeChanged += DocumentAboutToBeChanged; |
|
96 |
} |
|
97 |
||
98 |
bool inScrollUpdate; |
|
99 |
||
100 |
void CodeCompletionListViewFirstItemChanged(object sender, EventArgs e) |
|
101 |
{ |
|
102 |
if (inScrollUpdate) return; |
|
103 |
inScrollUpdate = true; |
|
104 |
vScrollBar.Value = Math.Min(vScrollBar.Maximum, codeCompletionListView.FirstItem); |
|
105 |
inScrollUpdate = false; |
|
106 |
} |
|
107 |
||
108 |
void VScrollBarValueChanged(object sender, EventArgs e) |
|
109 |
{ |
|
110 |
if (inScrollUpdate) return; |
|
111 |
inScrollUpdate = true; |
|
112 |
codeCompletionListView.FirstItem = vScrollBar.Value; |
|
113 |
codeCompletionListView.Refresh(); |
|
114 |
control.ActiveTextAreaControl.TextArea.Focus(); |
|
115 |
inScrollUpdate = false; |
|
116 |
} |
|
117 |
||
118 |
void SetDeclarationViewLocation() |
|
119 |
{ |
|
120 |
// This method uses the side with more free space |
|
121 |
int leftSpace = Bounds.Left - workingScreen.Left; |
|
122 |
int rightSpace = workingScreen.Right - Bounds.Right; |
|
123 |
Point pos; |
|
124 |
// The declaration view window has better line break when used on |
|
125 |
// the right side, so prefer the right side to the left. |
|
126 |
if (rightSpace * 2 > leftSpace) |
|
127 |
pos = new Point(Bounds.Right, Bounds.Top); |
|
128 |
else |
|
129 |
pos = new Point(Bounds.Left - declarationViewWindow.Width, Bounds.Top); |
|
130 |
if (declarationViewWindow.Location != pos) { |
|
131 |
declarationViewWindow.Location = pos; |
|
132 |
} |
|
133 |
} |
|
134 |
||
135 |
protected override void SetLocation() |
|
136 |
{ |
|
137 |
base.SetLocation(); |
|
138 |
if (declarationViewWindow != null) { |
|
139 |
SetDeclarationViewLocation(); |
|
140 |
} |
|
141 |
} |
|
142 |
||
143 |
public void HandleMouseWheel(MouseEventArgs e) |
|
144 |
{ |
|
145 |
int MAX_DELTA = 120; // basically it's constant now, but could be changed later by MS |
|
146 |
int multiplier = e.Delta / MAX_DELTA; |
|
147 |
multiplier *= System.Windows.Forms.SystemInformation.MouseWheelScrollLines * vScrollBar.SmallChange; |
|
148 |
||
149 |
int newValue; |
|
150 |
if (System.Windows.Forms.SystemInformation.MouseWheelScrollLines > 0) { |
|
151 |
newValue = this.vScrollBar.Value - (control.TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * multiplier; |
|
152 |
} else { |
|
153 |
newValue = this.vScrollBar.Value - (control.TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * multiplier; |
|
154 |
} |
|
155 |
vScrollBar.Value = Math.Max(vScrollBar.Minimum, Math.Min(vScrollBar.Maximum - vScrollBar.LargeChange + 1, newValue)); |
|
156 |
} |
|
157 |
||
158 |
void CodeCompletionListViewSelectedItemChanged(object sender, EventArgs e) |
|
159 |
{ |
|
160 |
ICompletionData data = codeCompletionListView.SelectedCompletionData; |
|
161 |
if (data != null && data.Description != null && data.Description.Length > 0) { |
|
162 |
declarationViewWindow.Description = data.Description; |
|
163 |
SetDeclarationViewLocation(); |
|
164 |
} else { |
|
165 |
declarationViewWindow.Description = null; |
|
166 |
} |
|
167 |
} |
|
168 |
||
169 |
public override bool ProcessKeyEvent(char ch) |
|
170 |
{ |
|
171 |
switch (dataProvider.ProcessKey(ch)) { |
|
172 |
case CompletionDataProviderKeyResult.BeforeStartKey: |
|
173 |
// increment start+end, then process as normal char |
|
174 |
++startOffset; |
|
175 |
++endOffset; |
|
176 |
return base.ProcessKeyEvent(ch); |
|
177 |
case CompletionDataProviderKeyResult.NormalKey: |
|
178 |
// just process normally |
|
179 |
return base.ProcessKeyEvent(ch); |
|
180 |
case CompletionDataProviderKeyResult.InsertionKey: |
|
181 |
return InsertSelectedItem(ch); |
|
182 |
default: |
|
183 |
throw new InvalidOperationException("Invalid return value of dataProvider.ProcessKey"); |
|
184 |
} |
|
185 |
} |
|
186 |
||
187 |
void DocumentAboutToBeChanged(object sender, DocumentEventArgs e) |
|
188 |
{ |
|
189 |
// => startOffset test required so that this startOffset/endOffset are not incremented again |
|
190 |
// for BeforeStartKey characters |
|
191 |
if (e.Offset >= startOffset && e.Offset <= endOffset) { |
|
192 |
if (e.Length > 0) { // length of removed region |
|
193 |
endOffset -= e.Length; |
|
194 |
} |
|
195 |
if (!string.IsNullOrEmpty(e.Text)) { |
|
196 |
endOffset += e.Text.Length; |
|
197 |
} |
|
198 |
} |
|
199 |
} |
|
200 |
||
201 |
protected override void CaretOffsetChanged(object sender, EventArgs e) |
|
202 |
{ |
|
203 |
int offset = control.ActiveTextAreaControl.Caret.Offset; |
|
204 |
if (offset == startOffset) { |
|
205 |
return; |
|
206 |
} |
|
207 |
if (offset < startOffset || offset > endOffset) { |
|
208 |
Close(); |
|
209 |
} else { |
|
210 |
codeCompletionListView.SelectItemWithStart(control.Document.GetText(startOffset, offset - startOffset)); |
|
211 |
} |
|
212 |
} |
|
213 |
||
214 |
protected override bool ProcessTextAreaKey(Keys keyData) |
|
215 |
{ |
|
216 |
if (!Visible) { |
|
217 |
return false; |
|
218 |
} |
|
219 |
||
220 |
switch (keyData) { |
|
221 |
case Keys.Home: |
|
222 |
codeCompletionListView.SelectIndex(0); |
|
223 |
return true; |
|
224 |
case Keys.End: |
|
225 |
codeCompletionListView.SelectIndex(completionData.Length-1); |
|
226 |
return true; |
|
227 |
case Keys.PageDown: |
|
228 |
codeCompletionListView.PageDown(); |
|
229 |
return true; |
|
230 |
case Keys.PageUp: |
|
231 |
codeCompletionListView.PageUp(); |
|
232 |
return true; |
|
233 |
case Keys.Down: |
|
234 |
codeCompletionListView.SelectNextItem(); |
|
235 |
return true; |
|
236 |
case Keys.Up: |
|
237 |
codeCompletionListView.SelectPrevItem(); |
|
238 |
return true; |
|
239 |
case Keys.Tab: |
|
240 |
case Keys.Return: |
|
241 |
InsertSelectedItem('\0'); |
|
242 |
return true; |
|
243 |
} |
|
244 |
return base.ProcessTextAreaKey(keyData); |
|
245 |
} |
|
246 |
||
247 |
void CodeCompletionListViewDoubleClick(object sender, EventArgs e) |
|
248 |
{ |
|
249 |
InsertSelectedItem('\0'); |
|
250 |
} |
|
251 |
||
252 |
void CodeCompletionListViewClick(object sender, EventArgs e) |
|
253 |
{ |
|
254 |
control.ActiveTextAreaControl.TextArea.Focus(); |
|
255 |
} |
|
256 |
||
257 |
protected override void Dispose(bool disposing) |
|
258 |
{ |
|
259 |
if (disposing) { |
|
260 |
document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged; |
|
261 |
if (codeCompletionListView != null) { |
|
262 |
codeCompletionListView.Dispose(); |
|
263 |
codeCompletionListView = null; |
|
264 |
} |
|
265 |
if (declarationViewWindow != null) { |
|
266 |
declarationViewWindow.Dispose(); |
|
267 |
declarationViewWindow = null; |
|
268 |
} |
|
269 |
} |
|
270 |
base.Dispose(disposing); |
|
271 |
} |
|
272 |
||
273 |
bool InsertSelectedItem(char ch) |
|
274 |
{ |
|
275 |
document.DocumentAboutToBeChanged -= DocumentAboutToBeChanged; |
|
276 |
ICompletionData data = codeCompletionListView.SelectedCompletionData; |
|
277 |
bool result = false; |
|
278 |
if (data != null) { |
|
279 |
control.BeginUpdate(); |
|
280 |
||
281 |
try { |
|
282 |
if (endOffset - startOffset > 0) { |
|
283 |
control.Document.Remove(startOffset, endOffset - startOffset); |
|
284 |
} |
|
285 |
Debug.Assert(startOffset <= document.TextLength); |
|
286 |
result = dataProvider.InsertAction(data, control.ActiveTextAreaControl.TextArea, startOffset, ch); |
|
287 |
} finally { |
|
288 |
control.EndUpdate(); |
|
289 |
} |
|
290 |
} |
|
291 |
Close(); |
|
292 |
return result; |
|
293 |
} |
|
294 |
} |
|
295 |
} |