Mercurial > projects > dwt-addons
annotate dwtx/jface/text/contentassist/CompletionProposalPopup.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 1a5b8f8129df |
children |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Sean Montgomery, sean_montgomery@comcast.net - https://bugs.eclipse.org/bugs/show_bug.cgi?id=116454 | |
11 * Port to the D programming language: | |
12 * Frank Benoit <benoit@tionex.de> | |
13 *******************************************************************************/ | |
14 module dwtx.jface.text.contentassist.CompletionProposalPopup; | |
15 | |
131 | 16 import dwtx.jface.text.contentassist.ContentAssistEvent; // packageimport |
17 import dwtx.jface.text.contentassist.Helper; // packageimport | |
18 import dwtx.jface.text.contentassist.PopupCloser; // packageimport | |
19 import dwtx.jface.text.contentassist.IContentAssistant; // packageimport | |
20 import dwtx.jface.text.contentassist.CompletionProposal; // packageimport | |
21 import dwtx.jface.text.contentassist.ICompletionProposalExtension5; // packageimport | |
22 import dwtx.jface.text.contentassist.IContextInformationValidator; // packageimport | |
23 import dwtx.jface.text.contentassist.IContentAssistListener; // packageimport | |
24 import dwtx.jface.text.contentassist.ICompletionProposalExtension6; // packageimport | |
25 import dwtx.jface.text.contentassist.ICompletionListener; // packageimport | |
26 import dwtx.jface.text.contentassist.ICompletionProposalExtension2; // packageimport | |
27 import dwtx.jface.text.contentassist.IContentAssistantExtension4; // packageimport | |
28 import dwtx.jface.text.contentassist.ContextInformation; // packageimport | |
29 import dwtx.jface.text.contentassist.ICompletionProposalExtension3; // packageimport | |
30 import dwtx.jface.text.contentassist.ContextInformationValidator; // packageimport | |
31 import dwtx.jface.text.contentassist.ICompletionProposal; // packageimport | |
32 import dwtx.jface.text.contentassist.IContentAssistProcessor; // packageimport | |
33 import dwtx.jface.text.contentassist.AdditionalInfoController; // packageimport | |
34 import dwtx.jface.text.contentassist.IContextInformationPresenter; // packageimport | |
35 import dwtx.jface.text.contentassist.ICompletionProposalExtension4; // packageimport | |
36 import dwtx.jface.text.contentassist.ICompletionListenerExtension; // packageimport | |
37 import dwtx.jface.text.contentassist.ContextInformationPopup; // packageimport | |
38 import dwtx.jface.text.contentassist.IContextInformationExtension; // packageimport | |
39 import dwtx.jface.text.contentassist.IContentAssistantExtension2; // packageimport | |
40 import dwtx.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport | |
41 import dwtx.jface.text.contentassist.ICompletionProposalExtension; // packageimport | |
42 import dwtx.jface.text.contentassist.IContextInformation; // packageimport | |
43 import dwtx.jface.text.contentassist.IContentAssistantExtension3; // packageimport | |
44 import dwtx.jface.text.contentassist.ContentAssistant; // packageimport | |
45 import dwtx.jface.text.contentassist.IContentAssistantExtension; // packageimport | |
46 import dwtx.jface.text.contentassist.JFaceTextMessages; // packageimport | |
47 | |
48 | |
129 | 49 import dwt.dwthelper.utils; |
50 | |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
147
diff
changeset
|
51 import dwtx.dwtxhelper.Collection; |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
147
diff
changeset
|
52 |
129 | 53 |
54 import dwt.DWT; | |
55 import dwt.custom.BusyIndicator; | |
56 import dwt.custom.StyleRange; | |
57 import dwt.events.ControlEvent; | |
58 import dwt.events.ControlListener; | |
59 import dwt.events.DisposeEvent; | |
60 import dwt.events.DisposeListener; | |
61 import dwt.events.FocusEvent; | |
62 import dwt.events.FocusListener; | |
63 import dwt.events.KeyAdapter; | |
64 import dwt.events.KeyEvent; | |
65 import dwt.events.KeyListener; | |
66 import dwt.events.MouseAdapter; | |
67 import dwt.events.MouseEvent; | |
68 import dwt.events.SelectionEvent; | |
69 import dwt.events.SelectionListener; | |
70 import dwt.events.TraverseEvent; | |
71 import dwt.events.TraverseListener; | |
72 import dwt.events.VerifyEvent; | |
73 import dwt.graphics.Color; | |
74 import dwt.graphics.Font; | |
75 import dwt.graphics.FontData; | |
76 import dwt.graphics.Image; | |
77 import dwt.graphics.Point; | |
78 import dwt.graphics.Rectangle; | |
79 import dwt.layout.GridData; | |
80 import dwt.layout.GridLayout; | |
81 import dwt.widgets.Control; | |
82 import dwt.widgets.Display; | |
83 import dwt.widgets.Event; | |
84 import dwt.widgets.Label; | |
85 import dwt.widgets.Listener; | |
86 import dwt.widgets.Shell; | |
87 import dwt.widgets.Table; | |
88 import dwt.widgets.TableItem; | |
89 import dwtx.core.commands.AbstractHandler; | |
90 import dwtx.core.commands.ExecutionEvent; | |
91 import dwtx.core.commands.ExecutionException; | |
92 import dwtx.core.commands.IHandler; | |
93 import dwtx.core.runtime.Assert; | |
94 import dwtx.jface.bindings.keys.KeySequence; | |
95 import dwtx.jface.bindings.keys.SWTKeySupport; | |
96 import dwtx.jface.contentassist.IContentAssistSubjectControl; | |
97 import dwtx.jface.internal.text.InformationControlReplacer; | |
98 import dwtx.jface.internal.text.TableOwnerDrawSupport; | |
99 import dwtx.jface.preference.JFacePreferences; | |
100 import dwtx.jface.resource.JFaceResources; | |
101 import dwtx.jface.text.AbstractInformationControlManager; | |
102 import dwtx.jface.text.BadLocationException; | |
103 import dwtx.jface.text.DocumentEvent; | |
104 import dwtx.jface.text.IDocument; | |
105 import dwtx.jface.text.IDocumentListener; | |
106 import dwtx.jface.text.IEditingSupport; | |
107 import dwtx.jface.text.IEditingSupportRegistry; | |
108 import dwtx.jface.text.IInformationControl; | |
109 import dwtx.jface.text.IRegion; | |
110 import dwtx.jface.text.IRewriteTarget; | |
111 import dwtx.jface.text.ITextViewer; | |
112 import dwtx.jface.text.ITextViewerExtension; | |
113 import dwtx.jface.text.TextUtilities; | |
158 | 114 import dwtx.jface.text.AbstractInformationControlManager; |
129 | 115 import dwtx.jface.util.Geometry; |
116 import dwtx.jface.viewers.StyledString; | |
117 | |
118 | |
119 /** | |
120 * This class is used to present proposals to the user. If additional | |
121 * information exists for a proposal, then selecting that proposal | |
122 * will result in the information being displayed in a secondary | |
123 * window. | |
124 * | |
125 * @see dwtx.jface.text.contentassist.ICompletionProposal | |
126 * @see dwtx.jface.text.contentassist.AdditionalInfoController | |
127 */ | |
128 class CompletionProposalPopup : IContentAssistListener { | |
129 /** | |
130 * Set to <code>true</code> to use a Table with DWT.VIRTUAL. | |
131 * XXX: This is a workaround for: https://bugs.eclipse.org/bugs/show_bug.cgi?id=90321 | |
132 * More details see also: https://bugs.eclipse.org/bugs/show_bug.cgi?id=98585#c36 | |
133 * @since 3.1 | |
134 */ | |
159 | 135 private static bool USE_VIRTUAL_; |
136 private static bool USE_VIRTUAL_init = false;; | |
137 private static bool USE_VIRTUAL(){ | |
138 if( !USE_VIRTUAL_init ){ | |
139 USE_VIRTUAL_init = true; | |
140 USE_VIRTUAL_ = !"motif".equals(DWT.getPlatform()); //$NON-NLS-1$ | |
141 } | |
142 return USE_VIRTUAL_; | |
143 } | |
129 | 144 |
145 /** | |
146 * Completion proposal selection handler. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
147 * |
129 | 148 * @since 3.4 |
149 */ | |
150 final class ProposalSelectionHandler : AbstractHandler { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
151 |
129 | 152 /** |
153 * Selection operation codes. | |
154 */ | |
159 | 155 static const int SELECT_NEXT= 1; |
156 static const int SELECT_PREVIOUS= 2; | |
129 | 157 |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
158 |
129 | 159 private int fOperationCode; |
160 | |
161 /** | |
162 * Creates a new selection handler. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
163 * |
129 | 164 * @param operationCode the operation code |
165 * @since 3.4 | |
166 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
167 public this(int operationCode) { |
129 | 168 Assert.isLegal(operationCode is SELECT_NEXT || operationCode is SELECT_PREVIOUS); |
169 fOperationCode= operationCode; | |
170 } | |
171 | |
172 /* | |
173 * @see dwtx.core.commands.AbstractHandler#execute(dwtx.core.commands.ExecutionEvent) | |
174 * @since 3.4 | |
175 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
176 public Object execute(ExecutionEvent event) { |
129 | 177 int itemCount= fProposalTable.getItemCount(); |
178 int selectionIndex= fProposalTable.getSelectionIndex(); | |
179 switch (fOperationCode) { | |
180 case SELECT_NEXT: | |
181 selectionIndex+= 1; | |
182 if (selectionIndex > itemCount - 1) | |
183 selectionIndex= 0; | |
184 break; | |
185 case SELECT_PREVIOUS: | |
186 selectionIndex-= 1; | |
187 if (selectionIndex < 0) | |
188 selectionIndex= itemCount - 1; | |
189 break; | |
192
c3583c6ec027
Added missing default cases for switch statements
Frank Benoit <benoit@tionex.de>
parents:
162
diff
changeset
|
190 default: |
129 | 191 } |
192 selectProposal(selectionIndex, false); | |
193 return null; | |
194 } | |
195 | |
196 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
197 |
129 | 198 |
199 /** | |
200 * The empty proposal displayed if there is nothing else to show. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
201 * |
129 | 202 * @since 3.2 |
203 */ | |
204 private static final class EmptyProposal : ICompletionProposal, ICompletionProposalExtension { | |
205 | |
206 String fDisplayString; | |
207 int fOffset; | |
208 /* | |
209 * @see ICompletionProposal#apply(IDocument) | |
210 */ | |
211 public void apply(IDocument document) { | |
212 } | |
213 | |
214 /* | |
215 * @see ICompletionProposal#getSelection(IDocument) | |
216 */ | |
217 public Point getSelection(IDocument document) { | |
218 return new Point(fOffset, 0); | |
219 } | |
220 | |
221 /* | |
222 * @see ICompletionProposal#getContextInformation() | |
223 */ | |
224 public IContextInformation getContextInformation() { | |
225 return null; | |
226 } | |
227 | |
228 /* | |
229 * @see ICompletionProposal#getImage() | |
230 */ | |
231 public Image getImage() { | |
232 return null; | |
233 } | |
234 | |
235 /* | |
236 * @see ICompletionProposal#getDisplayString() | |
237 */ | |
238 public String getDisplayString() { | |
239 return fDisplayString; | |
240 } | |
241 | |
242 /* | |
243 * @see ICompletionProposal#getAdditionalProposalInfo() | |
244 */ | |
245 public String getAdditionalProposalInfo() { | |
246 return null; | |
247 } | |
248 | |
249 /* | |
250 * @see dwtx.jface.text.contentassist.ICompletionProposalExtension#apply(dwtx.jface.text.IDocument, char, int) | |
251 */ | |
252 public void apply(IDocument document, char trigger, int offset) { | |
253 } | |
254 | |
255 /* | |
256 * @see dwtx.jface.text.contentassist.ICompletionProposalExtension#isValidFor(dwtx.jface.text.IDocument, int) | |
257 */ | |
258 public bool isValidFor(IDocument document, int offset) { | |
259 return false; | |
260 } | |
261 | |
262 /* | |
263 * @see dwtx.jface.text.contentassist.ICompletionProposalExtension#getTriggerCharacters() | |
264 */ | |
265 public char[] getTriggerCharacters() { | |
266 return null; | |
267 } | |
268 | |
269 /* | |
270 * @see dwtx.jface.text.contentassist.ICompletionProposalExtension#getContextInformationPosition() | |
271 */ | |
272 public int getContextInformationPosition() { | |
273 return -1; | |
274 } | |
275 } | |
276 | |
277 private final class ProposalSelectionListener : KeyListener { | |
278 public void keyPressed(KeyEvent e) { | |
279 if (!Helper.okToUse(fProposalShell)) | |
280 return; | |
281 | |
282 if (e.character is 0 && e.keyCode is DWT.CTRL) { | |
283 // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754 | |
284 int index= fProposalTable.getSelectionIndex(); | |
285 if (index >= 0) | |
286 selectProposal(index, true); | |
287 } | |
288 } | |
289 | |
290 public void keyReleased(KeyEvent e) { | |
291 if (!Helper.okToUse(fProposalShell)) | |
292 return; | |
293 | |
294 if (e.character is 0 && e.keyCode is DWT.CTRL) { | |
295 // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754 | |
296 int index= fProposalTable.getSelectionIndex(); | |
297 if (index >= 0) | |
298 selectProposal(index, false); | |
299 } | |
300 } | |
301 } | |
302 | |
303 private final class CommandKeyListener : KeyAdapter { | |
304 private KeySequence fCommandSequence; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
305 |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
306 private this(KeySequence keySequence) { |
129 | 307 fCommandSequence= keySequence; |
308 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
309 |
129 | 310 public void keyPressed(KeyEvent e) { |
311 if (!Helper.okToUse(fProposalShell)) | |
312 return; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
313 |
129 | 314 int accelerator= SWTKeySupport.convertEventToUnmodifiedAccelerator(e); |
315 KeySequence sequence= KeySequence.getInstance(SWTKeySupport.convertAcceleratorToKeyStroke(accelerator)); | |
162 | 316 if (sequence==/++/fCommandSequence) |
129 | 317 if (fContentAssistant.isPrefixCompletionEnabled()) |
318 incrementalComplete(); | |
319 else | |
320 showProposals(false); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
321 |
129 | 322 } |
323 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
324 |
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
325 |
129 | 326 /** The associated text viewer. */ |
327 private ITextViewer fViewer; | |
328 /** The associated content assistant. */ | |
329 private ContentAssistant fContentAssistant; | |
330 /** The used additional info controller. */ | |
331 private AdditionalInfoController fAdditionalInfoController; | |
332 /** The closing strategy for this completion proposal popup. */ | |
159 | 333 private PopupCloser fPopupCloser; |
129 | 334 /** The popup shell. */ |
335 private Shell fProposalShell; | |
336 /** The proposal table. */ | |
337 private Table fProposalTable; | |
338 /** Indicates whether a completion proposal is being inserted. */ | |
339 private bool fInserting= false; | |
340 /** The key listener to control navigation. */ | |
341 private ProposalSelectionListener fKeyListener; | |
342 /** List of document events used for filtering proposals. */ | |
159 | 343 private List fDocumentEvents; |
129 | 344 /** Listener filling the document event queue. */ |
345 private IDocumentListener fDocumentListener; | |
346 /** The filter list of proposals. */ | |
347 private ICompletionProposal[] fFilteredProposals; | |
348 /** The computed list of proposals. */ | |
349 private ICompletionProposal[] fComputedProposals; | |
350 /** The offset for which the proposals have been computed. */ | |
351 private int fInvocationOffset; | |
352 /** The offset for which the computed proposals have been filtered. */ | |
353 private int fFilterOffset; | |
354 /** | |
355 * The most recently selected proposal. | |
356 * @since 3.0 | |
357 */ | |
358 private ICompletionProposal fLastProposal; | |
359 /** | |
360 * The content assist subject control. | |
361 * This replaces <code>fViewer</code> | |
362 * | |
363 * @since 3.0 | |
364 */ | |
365 private IContentAssistSubjectControl fContentAssistSubjectControl; | |
366 /** | |
367 * The content assist subject control adapter. | |
368 * This replaces <code>fViewer</code> | |
369 * | |
370 * @since 3.0 | |
371 */ | |
372 private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter; | |
373 /** | |
374 * Remembers the size for this completion proposal popup. | |
375 * @since 3.0 | |
376 */ | |
377 private Point fSize; | |
378 /** | |
379 * Editor helper that communicates that the completion proposal popup may | |
380 * have focus while the 'logical focus' is still with the editor. | |
381 * @since 3.1 | |
382 */ | |
383 private IEditingSupport fFocusHelper; | |
384 /** | |
385 * Set to true by {@link #computeFilteredProposals(int, DocumentEvent)} if | |
386 * the returned proposals are a subset of {@link #fFilteredProposals}, | |
387 * <code>false</code> if not. | |
388 * @since 3.1 | |
389 */ | |
390 private bool fIsFilteredSubset; | |
391 /** | |
392 * The filter runnable. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
393 * |
129 | 394 * @since 3.1.1 |
395 */ | |
159 | 396 private Runnable fFilterRunnable; |
397 private void fFilterRunnableInit(){ | |
398 fFilterRunnable = dgRunnable( { | |
129 | 399 if (!fIsFilterPending) |
400 return; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
401 |
129 | 402 fIsFilterPending= false; |
403 | |
404 if (!Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) | |
405 return; | |
406 | |
407 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x; | |
408 ICompletionProposal[] proposals= null; | |
409 try { | |
410 if (offset > -1) { | |
411 DocumentEvent event= TextUtilities.mergeProcessedDocumentEvents(fDocumentEvents); | |
412 proposals= computeFilteredProposals(offset, event); | |
413 } | |
414 } catch (BadLocationException x) { | |
415 } finally { | |
416 fDocumentEvents.clear(); | |
417 } | |
418 fFilterOffset= offset; | |
419 | |
420 if (proposals !is null && proposals.length > 0) | |
421 setProposals(proposals, fIsFilteredSubset); | |
422 else | |
423 hide(); | |
159 | 424 }); |
425 } | |
426 | |
129 | 427 /** |
428 * <code>true</code> if <code>fFilterRunnable</code> has been | |
429 * posted, <code>false</code> if not. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
430 * |
129 | 431 * @since 3.1.1 |
432 */ | |
433 private bool fIsFilterPending= false; | |
434 /** | |
435 * The info message at the bottom of the popup, or <code>null</code> for no popup (if | |
436 * ContentAssistant does not provide one). | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
437 * |
129 | 438 * @since 3.2 |
439 */ | |
440 private Label fMessageText; | |
441 /** | |
442 * The font used for <code>fMessageText</code> or null; dispose when done. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
443 * |
129 | 444 * @since 3.2 |
445 */ | |
446 private Font fMessageTextFont; | |
447 /** | |
448 * The most recent completion offset (used to determine repeated invocation) | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
449 * |
129 | 450 * @since 3.2 |
451 */ | |
452 private int fLastCompletionOffset; | |
453 /** | |
454 * The (reusable) empty proposal. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
455 * |
129 | 456 * @since 3.2 |
457 */ | |
159 | 458 private EmptyProposal fEmptyProposal; |
129 | 459 /** |
460 * The text for the empty proposal, or <code>null</code> to use the default text. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
461 * |
129 | 462 * @since 3.2 |
463 */ | |
464 private String fEmptyMessage= null; | |
465 /** | |
466 * Tells whether colored labels support is enabled. | |
467 * Only valid while the popup is active. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
468 * |
129 | 469 * @since 3.4 |
470 */ | |
471 private bool fIsColoredLabelsSupportEnabled= false; | |
472 | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
473 |
129 | 474 /** |
475 * Creates a new completion proposal popup for the given elements. | |
476 * | |
477 * @param contentAssistant the content assistant feeding this popup | |
478 * @param viewer the viewer on top of which this popup appears | |
479 * @param infoController the information control collaborating with this popup | |
480 * @since 2.0 | |
481 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
482 public this(ContentAssistant contentAssistant, ITextViewer viewer, AdditionalInfoController infoController) { |
159 | 483 // DWT instance init |
484 fDocumentEvents= new ArrayList(); | |
485 fPopupCloser= new PopupCloser(); | |
486 if( fEmptyProposal is null ) fEmptyProposal= new EmptyProposal(); | |
487 fFilterRunnableInit(); | |
488 | |
129 | 489 fContentAssistant= contentAssistant; |
490 fViewer= viewer; | |
491 fAdditionalInfoController= infoController; | |
492 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer); | |
493 } | |
494 | |
495 /** | |
496 * Creates a new completion proposal popup for the given elements. | |
497 * | |
498 * @param contentAssistant the content assistant feeding this popup | |
499 * @param contentAssistSubjectControl the content assist subject control on top of which this popup appears | |
500 * @param infoController the information control collaborating with this popup | |
501 * @since 3.0 | |
502 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
503 public this(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl, AdditionalInfoController infoController) { |
159 | 504 // DWT instance init |
505 fDocumentEvents= new ArrayList(); | |
506 fPopupCloser= new PopupCloser(); | |
507 if( fEmptyProposal is null ) fEmptyProposal= new EmptyProposal(); | |
508 fFilterRunnableInit(); | |
509 | |
129 | 510 fContentAssistant= contentAssistant; |
511 fContentAssistSubjectControl= contentAssistSubjectControl; | |
512 fAdditionalInfoController= infoController; | |
513 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl); | |
514 } | |
515 | |
516 /** | |
517 * Computes and presents completion proposals. The flag indicates whether this call has | |
518 * be made out of an auto activation context. | |
519 * | |
520 * @param autoActivated <code>true</code> if auto activation context | |
521 * @return an error message or <code>null</code> in case of no error | |
522 */ | |
158 | 523 public String showProposals(bool autoActivated) { |
129 | 524 |
525 if (fKeyListener is null) | |
526 fKeyListener= new ProposalSelectionListener(); | |
527 | |
528 final Control control= fContentAssistSubjectControlAdapter.getControl(); | |
529 | |
530 if (!Helper.okToUse(fProposalShell) && control !is null && !control.isDisposed()) { | |
531 // add the listener before computing the proposals so we don't move the caret | |
532 // when the user types fast. | |
533 fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener); | |
534 | |
158 | 535 BusyIndicator.showWhile(control.getDisplay(), dgRunnable((bool autoActivated_) { |
129 | 536 |
158 | 537 fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x; |
538 fFilterOffset= fInvocationOffset; | |
539 fLastCompletionOffset= fFilterOffset; | |
540 fComputedProposals= computeProposals(fInvocationOffset); | |
129 | 541 |
158 | 542 int count= (fComputedProposals is null ? 0 : fComputedProposals.length); |
543 if (count is 0 && hideWhenNoProposals(autoActivated_)) | |
544 return; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
545 |
158 | 546 if (count is 1 && !autoActivated_ && canAutoInsert(fComputedProposals[0])) { |
547 insertProposal(fComputedProposals[0], cast(wchar) 0, 0, fInvocationOffset); | |
548 hide(); | |
549 } else { | |
550 createProposalSelector(); | |
551 setProposals(fComputedProposals, false); | |
552 displayProposals(); | |
129 | 553 } |
158 | 554 }, autoActivated )); |
129 | 555 } else { |
556 fLastCompletionOffset= fFilterOffset; | |
557 handleRepeatedInvocation(); | |
558 } | |
559 | |
560 return getErrorMessage(); | |
561 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
562 |
129 | 563 /** |
564 * Hides the popup and returns <code>true</code> if the popup is configured | |
565 * to never display an empty list. Returns <code>false</code> otherwise. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
566 * |
129 | 567 * @param autoActivated whether the invocation was auto-activated |
568 * @return <code>false</code> if an empty list should be displayed, <code>true</code> otherwise | |
569 * @since 3.2 | |
570 */ | |
571 private bool hideWhenNoProposals(bool autoActivated) { | |
572 if (autoActivated || !fContentAssistant.isShowEmptyList()) { | |
573 if (!autoActivated) { | |
574 Control control= fContentAssistSubjectControlAdapter.getControl(); | |
575 if (control !is null && !control.isDisposed()) | |
576 control.getDisplay().beep(); | |
577 } | |
578 hide(); | |
579 return true; | |
580 } | |
581 return false; | |
582 } | |
583 | |
584 /** | |
585 * If content assist is set up to handle cycling, then the proposals are recomputed. Otherwise, | |
586 * nothing happens. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
587 * |
129 | 588 * @since 3.2 |
589 */ | |
590 private void handleRepeatedInvocation() { | |
591 if (fContentAssistant.isRepeatedInvocationMode()) { | |
592 fComputedProposals= computeProposals(fFilterOffset); | |
593 setProposals(fComputedProposals, false); | |
594 } | |
595 } | |
596 | |
597 /** | |
598 * Returns the completion proposal available at the given offset of the | |
599 * viewer's document. Delegates the work to the content assistant. | |
600 * | |
601 * @param offset the offset | |
602 * @return the completion proposals available at this offset | |
603 */ | |
604 private ICompletionProposal[] computeProposals(int offset) { | |
605 if (fContentAssistSubjectControl !is null) | |
606 return fContentAssistant.computeCompletionProposals(fContentAssistSubjectControl, offset); | |
607 return fContentAssistant.computeCompletionProposals(fViewer, offset); | |
608 } | |
609 | |
610 /** | |
611 * Returns the error message. | |
612 * | |
613 * @return the error message | |
614 */ | |
615 private String getErrorMessage() { | |
616 return fContentAssistant.getErrorMessage(); | |
617 } | |
618 | |
619 /** | |
620 * Creates the proposal selector. | |
621 */ | |
622 private void createProposalSelector() { | |
623 if (Helper.okToUse(fProposalShell)) | |
624 return; | |
625 | |
626 Control control= fContentAssistSubjectControlAdapter.getControl(); | |
627 fProposalShell= new Shell(control.getShell(), DWT.ON_TOP | DWT.RESIZE ); | |
628 fProposalShell.setFont(JFaceResources.getDefaultFont()); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
629 if (USE_VIRTUAL) { |
129 | 630 fProposalTable= new Table(fProposalShell, DWT.H_SCROLL | DWT.V_SCROLL | DWT.VIRTUAL); |
631 | |
135 | 632 Listener listener= new class() Listener { |
129 | 633 public void handleEvent(Event event) { |
634 handleSetData(event); | |
635 } | |
636 }; | |
637 fProposalTable.addListener(DWT.SetData, listener); | |
638 } else { | |
639 fProposalTable= new Table(fProposalShell, DWT.H_SCROLL | DWT.V_SCROLL); | |
640 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
641 |
129 | 642 fIsColoredLabelsSupportEnabled= fContentAssistant.isColoredLabelsSupportEnabled(); |
643 if (fIsColoredLabelsSupportEnabled) | |
644 TableOwnerDrawSupport.install(fProposalTable); | |
645 | |
646 fProposalTable.setLocation(0, 0); | |
647 if (fAdditionalInfoController !is null) | |
648 fAdditionalInfoController.setSizeConstraints(50, 10, true, true); | |
649 | |
650 GridLayout layout= new GridLayout(); | |
651 layout.marginWidth= 0; | |
652 layout.marginHeight= 0; | |
653 layout.verticalSpacing= 1; | |
654 fProposalShell.setLayout(layout); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
655 |
129 | 656 if (fContentAssistant.isStatusLineVisible()) { |
657 createMessageText(); | |
658 } | |
659 | |
660 GridData data= new GridData(GridData.FILL_BOTH); | |
661 | |
162 | 662 Point size= fContentAssistant.restoreCompletionProposalPopupSize_package(); |
129 | 663 if (size !is null) { |
664 fProposalTable.setLayoutData(data); | |
665 fProposalShell.setSize(size); | |
666 } else { | |
667 int height= fProposalTable.getItemHeight() * 10; | |
668 // use golden ratio as default aspect ratio | |
162 | 669 double aspectRatio= (1 + Math.sqrt(5.0f)) / 2; |
134 | 670 int width= cast(int) (height * aspectRatio); |
129 | 671 Rectangle trim= fProposalTable.computeTrim(0, 0, width, height); |
672 data.heightHint= trim.height; | |
673 data.widthHint= trim.width; | |
674 fProposalTable.setLayoutData(data); | |
675 fProposalShell.pack(); | |
676 } | |
677 fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset()); | |
678 | |
135 | 679 fProposalShell.addControlListener(new class() ControlListener { |
129 | 680 |
681 public void controlMoved(ControlEvent e) {} | |
682 | |
683 public void controlResized(ControlEvent e) { | |
684 if (fAdditionalInfoController !is null) { | |
685 // reset the cached resize constraints | |
686 fAdditionalInfoController.setSizeConstraints(50, 10, true, false); | |
687 } | |
688 | |
689 fSize= fProposalShell.getSize(); | |
690 } | |
691 }); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
692 |
129 | 693 fProposalShell.setBackground(control.getDisplay().getSystemColor(DWT.COLOR_GRAY)); |
694 | |
695 Color c= getBackgroundColor(control); | |
696 fProposalTable.setBackground(c); | |
697 | |
698 c= getForegroundColor(control); | |
699 fProposalTable.setForeground(c); | |
700 | |
135 | 701 fProposalTable.addSelectionListener(new class() SelectionListener { |
129 | 702 |
703 public void widgetSelected(SelectionEvent e) {} | |
704 | |
705 public void widgetDefaultSelected(SelectionEvent e) { | |
706 insertSelectedProposalWithMask(e.stateMask); | |
707 } | |
708 }); | |
709 | |
710 fPopupCloser.install(fContentAssistant, fProposalTable, fAdditionalInfoController); | |
711 | |
135 | 712 fProposalShell.addDisposeListener(new class() DisposeListener { |
129 | 713 public void widgetDisposed(DisposeEvent e) { |
714 unregister(); // but don't dispose the shell, since we're being called from its disposal event! | |
715 } | |
716 }); | |
717 | |
718 fProposalTable.setHeaderVisible(false); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
719 |
129 | 720 addCommandSupport(fProposalTable); |
721 } | |
722 | |
723 /** | |
724 * Returns the minimal required height for the proposal, may return 0 if the popup has not been | |
725 * created yet. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
726 * |
129 | 727 * @return the minimal height |
728 * @since 3.3 | |
729 */ | |
730 int getMinimalHeight() { | |
731 int height= 0; | |
732 if (Helper.okToUse(fProposalTable)) { | |
733 int items= fProposalTable.getItemHeight() * 10; | |
734 Rectangle trim= fProposalTable.computeTrim(0, 0, DWT.DEFAULT, items); | |
735 height= trim.height; | |
736 } | |
737 if (Helper.okToUse(fMessageText)) | |
738 height+= fMessageText.getSize().y + 1; | |
739 return height; | |
740 } | |
741 | |
742 /** | |
743 * Adds command support to the given control. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
744 * |
129 | 745 * @param control the control to watch for focus |
746 * @since 3.2 | |
747 */ | |
158 | 748 private void addCommandSupport(Control control) { |
129 | 749 final KeySequence commandSequence= fContentAssistant.getRepeatedInvocationKeySequence(); |
750 if (commandSequence !is null && !commandSequence.isEmpty() && fContentAssistant.isRepeatedInvocationMode()) { | |
158 | 751 control.addFocusListener(new class(control,commandSequence) FocusListener { |
752 Control control_; | |
753 KeySequence commandSequence_; | |
754 this(Control a, KeySequence b){ | |
755 control_=a; | |
756 commandSequence_=b; | |
757 } | |
129 | 758 private CommandKeyListener fCommandKeyListener; |
759 public void focusGained(FocusEvent e) { | |
158 | 760 if (Helper.okToUse(control_)) { |
129 | 761 if (fCommandKeyListener is null) { |
158 | 762 fCommandKeyListener= new CommandKeyListener(commandSequence_); |
129 | 763 fProposalTable.addKeyListener(fCommandKeyListener); |
764 } | |
765 } | |
766 } | |
767 public void focusLost(FocusEvent e) { | |
768 if (fCommandKeyListener !is null) { | |
158 | 769 control_.removeKeyListener(fCommandKeyListener); |
129 | 770 fCommandKeyListener= null; |
771 } | |
772 } | |
773 }); | |
774 } | |
158 | 775 control.addFocusListener(new class(control) FocusListener { |
776 Control control_; | |
129 | 777 private TraverseListener fTraverseListener; |
158 | 778 this(Control a){ |
779 control_=a; | |
780 } | |
129 | 781 public void focusGained(FocusEvent e) { |
158 | 782 if (Helper.okToUse(control_)) { |
129 | 783 if (fTraverseListener is null) { |
135 | 784 fTraverseListener= new class() TraverseListener { |
129 | 785 public void keyTraversed(TraverseEvent event) { |
786 if (event.detail is DWT.TRAVERSE_TAB_NEXT) { | |
787 IInformationControl iControl= fAdditionalInfoController.getCurrentInformationControl2(); | |
788 if (fAdditionalInfoController.getInternalAccessor().canReplace(iControl)) { | |
789 fAdditionalInfoController.getInternalAccessor().replaceInformationControl(true); | |
790 event.doit= false; | |
791 } | |
792 } | |
793 } | |
794 }; | |
795 fProposalTable.addTraverseListener(fTraverseListener); | |
796 } | |
797 } | |
798 } | |
799 public void focusLost(FocusEvent e) { | |
800 if (fTraverseListener !is null) { | |
158 | 801 control_.removeTraverseListener(fTraverseListener); |
129 | 802 fTraverseListener= null; |
803 } | |
804 } | |
805 }); | |
806 } | |
807 | |
808 /** | |
809 * Returns the background color to use. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
810 * |
129 | 811 * @param control the control to get the display from |
812 * @return the background color | |
813 * @since 3.2 | |
814 */ | |
815 private Color getBackgroundColor(Control control) { | |
816 Color c= fContentAssistant.getProposalSelectorBackground(); | |
817 if (c is null) | |
818 c= JFaceResources.getColorRegistry().get(JFacePreferences.CONTENT_ASSIST_BACKGROUND_COLOR); | |
819 return c; | |
820 } | |
821 | |
822 /** | |
823 * Returns the foreground color to use. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
824 * |
129 | 825 * @param control the control to get the display from |
826 * @return the foreground color | |
827 * @since 3.2 | |
828 */ | |
829 private Color getForegroundColor(Control control) { | |
830 Color c= fContentAssistant.getProposalSelectorForeground(); | |
831 if (c is null) | |
832 c= JFaceResources.getColorRegistry().get(JFacePreferences.CONTENT_ASSIST_FOREGROUND_COLOR); | |
833 return c; | |
834 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
835 |
129 | 836 /** |
837 * Creates the caption line under the proposal table. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
838 * |
129 | 839 * @since 3.2 |
840 */ | |
841 private void createMessageText() { | |
842 if (fMessageText is null) { | |
843 fMessageText= new Label(fProposalShell, DWT.RIGHT); | |
844 GridData textData= new GridData(DWT.FILL, DWT.BOTTOM, true, false); | |
845 fMessageText.setLayoutData(textData); | |
162 | 846 fMessageText.setText(fContentAssistant.getStatusMessage() ~ " "); //$NON-NLS-1$ |
129 | 847 if (fMessageTextFont is null) { |
848 Font font= fMessageText.getFont(); | |
849 Display display= fProposalShell.getDisplay(); | |
850 FontData[] fontDatas= font.getFontData(); | |
851 for (int i= 0; i < fontDatas.length; i++) | |
852 fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10); | |
853 fMessageTextFont= new Font(display, fontDatas); | |
854 } | |
855 fMessageText.setFont(fMessageTextFont); | |
856 fMessageText.setBackground(getBackgroundColor(fProposalShell)); | |
857 fMessageText.setForeground(getForegroundColor(fProposalShell)); | |
858 | |
859 if (fContentAssistant.isRepeatedInvocationMode()) { | |
860 fMessageText.setCursor(fProposalShell.getDisplay().getSystemCursor(DWT.CURSOR_HAND)); | |
135 | 861 fMessageText.addMouseListener(new class() MouseAdapter { |
129 | 862 public void mouseUp(MouseEvent e) { |
863 fLastCompletionOffset= fFilterOffset; | |
864 fProposalTable.setFocus(); | |
865 handleRepeatedInvocation(); | |
866 } | |
867 | |
868 public void mouseDown(MouseEvent e) { | |
869 } | |
870 }); | |
871 } | |
872 } | |
873 } | |
874 | |
875 /* | |
876 * @since 3.1 | |
877 */ | |
878 private void handleSetData(Event event) { | |
134 | 879 TableItem item= cast(TableItem) event.item; |
129 | 880 int index= fProposalTable.indexOf(item); |
881 | |
882 if (0 <= index && index < fFilteredProposals.length) { | |
883 ICompletionProposal current= fFilteredProposals[index]; | |
884 | |
885 String displayString; | |
886 StyleRange[] styleRanges= null; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
887 if (fIsColoredLabelsSupportEnabled && cast(ICompletionProposalExtension6)current ) { |
134 | 888 StyledString styledString= (cast(ICompletionProposalExtension6)current).getStyledDisplayString(); |
129 | 889 displayString= styledString.getString(); |
890 styleRanges= styledString.getStyleRanges(); | |
891 } else | |
892 displayString= current.getDisplayString(); | |
893 | |
894 item.setText(displayString); | |
895 if (fIsColoredLabelsSupportEnabled) | |
896 TableOwnerDrawSupport.storeStyleRanges(item, 0, styleRanges); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
897 |
129 | 898 item.setImage(current.getImage()); |
162 | 899 item.setData(cast(Object)current); |
129 | 900 } else { |
901 // this should not happen, but does on win32 | |
902 } | |
903 } | |
904 | |
905 /** | |
906 * Returns the proposal selected in the proposal selector. | |
907 * | |
908 * @return the selected proposal | |
909 * @since 2.0 | |
910 */ | |
911 private ICompletionProposal getSelectedProposal() { | |
912 /* Make sure that there is no filter runnable pending. | |
913 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=31427 | |
914 */ | |
915 if (fIsFilterPending) | |
916 fFilterRunnable.run(); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
917 |
129 | 918 // filter runnable may have hidden the proposals |
919 if (!Helper.okToUse(fProposalTable)) | |
920 return null; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
921 |
129 | 922 int i= fProposalTable.getSelectionIndex(); |
923 if (fFilteredProposals is null || i < 0 || i >= fFilteredProposals.length) | |
924 return null; | |
925 return fFilteredProposals[i]; | |
926 } | |
927 | |
928 /** | |
929 * Takes the selected proposal and applies it. | |
930 * | |
931 * @param stateMask the state mask | |
932 * @since 3.2 | |
933 */ | |
934 private void insertSelectedProposalWithMask(int stateMask) { | |
935 ICompletionProposal p= getSelectedProposal(); | |
936 hide(); | |
937 if (p !is null) | |
134 | 938 insertProposal(p, cast(wchar) 0, stateMask, fContentAssistSubjectControlAdapter.getSelectedRange().x); |
129 | 939 } |
940 | |
941 /** | |
942 * Applies the given proposal at the given offset. The given character is the | |
943 * one that triggered the insertion of this proposal. | |
944 * | |
945 * @param p the completion proposal | |
946 * @param trigger the trigger character | |
947 * @param stateMask the state mask | |
948 * @param offset the offset | |
949 * @since 2.1 | |
950 */ | |
158 | 951 private void insertProposal(ICompletionProposal p, char trigger, int stateMask, int offset) { |
129 | 952 |
953 fInserting= true; | |
954 IRewriteTarget target= null; | |
158 | 955 IEditingSupport helper= new class(offset) IEditingSupport { |
956 int offset_; | |
957 this(int a){ | |
958 offset_=a; | |
959 } | |
129 | 960 public bool isOriginator(DocumentEvent event, IRegion focus) { |
158 | 961 return focus.getOffset() <= offset_ && focus.getOffset() + focus.getLength() >= offset_; |
129 | 962 } |
963 | |
964 public bool ownsFocusShell() { | |
965 return false; | |
966 } | |
967 | |
968 }; | |
969 | |
970 try { | |
971 | |
972 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); | |
973 | |
138 | 974 if ( cast(ITextViewerExtension)fViewer ) { |
134 | 975 ITextViewerExtension extension= cast(ITextViewerExtension) fViewer; |
129 | 976 target= extension.getRewriteTarget(); |
977 } | |
978 | |
979 if (target !is null) | |
980 target.beginCompoundChange(); | |
981 | |
138 | 982 if ( cast(IEditingSupportRegistry)fViewer ) { |
134 | 983 IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer; |
129 | 984 registry.register(helper); |
985 } | |
986 | |
987 | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
988 if (cast(ICompletionProposalExtension2)p && fViewer !is null) { |
134 | 989 ICompletionProposalExtension2 e= cast(ICompletionProposalExtension2) p; |
129 | 990 e.apply(fViewer, trigger, stateMask, offset); |
138 | 991 } else if ( cast(ICompletionProposalExtension)p ) { |
134 | 992 ICompletionProposalExtension e= cast(ICompletionProposalExtension) p; |
129 | 993 e.apply(document, trigger, offset); |
994 } else { | |
995 p.apply(document); | |
996 } | |
997 | |
998 Point selection= p.getSelection(document); | |
999 if (selection !is null) { | |
1000 fContentAssistSubjectControlAdapter.setSelectedRange(selection.x, selection.y); | |
1001 fContentAssistSubjectControlAdapter.revealRange(selection.x, selection.y); | |
1002 } | |
1003 | |
1004 IContextInformation info= p.getContextInformation(); | |
1005 if (info !is null) { | |
1006 | |
1007 int contextInformationOffset; | |
138 | 1008 if ( cast(ICompletionProposalExtension)p ) { |
134 | 1009 ICompletionProposalExtension e= cast(ICompletionProposalExtension) p; |
129 | 1010 contextInformationOffset= e.getContextInformationPosition(); |
1011 } else { | |
1012 if (selection is null) | |
1013 selection= fContentAssistSubjectControlAdapter.getSelectedRange(); | |
1014 contextInformationOffset= selection.x + selection.y; | |
1015 } | |
1016 | |
1017 fContentAssistant.showContextInformation(info, contextInformationOffset); | |
1018 } else | |
1019 fContentAssistant.showContextInformation(null, -1); | |
1020 | |
1021 | |
1022 } finally { | |
1023 if (target !is null) | |
1024 target.endCompoundChange(); | |
1025 | |
138 | 1026 if ( cast(IEditingSupportRegistry)fViewer ) { |
134 | 1027 IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer; |
129 | 1028 registry.unregister(helper); |
1029 } | |
1030 fInserting= false; | |
1031 } | |
1032 } | |
1033 | |
1034 /** | |
1035 * Returns whether this popup has the focus. | |
1036 * | |
1037 * @return <code>true</code> if the popup has the focus | |
1038 */ | |
1039 public bool hasFocus() { | |
1040 if (Helper.okToUse(fProposalShell)) { | |
1041 if ((fProposalShell.isFocusControl() || fProposalTable.isFocusControl())) | |
1042 return true; | |
1043 /* | |
1044 * We have to delegate this query to the additional info controller | |
1045 * as well, since the content assistant is the widget token owner | |
1046 * and its closer does not know that the additional info control can | |
1047 * now also take focus. | |
1048 */ | |
1049 if (fAdditionalInfoController !is null) { | |
1050 IInformationControl informationControl= fAdditionalInfoController.getCurrentInformationControl2(); | |
1051 if (informationControl !is null && informationControl.isFocusControl()) | |
1052 return true; | |
1053 InformationControlReplacer replacer= fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer(); | |
1054 if (replacer !is null) { | |
1055 informationControl= replacer.getCurrentInformationControl2(); | |
1056 if (informationControl !is null && informationControl.isFocusControl()) | |
1057 return true; | |
1058 } | |
1059 } | |
1060 } | |
1061 | |
1062 return false; | |
1063 } | |
1064 | |
1065 /** | |
1066 * Hides this popup. | |
1067 */ | |
1068 public void hide() { | |
1069 | |
1070 unregister(); | |
1071 | |
138 | 1072 if ( cast(IEditingSupportRegistry)fViewer ) { |
134 | 1073 IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer; |
129 | 1074 registry.unregister(fFocusHelper); |
1075 } | |
1076 | |
1077 if (Helper.okToUse(fProposalShell)) { | |
1078 | |
1079 fContentAssistant.removeContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR); | |
1080 | |
1081 fPopupCloser.uninstall(); | |
1082 fProposalShell.setVisible(false); | |
1083 fProposalShell.dispose(); | |
1084 fProposalShell= null; | |
1085 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1086 |
129 | 1087 if (fMessageTextFont !is null) { |
1088 fMessageTextFont.dispose(); | |
1089 fMessageTextFont= null; | |
1090 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1091 |
129 | 1092 if (fMessageText !is null) { |
1093 fMessageText= null; | |
1094 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1095 |
129 | 1096 fEmptyMessage= null; |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1097 |
129 | 1098 fLastCompletionOffset= -1; |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1099 |
129 | 1100 fContentAssistant.fireSessionEndEvent(); |
1101 } | |
1102 | |
1103 /** | |
1104 * Unregister this completion proposal popup. | |
1105 * | |
1106 * @since 3.0 | |
1107 */ | |
1108 private void unregister() { | |
1109 if (fDocumentListener !is null) { | |
1110 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); | |
1111 if (document !is null) | |
1112 document.removeDocumentListener(fDocumentListener); | |
1113 fDocumentListener= null; | |
1114 } | |
1115 fDocumentEvents.clear(); | |
1116 | |
1117 if (fKeyListener !is null && fContentAssistSubjectControlAdapter.getControl() !is null && !fContentAssistSubjectControlAdapter.getControl().isDisposed()) { | |
1118 fContentAssistSubjectControlAdapter.removeKeyListener(fKeyListener); | |
1119 fKeyListener= null; | |
1120 } | |
1121 | |
1122 if (fLastProposal !is null) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1123 if (cast(ICompletionProposalExtension2)fLastProposal && fViewer !is null) { |
134 | 1124 ICompletionProposalExtension2 extension= cast(ICompletionProposalExtension2) fLastProposal; |
129 | 1125 extension.unselected(fViewer); |
1126 } | |
1127 fLastProposal= null; | |
1128 } | |
1129 | |
1130 fFilteredProposals= null; | |
1131 fComputedProposals= null; | |
1132 | |
162 | 1133 fContentAssistant.possibleCompletionsClosed_package(); |
129 | 1134 } |
1135 | |
1136 /** | |
1137 *Returns whether this popup is active. It is active if the proposal selector is visible. | |
1138 * | |
1139 * @return <code>true</code> if this popup is active | |
1140 */ | |
1141 public bool isActive() { | |
1142 return fProposalShell !is null && !fProposalShell.isDisposed(); | |
1143 } | |
1144 | |
1145 /** | |
1146 * Initializes the proposal selector with these given proposals. | |
1147 * @param proposals the proposals | |
1148 * @param isFilteredSubset if <code>true</code>, the proposal table is | |
1149 * not cleared, but the proposals that are not in the passed array | |
1150 * are removed from the displayed set | |
1151 */ | |
1152 private void setProposals(ICompletionProposal[] proposals, bool isFilteredSubset) { | |
1153 ICompletionProposal[] oldProposals= fFilteredProposals; | |
1154 ICompletionProposal oldProposal= getSelectedProposal(); // may trigger filtering and a reentrant call to setProposals() | |
1155 if (oldProposals !is fFilteredProposals) // reentrant call was first - abort | |
1156 return; | |
1157 | |
1158 if (Helper.okToUse(fProposalTable)) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1159 if (cast(ICompletionProposalExtension2)oldProposal && fViewer !is null) |
134 | 1160 (cast(ICompletionProposalExtension2) oldProposal).unselected(fViewer); |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1161 |
129 | 1162 if (proposals is null || proposals.length is 0) { |
1163 fEmptyProposal.fOffset= fFilterOffset; | |
1164 fEmptyProposal.fDisplayString= fEmptyMessage !is null ? fEmptyMessage : JFaceTextMessages.getString("CompletionProposalPopup.no_proposals"); //$NON-NLS-1$ | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1165 proposals= [ fEmptyProposal ]; |
129 | 1166 } |
1167 | |
1168 fFilteredProposals= proposals; | |
1169 final int newLen= proposals.length; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1170 if (USE_VIRTUAL) { |
129 | 1171 fProposalTable.setItemCount(newLen); |
1172 fProposalTable.clearAll(); | |
1173 } else { | |
1174 fProposalTable.setRedraw(false); | |
1175 fProposalTable.setItemCount(newLen); | |
1176 TableItem[] items= fProposalTable.getItems(); | |
1177 for (int i= 0; i < items.length; i++) { | |
1178 TableItem item= items[i]; | |
1179 ICompletionProposal proposal= proposals[i]; | |
1180 item.setText(proposal.getDisplayString()); | |
1181 item.setImage(proposal.getImage()); | |
162 | 1182 item.setData(cast(Object)proposal); |
129 | 1183 } |
1184 fProposalTable.setRedraw(true); | |
1185 } | |
1186 | |
1187 Point currentLocation= fProposalShell.getLocation(); | |
1188 Point newLocation= getLocation(); | |
1189 if ((newLocation.x < currentLocation.x && newLocation.y is currentLocation.y) || newLocation.y < currentLocation.y) | |
1190 fProposalShell.setLocation(newLocation); | |
1191 | |
1192 selectProposal(0, false); | |
1193 } | |
1194 } | |
1195 | |
1196 /** | |
1197 * Returns the graphical location at which this popup should be made visible. | |
1198 * | |
1199 * @return the location of this popup | |
1200 */ | |
1201 private Point getLocation() { | |
1202 int caret= fContentAssistSubjectControlAdapter.getCaretOffset(); | |
162 | 1203 Rectangle location= fContentAssistant.getLayoutManager().computeBoundsBelowAbove_package(fProposalShell, fSize is null ? fProposalShell.getSize() : fSize, caret, this); |
129 | 1204 return Geometry.getLocation(location); |
1205 } | |
1206 | |
1207 /** | |
1208 * Returns the size of this completion proposal popup. | |
1209 * | |
1210 * @return a Point containing the size | |
1211 * @since 3.0 | |
1212 */ | |
1213 Point getSize() { | |
1214 return fSize; | |
1215 } | |
1216 | |
1217 /** | |
1218 * Displays this popup and install the additional info controller, so that additional info | |
1219 * is displayed when a proposal is selected and additional info is available. | |
1220 */ | |
1221 private void displayProposals() { | |
1222 | |
1223 if (!Helper.okToUse(fProposalShell) || !Helper.okToUse(fProposalTable)) | |
1224 return; | |
1225 | |
1226 if (fContentAssistant.addContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR)) { | |
1227 | |
1228 ensureDocumentListenerInstalled(); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1229 |
129 | 1230 if (fFocusHelper is null) { |
135 | 1231 fFocusHelper= new class() IEditingSupport { |
129 | 1232 |
1233 public bool isOriginator(DocumentEvent event, IRegion focus) { | |
1234 return false; // this helper just covers the focus change to the proposal shell, no remote editions | |
1235 } | |
1236 | |
1237 public bool ownsFocusShell() { | |
1238 return true; | |
1239 } | |
1240 | |
1241 }; | |
1242 } | |
138 | 1243 if ( cast(IEditingSupportRegistry)fViewer ) { |
134 | 1244 IEditingSupportRegistry registry= cast(IEditingSupportRegistry) fViewer; |
129 | 1245 registry.register(fFocusHelper); |
1246 } | |
1247 | |
1248 | |
1249 /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=52646 | |
1250 * on GTK, setVisible and such may run the event loop | |
1251 * (see also https://bugs.eclipse.org/bugs/show_bug.cgi?id=47511) | |
1252 * Since the user may have already canceled the popup or selected | |
1253 * an entry (ESC or RETURN), we have to double check whether | |
1254 * the table is still okToUse. See comments below | |
1255 */ | |
1256 fProposalShell.setVisible(true); // may run event loop on GTK | |
1257 // transfer focus since no verify key listener can be attached | |
1258 if (!fContentAssistSubjectControlAdapter.supportsVerifyKeyListener() && Helper.okToUse(fProposalShell)) | |
1259 fProposalShell.setFocus(); // may run event loop on GTK ?? | |
1260 | |
1261 if (fAdditionalInfoController !is null && Helper.okToUse(fProposalTable)) { | |
1262 fAdditionalInfoController.install(fProposalTable); | |
1263 fAdditionalInfoController.handleTableSelectionChanged(); | |
1264 } | |
1265 } else | |
1266 hide(); | |
1267 } | |
1268 | |
1269 /** | |
1270 * Installs the document listener if not already done. | |
1271 * | |
1272 * @since 3.2 | |
1273 */ | |
1274 private void ensureDocumentListenerInstalled() { | |
1275 if (fDocumentListener is null) { | |
135 | 1276 fDocumentListener= new class() IDocumentListener { |
129 | 1277 public void documentAboutToBeChanged(DocumentEvent event) { |
1278 if (!fInserting) | |
1279 fDocumentEvents.add(event); | |
1280 } | |
1281 | |
1282 public void documentChanged(DocumentEvent event) { | |
1283 if (!fInserting) | |
1284 filterProposals(); | |
1285 } | |
1286 }; | |
1287 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); | |
1288 if (document !is null) | |
1289 document.addDocumentListener(fDocumentListener); | |
1290 } | |
1291 } | |
1292 | |
1293 /* | |
1294 * @see IContentAssistListener#verifyKey(VerifyEvent) | |
1295 */ | |
1296 public bool verifyKey(VerifyEvent e) { | |
1297 if (!Helper.okToUse(fProposalShell)) | |
1298 return true; | |
1299 | |
1300 char key= e.character; | |
1301 if (key is 0) { | |
1302 int newSelection= fProposalTable.getSelectionIndex(); | |
1303 int visibleRows= (fProposalTable.getSize().y / fProposalTable.getItemHeight()) - 1; | |
1304 int itemCount= fProposalTable.getItemCount(); | |
1305 bool smartToggle= false; | |
1306 switch (e.keyCode) { | |
1307 | |
1308 case DWT.ARROW_LEFT : | |
1309 case DWT.ARROW_RIGHT : | |
1310 filterProposals(); | |
1311 return true; | |
1312 | |
1313 case DWT.ARROW_UP : | |
1314 newSelection -= 1; | |
1315 if (newSelection < 0) | |
1316 newSelection= itemCount - 1; | |
1317 break; | |
1318 | |
1319 case DWT.ARROW_DOWN : | |
1320 newSelection += 1; | |
1321 if (newSelection > itemCount - 1) | |
1322 newSelection= 0; | |
1323 break; | |
1324 | |
1325 case DWT.PAGE_DOWN : | |
1326 newSelection += visibleRows; | |
1327 if (newSelection >= itemCount) | |
1328 newSelection= itemCount - 1; | |
1329 break; | |
1330 | |
1331 case DWT.PAGE_UP : | |
1332 newSelection -= visibleRows; | |
1333 if (newSelection < 0) | |
1334 newSelection= 0; | |
1335 break; | |
1336 | |
1337 case DWT.HOME : | |
1338 newSelection= 0; | |
1339 break; | |
1340 | |
1341 case DWT.END : | |
1342 newSelection= itemCount - 1; | |
1343 break; | |
1344 | |
1345 default : | |
1346 if (e.keyCode !is DWT.CAPS_LOCK && e.keyCode !is DWT.MOD1 && e.keyCode !is DWT.MOD2 && e.keyCode !is DWT.MOD3 && e.keyCode !is DWT.MOD4) | |
1347 hide(); | |
1348 return true; | |
1349 } | |
1350 | |
1351 selectProposal(newSelection, smartToggle); | |
1352 | |
1353 e.doit= false; | |
1354 return false; | |
1355 | |
1356 } | |
1357 | |
1358 // key !is 0 | |
1359 switch (key) { | |
1360 case 0x1B: // Esc | |
1361 e.doit= false; | |
1362 hide(); | |
1363 break; | |
1364 | |
1365 case '\n': // Ctrl-Enter on w2k | |
1366 case '\r': // Enter | |
1367 e.doit= false; | |
1368 insertSelectedProposalWithMask(e.stateMask); | |
1369 break; | |
1370 | |
1371 case '\t': | |
1372 e.doit= false; | |
1373 fProposalShell.setFocus(); | |
1374 return false; | |
1375 | |
1376 default: | |
1377 ICompletionProposal p= getSelectedProposal(); | |
138 | 1378 if ( cast(ICompletionProposalExtension)p ) { |
134 | 1379 ICompletionProposalExtension t= cast(ICompletionProposalExtension) p; |
129 | 1380 char[] triggers= t.getTriggerCharacters(); |
1381 if (contains(triggers, key)) { | |
1382 e.doit= false; | |
1383 hide(); | |
1384 insertProposal(p, key, e.stateMask, fContentAssistSubjectControlAdapter.getSelectedRange().x); | |
1385 } | |
1386 } | |
1387 } | |
1388 | |
1389 return true; | |
1390 } | |
1391 | |
1392 /** | |
1393 * Selects the entry with the given index in the proposal selector and feeds | |
1394 * the selection to the additional info controller. | |
1395 * | |
1396 * @param index the index in the list | |
1397 * @param smartToggle <code>true</code> if the smart toggle key has been pressed | |
1398 * @since 2.1 | |
1399 */ | |
1400 private void selectProposal(int index, bool smartToggle) { | |
1401 | |
1402 ICompletionProposal oldProposal= getSelectedProposal(); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1403 if (cast(ICompletionProposalExtension2)oldProposal && fViewer !is null) |
134 | 1404 (cast(ICompletionProposalExtension2) oldProposal).unselected(fViewer); |
129 | 1405 |
1406 if (fFilteredProposals is null) { | |
1407 fireSelectionEvent(null, smartToggle); | |
1408 return; | |
1409 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1410 |
129 | 1411 ICompletionProposal proposal= fFilteredProposals[index]; |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1412 if (cast(ICompletionProposalExtension2)proposal && fViewer !is null) |
134 | 1413 (cast(ICompletionProposalExtension2) proposal).selected(fViewer, smartToggle); |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1414 |
129 | 1415 fireSelectionEvent(proposal, smartToggle); |
1416 | |
1417 fLastProposal= proposal; | |
1418 | |
1419 fProposalTable.setSelection(index); | |
1420 fProposalTable.showSelection(); | |
1421 if (fAdditionalInfoController !is null) | |
1422 fAdditionalInfoController.handleTableSelectionChanged(); | |
1423 } | |
1424 | |
1425 /** | |
1426 * Fires a selection event, see {@link ICompletionListener}. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1427 * |
129 | 1428 * @param proposal the selected proposal, possibly <code>null</code> |
1429 * @param smartToggle true if the smart toggle is on | |
1430 * @since 3.2 | |
1431 */ | |
1432 private void fireSelectionEvent(ICompletionProposal proposal, bool smartToggle) { | |
1433 fContentAssistant.fireSelectionEvent(proposal, smartToggle); | |
1434 } | |
1435 | |
1436 /** | |
1437 * Returns whether the given character is contained in the given array of | |
1438 * characters. | |
1439 * | |
1440 * @param characters the list of characters | |
1441 * @param c the character to look for in the list | |
1442 * @return <code>true</code> if character belongs to the list | |
1443 * @since 2.0 | |
1444 */ | |
1445 private bool contains(char[] characters, char c) { | |
1446 | |
1447 if (characters is null) | |
1448 return false; | |
1449 | |
1450 for (int i= 0; i < characters.length; i++) { | |
1451 if (c is characters[i]) | |
1452 return true; | |
1453 } | |
1454 | |
1455 return false; | |
1456 } | |
1457 | |
1458 /* | |
1459 * @see IEventConsumer#processEvent(VerifyEvent) | |
1460 */ | |
1461 public void processEvent(VerifyEvent e) { | |
1462 } | |
1463 | |
1464 /** | |
1465 * Filters the displayed proposal based on the given cursor position and the | |
1466 * offset of the original invocation of the content assistant. | |
1467 */ | |
1468 private void filterProposals() { | |
1469 if (!fIsFilterPending) { | |
1470 fIsFilterPending= true; | |
1471 Control control= fContentAssistSubjectControlAdapter.getControl(); | |
1472 control.getDisplay().asyncExec(fFilterRunnable); | |
1473 } | |
1474 } | |
1475 | |
1476 /** | |
1477 * Computes the subset of already computed proposals that are still valid for | |
1478 * the given offset. | |
1479 * | |
1480 * @param offset the offset | |
1481 * @param event the merged document event | |
1482 * @return the set of filtered proposals | |
1483 * @since 3.0 | |
1484 */ | |
1485 private ICompletionProposal[] computeFilteredProposals(int offset, DocumentEvent event) { | |
1486 | |
1487 if (offset is fInvocationOffset && event is null) { | |
1488 fIsFilteredSubset= false; | |
1489 return fComputedProposals; | |
1490 } | |
1491 | |
1492 if (offset < fInvocationOffset) { | |
1493 fIsFilteredSubset= false; | |
1494 fInvocationOffset= offset; | |
1495 fContentAssistant.fireSessionRestartEvent(); | |
1496 fComputedProposals= computeProposals(fInvocationOffset); | |
1497 return fComputedProposals; | |
1498 } | |
1499 | |
1500 ICompletionProposal[] proposals; | |
1501 if (offset < fFilterOffset) { | |
1502 proposals= fComputedProposals; | |
1503 fIsFilteredSubset= false; | |
1504 } else { | |
1505 proposals= fFilteredProposals; | |
1506 fIsFilteredSubset= true; | |
1507 } | |
1508 | |
1509 if (proposals is null) { | |
1510 fIsFilteredSubset= false; | |
1511 return null; | |
1512 } | |
1513 | |
1514 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); | |
1515 int length= proposals.length; | |
1516 List filtered= new ArrayList(length); | |
1517 for (int i= 0; i < length; i++) { | |
1518 | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1519 if (cast(ICompletionProposalExtension2)proposals[i] ) { |
129 | 1520 |
134 | 1521 ICompletionProposalExtension2 p= cast(ICompletionProposalExtension2) proposals[i]; |
129 | 1522 if (p.validate(document, offset, event)) |
162 | 1523 filtered.add(cast(Object)p); |
129 | 1524 |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1525 } else if (cast(ICompletionProposalExtension)proposals[i] ) { |
129 | 1526 |
134 | 1527 ICompletionProposalExtension p= cast(ICompletionProposalExtension) proposals[i]; |
129 | 1528 if (p.isValidFor(document, offset)) |
162 | 1529 filtered.add(cast(Object)p); |
129 | 1530 |
1531 } else { | |
1532 // restore original behavior | |
1533 fIsFilteredSubset= false; | |
1534 fInvocationOffset= offset; | |
1535 fContentAssistant.fireSessionRestartEvent(); | |
1536 fComputedProposals= computeProposals(fInvocationOffset); | |
1537 return fComputedProposals; | |
1538 } | |
1539 } | |
1540 | |
158 | 1541 return arraycast!(ICompletionProposal)( filtered.toArray()); |
129 | 1542 } |
1543 | |
1544 /** | |
1545 * Requests the proposal shell to take focus. | |
1546 * | |
1547 * @since 3.0 | |
1548 */ | |
1549 public void setFocus() { | |
1550 if (Helper.okToUse(fProposalShell)) { | |
1551 fProposalShell.setFocus(); | |
1552 } | |
1553 } | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1554 |
129 | 1555 /** |
1556 * Returns <code>true</code> if <code>proposal</code> should be auto-inserted, | |
1557 * <code>false</code> otherwise. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1558 * |
129 | 1559 * @param proposal the single proposal that might be automatically inserted |
1560 * @return <code>true</code> if <code>proposal</code> can be inserted automatically, | |
1561 * <code>false</code> otherwise | |
1562 * @since 3.1 | |
1563 */ | |
1564 private bool canAutoInsert(ICompletionProposal proposal) { | |
1565 if (fContentAssistant.isAutoInserting()) { | |
138 | 1566 if ( cast(ICompletionProposalExtension4)proposal ) { |
134 | 1567 ICompletionProposalExtension4 ext= cast(ICompletionProposalExtension4) proposal; |
129 | 1568 return ext.isAutoInsertable(); |
1569 } | |
1570 return true; // default behavior before ICompletionProposalExtension4 was introduced | |
1571 } | |
1572 return false; | |
1573 } | |
1574 | |
1575 /** | |
1576 * Completes the common prefix of all proposals directly in the code. If no | |
1577 * common prefix can be found, the proposal popup is shown. | |
1578 * | |
1579 * @return an error message if completion failed. | |
1580 * @since 3.0 | |
1581 */ | |
1582 public String incrementalComplete() { | |
1583 if (Helper.okToUse(fProposalShell) && fFilteredProposals !is null) { | |
1584 if (fLastCompletionOffset is fFilterOffset) { | |
1585 handleRepeatedInvocation(); | |
1586 } else { | |
1587 fLastCompletionOffset= fFilterOffset; | |
1588 completeCommonPrefix(); | |
1589 } | |
1590 } else { | |
1591 final Control control= fContentAssistSubjectControlAdapter.getControl(); | |
1592 | |
1593 if (fKeyListener is null) | |
1594 fKeyListener= new ProposalSelectionListener(); | |
1595 | |
1596 if (!Helper.okToUse(fProposalShell) && !control.isDisposed()) | |
1597 fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener); | |
1598 | |
135 | 1599 BusyIndicator.showWhile(control.getDisplay(), new class() Runnable { |
129 | 1600 public void run() { |
1601 | |
1602 fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x; | |
1603 fFilterOffset= fInvocationOffset; | |
1604 fLastCompletionOffset= fFilterOffset; | |
1605 fFilteredProposals= computeProposals(fInvocationOffset); | |
1606 | |
1607 int count= (fFilteredProposals is null ? 0 : fFilteredProposals.length); | |
1608 if (count is 0 && hideWhenNoProposals(false)) | |
1609 return; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1610 |
129 | 1611 if (count is 1 && canAutoInsert(fFilteredProposals[0])) { |
134 | 1612 insertProposal(fFilteredProposals[0], cast(wchar) 0, 0, fInvocationOffset); |
129 | 1613 hide(); |
1614 } else { | |
1615 ensureDocumentListenerInstalled(); | |
1616 if (count > 0 && completeCommonPrefix()) | |
1617 hide(); | |
1618 else { | |
1619 fComputedProposals= fFilteredProposals; | |
1620 createProposalSelector(); | |
1621 setProposals(fComputedProposals, false); | |
1622 displayProposals(); | |
1623 } | |
1624 } | |
1625 } | |
1626 }); | |
1627 } | |
1628 return getErrorMessage(); | |
1629 } | |
1630 | |
1631 /** | |
1632 * Acts upon <code>fFilteredProposals</code>: if there is just one valid | |
1633 * proposal, it is inserted, otherwise, the common prefix of all proposals | |
1634 * is inserted into the document. If there is no common prefix, nothing | |
1635 * happens and <code>false</code> is returned. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1636 * |
129 | 1637 * @return <code>true</code> if a single proposal was inserted and the |
1638 * selector can be closed, <code>false</code> otherwise | |
1639 * @since 3.0 | |
1640 */ | |
1641 private bool completeCommonPrefix() { | |
1642 | |
1643 // 0: insert single proposals | |
1644 if (fFilteredProposals.length is 1) { | |
1645 if (canAutoInsert(fFilteredProposals[0])) { | |
134 | 1646 insertProposal(fFilteredProposals[0], cast(wchar) 0, 0, fFilterOffset); |
129 | 1647 hide(); |
1648 return true; | |
1649 } | |
1650 return false; | |
1651 } | |
1652 | |
1653 // 1: extract pre- and postfix from all remaining proposals | |
1654 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); | |
1655 | |
1656 // contains the common postfix in the case that there are any proposals matching our LHS | |
162 | 1657 StringBuffer rightCasePostfix; |
129 | 1658 List rightCase= new ArrayList(); |
1659 | |
1660 bool isWrongCaseMatch= false; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1661 |
129 | 1662 // the prefix of all case insensitive matches. This differs from the document |
1663 // contents and will be replaced. | |
1664 CharSequence wrongCasePrefix= null; | |
1665 int wrongCasePrefixStart= 0; | |
1666 // contains the common postfix of all case-insensitive matches | |
162 | 1667 StringBuffer wrongCasePostfix; |
129 | 1668 List wrongCase= new ArrayList(); |
1669 | |
1670 for (int i= 0; i < fFilteredProposals.length; i++) { | |
1671 ICompletionProposal proposal= fFilteredProposals[i]; | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1672 |
138 | 1673 if (!( cast(ICompletionProposalExtension3)proposal )) |
129 | 1674 return false; |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1675 |
134 | 1676 int start= (cast(ICompletionProposalExtension3)proposal).getPrefixCompletionStart(fContentAssistSubjectControlAdapter.getDocument(), fFilterOffset); |
1677 CharSequence insertion= (cast(ICompletionProposalExtension3)proposal).getPrefixCompletionText(fContentAssistSubjectControlAdapter.getDocument(), fFilterOffset); | |
129 | 1678 if (insertion is null) |
162 | 1679 insertion= new StringCharSequence(proposal.getDisplayString()); |
129 | 1680 try { |
1681 int prefixLength= fFilterOffset - start; | |
1682 int relativeCompletionOffset= Math.min(insertion.length(), prefixLength); | |
1683 String prefix= document.get(start, prefixLength); | |
1684 if (!isWrongCaseMatch && insertion.toString().startsWith(prefix)) { | |
1685 isWrongCaseMatch= false; | |
162 | 1686 rightCase.add(cast(Object)proposal); |
129 | 1687 CharSequence newPostfix= insertion.subSequence(relativeCompletionOffset, insertion.length()); |
1688 if (rightCasePostfix is null) | |
1689 rightCasePostfix= new StringBuffer(newPostfix.toString()); | |
1690 else | |
1691 truncatePostfix(rightCasePostfix, newPostfix); | |
1692 } else if (i is 0 || isWrongCaseMatch) { | |
1693 CharSequence newPrefix= insertion.subSequence(0, relativeCompletionOffset); | |
1694 if (isPrefixCompatible(wrongCasePrefix, wrongCasePrefixStart, newPrefix, start, document)) { | |
1695 isWrongCaseMatch= true; | |
1696 wrongCasePrefix= newPrefix; | |
1697 wrongCasePrefixStart= start; | |
1698 CharSequence newPostfix= insertion.subSequence(relativeCompletionOffset, insertion.length()); | |
1699 if (wrongCasePostfix is null) | |
1700 wrongCasePostfix= new StringBuffer(newPostfix.toString()); | |
1701 else | |
1702 truncatePostfix(wrongCasePostfix, newPostfix); | |
162 | 1703 wrongCase.add(cast(Object)proposal); |
129 | 1704 } else { |
1705 return false; | |
1706 } | |
1707 } else | |
1708 return false; | |
1709 } catch (BadLocationException e2) { | |
1710 // bail out silently | |
1711 return false; | |
1712 } | |
1713 | |
1714 if (rightCasePostfix !is null && rightCasePostfix.length() is 0 && rightCase.size() > 1) | |
1715 return false; | |
1716 } | |
1717 | |
1718 // 2: replace single proposals | |
1719 | |
1720 if (rightCase.size() is 1) { | |
134 | 1721 ICompletionProposal proposal= cast(ICompletionProposal) rightCase.get(0); |
129 | 1722 if (canAutoInsert(proposal) && rightCasePostfix.length() > 0) { |
134 | 1723 insertProposal(proposal, cast(wchar) 0, 0, fInvocationOffset); |
129 | 1724 hide(); |
1725 return true; | |
1726 } | |
1727 return false; | |
1728 } else if (isWrongCaseMatch && wrongCase.size() is 1) { | |
134 | 1729 ICompletionProposal proposal= cast(ICompletionProposal) wrongCase.get(0); |
129 | 1730 if (canAutoInsert(proposal)) { |
134 | 1731 insertProposal(proposal, cast(wchar) 0, 0, fInvocationOffset); |
129 | 1732 hide(); |
1733 return true; | |
1734 } | |
1735 return false; | |
1736 } | |
1737 | |
1738 // 3: replace post- / prefixes | |
1739 | |
1740 CharSequence prefix; | |
1741 if (isWrongCaseMatch) | |
1742 prefix= wrongCasePrefix; | |
1743 else | |
162 | 1744 prefix= new StringCharSequence(""); //$NON-NLS-1$ |
129 | 1745 |
1746 CharSequence postfix; | |
1747 if (isWrongCaseMatch) | |
162 | 1748 postfix= new StringCharSequence(wrongCasePostfix.toString); |
129 | 1749 else |
162 | 1750 postfix= new StringCharSequence(rightCasePostfix.toString); |
129 | 1751 |
1752 if (prefix is null || postfix is null) | |
1753 return false; | |
1754 | |
1755 try { | |
1756 // 4: check if parts of the postfix are already in the document | |
1757 int to= Math.min(document.getLength(), fFilterOffset + postfix.length()); | |
1758 StringBuffer inDocument= new StringBuffer(document.get(fFilterOffset, to - fFilterOffset)); | |
1759 truncatePostfix(inDocument, postfix); | |
1760 | |
1761 // 5: replace and reveal | |
162 | 1762 document.replace(fFilterOffset - prefix.length(), prefix.length() + inDocument.length(), prefix.toString() ~ postfix.toString()); |
129 | 1763 |
1764 fContentAssistSubjectControlAdapter.setSelectedRange(fFilterOffset + postfix.length(), 0); | |
1765 fContentAssistSubjectControlAdapter.revealRange(fFilterOffset + postfix.length(), 0); | |
1766 fFilterOffset+= postfix.length(); | |
1767 fLastCompletionOffset= fFilterOffset; | |
1768 | |
1769 return false; | |
1770 } catch (BadLocationException e) { | |
1771 // ignore and return false | |
1772 return false; | |
1773 } | |
1774 } | |
1775 | |
1776 /* | |
1777 * @since 3.1 | |
1778 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
1779 private bool isPrefixCompatible(CharSequence oneSequence, int oneOffset, CharSequence twoSequence, int twoOffset, IDocument document) { |
129 | 1780 if (oneSequence is null || twoSequence is null) |
1781 return true; | |
1782 | |
1783 int min= Math.min(oneOffset, twoOffset); | |
1784 int oneEnd= oneOffset + oneSequence.length(); | |
1785 int twoEnd= twoOffset + twoSequence.length(); | |
1786 | |
162 | 1787 String one= document.get(oneOffset, min - oneOffset) ~ oneSequence.toString ~ document.get(oneEnd, Math.min(fFilterOffset, fFilterOffset - oneEnd)); |
1788 String two= document.get(twoOffset, min - twoOffset) ~ twoSequence.toString ~ document.get(twoEnd, Math.min(fFilterOffset, fFilterOffset - twoEnd)); | |
129 | 1789 |
1790 return one.equals(two); | |
1791 } | |
1792 | |
1793 /** | |
1794 * Truncates <code>buffer</code> to the common prefix of <code>buffer</code> | |
1795 * and <code>sequence</code>. | |
1796 * | |
1797 * @param buffer the common postfix to truncate | |
1798 * @param sequence the characters to truncate with | |
1799 */ | |
1800 private void truncatePostfix(StringBuffer buffer, CharSequence sequence) { | |
1801 // find common prefix | |
1802 int min= Math.min(buffer.length(), sequence.length()); | |
1803 for (int c= 0; c < min; c++) { | |
162 | 1804 if (sequence.charAt(c) !is buffer.slice()[c]) { |
1805 buffer.truncate(c); | |
129 | 1806 return; |
1807 } | |
1808 } | |
1809 | |
1810 // all equal up to minimum | |
162 | 1811 buffer.truncate(min); |
129 | 1812 } |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1813 |
129 | 1814 /** |
1815 * Sets the message for the repetition affordance text at the bottom of the proposal. Only has | |
1816 * an effect if {@link ContentAssistant#isRepeatedInvocationMode()} returns <code>true</code>. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1817 * |
129 | 1818 * @param message the new caption |
1819 * @since 3.2 | |
1820 */ | |
1821 void setMessage(String message) { | |
1822 Assert.isNotNull(message); | |
1823 if (isActive() && fMessageText !is null) | |
162 | 1824 fMessageText.setText(message ~ " "); //$NON-NLS-1$ |
129 | 1825 } |
1826 | |
1827 /** | |
1828 * Sets the text to be displayed if no proposals are available. Only has an effect if | |
1829 * {@link ContentAssistant#isShowEmptyList()} returns <code>true</code>. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1830 * |
129 | 1831 * @param message the empty message |
1832 * @since 3.2 | |
1833 */ | |
1834 void setEmptyMessage(String message) { | |
1835 Assert.isNotNull(message); | |
1836 fEmptyMessage= message; | |
1837 } | |
1838 | |
1839 /** | |
1840 * Enables or disables showing of the caption line. See also {@link #setMessage(String)}. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1841 * |
129 | 1842 * @param show |
1843 * @since 3.2 | |
1844 */ | |
1845 public void setStatusLineVisible(bool show) { | |
1846 if (!isActive() || show is (fMessageText !is null)) | |
1847 return; // nothing to do | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1848 |
129 | 1849 if (show) { |
1850 createMessageText(); | |
1851 } else { | |
1852 fMessageText.dispose(); | |
1853 fMessageText= null; | |
1854 } | |
1855 fProposalShell.layout(); | |
1856 } | |
1857 | |
1858 /** | |
1859 * Informs the popup that it is being placed above the caret line instead of below. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1860 * |
129 | 1861 * @param above <code>true</code> if the location of the popup is above the caret line, <code>false</code> if it is below |
1862 * @since 3.3 | |
1863 */ | |
1864 void switchedPositionToAbove(bool above) { | |
1865 if (fAdditionalInfoController !is null) { | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1866 fAdditionalInfoController.setFallbackAnchors([ |
129 | 1867 AbstractInformationControlManager.ANCHOR_RIGHT, |
1868 AbstractInformationControlManager.ANCHOR_LEFT, | |
1869 above ? AbstractInformationControlManager.ANCHOR_TOP : AbstractInformationControlManager.ANCHOR_BOTTOM | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1870 ]); |
129 | 1871 } |
1872 } | |
1873 | |
1874 /** | |
1875 * Returns a new proposal selection handler. | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
1876 * |
129 | 1877 * @param operationCode the operation code |
1878 * @return the handler | |
1879 * @since 3.4 | |
1880 */ | |
1881 IHandler createProposalSelectionHandler(int operationCode) { | |
1882 return new ProposalSelectionHandler(operationCode); | |
1883 } | |
1884 | |
1885 } |