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