Mercurial > projects > dwt-addons
annotate dwtx/jface/internal/text/link/contentassist/ContentAssistant2.d @ 158:25f1f92fa3df
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 26 Aug 2008 02:46:34 +0200 |
parents | f70d9508c95c |
children | 1a5b8f8129df |
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 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.internal.text.link.contentassist.ContentAssistant2; | |
14 | |
131 | 15 import dwtx.jface.internal.text.link.contentassist.IProposalListener; // packageimport |
16 import dwtx.jface.internal.text.link.contentassist.LineBreakingReader; // packageimport | |
17 import dwtx.jface.internal.text.link.contentassist.CompletionProposalPopup2; // packageimport | |
18 import dwtx.jface.internal.text.link.contentassist.ContextInformationPopup2; // packageimport | |
19 import dwtx.jface.internal.text.link.contentassist.ContentAssistMessages; // packageimport | |
20 import dwtx.jface.internal.text.link.contentassist.Helper2; // packageimport | |
21 import dwtx.jface.internal.text.link.contentassist.PopupCloser2; // packageimport | |
22 import dwtx.jface.internal.text.link.contentassist.IContentAssistListener2; // packageimport | |
23 import dwtx.jface.internal.text.link.contentassist.AdditionalInfoController2; // packageimport | |
24 | |
129 | 25 import dwt.dwthelper.utils; |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
147
diff
changeset
|
26 import dwtx.dwtxhelper.Collection; |
158 | 27 import tango.core.Thread; |
129 | 28 |
29 import dwt.DWT; | |
30 import dwt.DWTError; | |
31 import dwt.custom.StyledText; | |
32 import dwt.custom.VerifyKeyListener; | |
33 import dwt.events.ControlEvent; | |
34 import dwt.events.ControlListener; | |
35 import dwt.events.DisposeEvent; | |
36 import dwt.events.DisposeListener; | |
37 import dwt.events.FocusEvent; | |
38 import dwt.events.FocusListener; | |
39 import dwt.events.MouseEvent; | |
40 import dwt.events.MouseListener; | |
41 import dwt.events.VerifyEvent; | |
42 import dwt.graphics.Color; | |
43 import dwt.graphics.Point; | |
44 import dwt.graphics.Rectangle; | |
45 import dwt.widgets.Control; | |
46 import dwt.widgets.Display; | |
47 import dwt.widgets.Event; | |
48 import dwt.widgets.Listener; | |
49 import dwt.widgets.Shell; | |
50 import dwt.widgets.Widget; | |
51 import dwtx.core.runtime.Assert; | |
52 import dwtx.jface.text.BadLocationException; | |
53 import dwtx.jface.text.DefaultInformationControl; | |
54 import dwtx.jface.text.IEventConsumer; | |
55 import dwtx.jface.text.IInformationControl; | |
56 import dwtx.jface.text.IInformationControlCreator; | |
57 import dwtx.jface.text.ITextViewer; | |
58 import dwtx.jface.text.ITextViewerExtension; | |
59 import dwtx.jface.text.IViewportListener; | |
60 import dwtx.jface.text.IWidgetTokenKeeper; | |
61 import dwtx.jface.text.IWidgetTokenKeeperExtension; | |
62 import dwtx.jface.text.IWidgetTokenOwner; | |
63 import dwtx.jface.text.IWidgetTokenOwnerExtension; | |
64 import dwtx.jface.text.TextUtilities; | |
65 import dwtx.jface.text.contentassist.CompletionProposal; | |
66 import dwtx.jface.text.contentassist.ICompletionProposal; | |
67 import dwtx.jface.text.contentassist.ICompletionProposalExtension6; | |
68 import dwtx.jface.text.contentassist.IContentAssistProcessor; | |
69 import dwtx.jface.text.contentassist.IContentAssistant; | |
70 import dwtx.jface.text.contentassist.IContentAssistantExtension; | |
71 import dwtx.jface.text.contentassist.IContextInformation; | |
72 import dwtx.jface.text.contentassist.IContextInformationPresenter; | |
73 import dwtx.jface.text.contentassist.IContextInformationValidator; | |
74 | |
75 | |
76 /** | |
77 * A custom implementation of the <code>IContentAssistant</code> interface. | |
78 * This implementation is used by the linked mode UI. This is internal and subject | |
79 * to change without notice. | |
80 */ | |
81 public class ContentAssistant2 : IContentAssistant, IContentAssistantExtension, IWidgetTokenKeeper, IWidgetTokenKeeperExtension { | |
82 | |
83 /** | |
84 * A generic closer class used to monitor various | |
85 * interface events in order to determine whether | |
86 * content-assist should be terminated and all | |
87 * associated windows closed. | |
88 */ | |
89 class Closer : ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener { | |
90 | |
91 /** The shell on which we add listeners. */ | |
92 private Shell fShell; | |
93 private long fViewportListenerStartTime; | |
94 | |
95 /** | |
96 * Installs this closer on it's viewer's text widget. | |
97 */ | |
98 protected void install() { | |
99 Control w= fViewer.getTextWidget(); | |
100 if (Helper2.okToUse(w)) { | |
101 | |
102 Shell shell= w.getShell(); | |
103 fShell= shell; | |
104 shell.addControlListener(this); | |
105 | |
106 w.addMouseListener(this); | |
107 w.addFocusListener(this); | |
108 | |
109 /* | |
110 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors | |
111 */ | |
112 w.addDisposeListener(this); | |
113 } | |
114 | |
115 fViewer.addViewportListener(this); | |
116 fViewportListenerStartTime= System.currentTimeMillis() + 500; | |
117 } | |
118 | |
119 /** | |
120 * Uninstalls this closer from the viewer's text widget. | |
121 */ | |
122 protected void uninstall() { | |
123 Shell shell= fShell; | |
124 fShell= null; | |
125 if (Helper2.okToUse(shell)) | |
126 shell.removeControlListener(this); | |
127 | |
128 Control w= fViewer.getTextWidget(); | |
129 if (Helper2.okToUse(w)) { | |
130 | |
131 w.removeMouseListener(this); | |
132 w.removeFocusListener(this); | |
133 | |
134 /* | |
135 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors | |
136 */ | |
137 w.removeDisposeListener(this); | |
138 } | |
139 | |
140 fViewer.removeViewportListener(this); | |
141 } | |
142 | |
143 /* | |
144 * @see ControlListener#controlResized(ControlEvent) | |
145 */ | |
146 public void controlResized(ControlEvent e) { | |
147 hide(); | |
148 } | |
149 | |
150 /* | |
151 * @see ControlListener#controlMoved(ControlEvent) | |
152 */ | |
153 public void controlMoved(ControlEvent e) { | |
154 hide(); | |
155 } | |
156 | |
157 /* | |
158 * @see MouseListener#mouseDown(MouseEvent) | |
159 */ | |
160 public void mouseDown(MouseEvent e) { | |
161 hide(); | |
162 } | |
163 | |
164 /* | |
165 * @see MouseListener#mouseUp(MouseEvent) | |
166 */ | |
167 public void mouseUp(MouseEvent e) { | |
168 } | |
169 | |
170 /* | |
171 * @see MouseListener#mouseDoubleClick(MouseEvent) | |
172 */ | |
173 public void mouseDoubleClick(MouseEvent e) { | |
174 hide(); | |
175 } | |
176 | |
177 /* | |
178 * @see FocusListener#focusGained(FocusEvent) | |
179 */ | |
180 public void focusGained(FocusEvent e) { | |
181 } | |
182 | |
183 /* | |
184 * @see FocusListener#focusLost(FocusEvent) | |
185 */ | |
186 public void focusLost(FocusEvent e) { | |
187 if (fViewer !is null) { | |
188 Control control= fViewer.getTextWidget(); | |
189 if (control !is null) { | |
190 Display d= control.getDisplay(); | |
191 if (d !is null) { | |
135 | 192 d.asyncExec(new class() Runnable { |
129 | 193 public void run() { |
194 if (!hasFocus()) | |
195 hide(); | |
196 } | |
197 }); | |
198 } | |
199 } | |
200 } | |
201 } | |
202 | |
203 /* | |
204 * @seeDisposeListener#widgetDisposed(DisposeEvent) | |
205 */ | |
206 public void widgetDisposed(DisposeEvent e) { | |
207 /* | |
208 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal Errors | |
209 */ | |
210 hide(); | |
211 } | |
212 | |
213 /* | |
214 * @see IViewportListener#viewportChanged(int) | |
215 */ | |
216 public void viewportChanged(int topIndex) { | |
217 if (System.currentTimeMillis() > fViewportListenerStartTime) | |
218 hide(); | |
219 } | |
220 } | |
221 | |
222 /** | |
223 * An implementation of <code>IContentAssistListener</code>, this class is | |
224 * used to monitor key events in support of automatic activation | |
225 * of the content assistant. If enabled, the implementation utilizes a | |
226 * thread to watch for input characters matching the activation | |
227 * characters specified by the content assist processor, and if | |
228 * detected, will wait the indicated delay interval before | |
229 * activating the content assistant. | |
230 */ | |
231 class AutoAssistListener : VerifyKeyListener, Runnable { | |
232 | |
233 private Thread fThread; | |
234 private bool fIsReset= false; | |
235 private Object fMutex= new Object(); | |
236 private int fShowStyle; | |
237 | |
146 | 238 private const static int SHOW_PROPOSALS= 1; |
239 private const static int SHOW_CONTEXT_INFO= 2; | |
129 | 240 |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
241 protected this() { |
129 | 242 } |
243 | |
244 protected void start(int showStyle) { | |
245 fShowStyle= showStyle; | |
158 | 246 fThread= new Thread(&run); |
247 fThread.name = ContentAssistMessages.getString("ContentAssistant.assist_delay_timer_name"); //$NON-NLS-1$ | |
129 | 248 fThread.start(); |
249 } | |
250 | |
251 public void run() { | |
252 try { | |
253 while (true) { | |
254 synchronized (fMutex) { | |
255 if (fAutoActivationDelay !is 0) | |
256 fMutex.wait(fAutoActivationDelay); | |
257 if (fIsReset) { | |
258 fIsReset= false; | |
259 continue; | |
260 } | |
261 } | |
262 showAssist(fShowStyle); | |
263 break; | |
264 } | |
265 } catch (InterruptedException e) { | |
266 } | |
267 fThread= null; | |
268 } | |
269 | |
270 protected void reset(int showStyle) { | |
271 synchronized (fMutex) { | |
272 fShowStyle= showStyle; | |
273 fIsReset= true; | |
274 fMutex.notifyAll(); | |
275 } | |
276 } | |
277 | |
278 protected void stop() { | |
279 Thread threadToStop= fThread; | |
280 if (threadToStop !is null) | |
281 threadToStop.interrupt(); | |
282 } | |
283 | |
284 private bool contains(char[] characters, char character) { | |
285 if (characters !is null) { | |
286 for (int i= 0; i < characters.length; i++) { | |
287 if (character is characters[i]) | |
288 return true; | |
289 } | |
290 } | |
291 return false; | |
292 } | |
293 | |
294 public void verifyKey(VerifyEvent e) { | |
295 // Only act on typed characters and ignore modifier-only events | |
296 if (e.character is 0 && (e.keyCode & DWT.KEYCODE_BIT) is 0) | |
297 return; | |
298 | |
299 if (e.character !is 0 && (e.stateMask is DWT.ALT)) | |
300 return; | |
301 | |
302 int showStyle; | |
303 int pos= fViewer.getSelectedRange().x; | |
304 char[] activation= getCompletionProposalAutoActivationCharacters(fViewer, pos); | |
305 | |
306 if (contains(activation, e.character) && !fProposalPopup.isActive()) | |
307 showStyle= SHOW_PROPOSALS; | |
308 else { | |
309 activation= getContextInformationAutoActivationCharacters(fViewer, pos); | |
310 if (contains(activation, e.character) && !fContextInfoPopup.isActive()) | |
311 showStyle= SHOW_CONTEXT_INFO; | |
312 else { | |
313 if (fThread !is null && fThread.isAlive()) | |
314 stop(); | |
315 return; | |
316 } | |
317 } | |
318 | |
319 if (fThread !is null && fThread.isAlive()) | |
320 reset(showStyle); | |
321 else | |
322 start(showStyle); | |
323 } | |
324 | |
158 | 325 protected void showAssist(int showStyle) { |
129 | 326 Control control= fViewer.getTextWidget(); |
327 Display d= control.getDisplay(); | |
328 if (d !is null) { | |
329 try { | |
135 | 330 d.syncExec(new class() Runnable { |
129 | 331 public void run() { |
332 if (showStyle is SHOW_PROPOSALS) | |
333 fProposalPopup.showProposals(true); | |
334 else if (showStyle is SHOW_CONTEXT_INFO) | |
335 fContextInfoPopup.showContextProposals(true); | |
336 } | |
337 }); | |
338 } catch (DWTError e) { | |
339 } | |
340 } | |
341 } | |
342 } | |
343 | |
344 /** | |
345 * The layout manager layouts the various | |
346 * windows associated with the content assistant based on the | |
347 * settings of the content assistant. | |
348 */ | |
349 class LayoutManager : Listener { | |
350 | |
351 // Presentation types. | |
352 /** proposal selector */ | |
146 | 353 public const static int LAYOUT_PROPOSAL_SELECTOR= 0; |
129 | 354 /** context selector */ |
146 | 355 public const static int LAYOUT_CONTEXT_SELECTOR= 1; |
129 | 356 /** context info */ |
146 | 357 public const static int LAYOUT_CONTEXT_INFO_POPUP= 2; |
129 | 358 |
359 int fContextType= LAYOUT_CONTEXT_SELECTOR; | |
360 Shell[] fShells= new Shell[3]; | |
361 Object[] fPopups= new Object[3]; | |
362 | |
363 protected void add(Object popup, Shell shell, int type, int offset) { | |
364 Assert.isNotNull(popup); | |
365 Assert.isTrue(shell !is null && !shell.isDisposed()); | |
366 checkType(type); | |
367 | |
368 if (fShells[type] !is shell) { | |
369 if (fShells[type] !is null) | |
370 fShells[type].removeListener(DWT.Dispose, this); | |
371 shell.addListener(DWT.Dispose, this); | |
372 fShells[type]= shell; | |
373 } | |
374 | |
375 fPopups[type]= popup; | |
376 if (type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP) | |
377 fContextType= type; | |
378 | |
379 layout(type, offset); | |
380 adjustListeners(type); | |
381 } | |
382 | |
383 protected void checkType(int type) { | |
384 Assert.isTrue(type is LAYOUT_PROPOSAL_SELECTOR || | |
385 type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP); | |
386 } | |
387 | |
388 public void handleEvent(Event event) { | |
389 Widget source= event.widget; | |
390 source.removeListener(DWT.Dispose, this); | |
391 | |
392 int type= getShellType(source); | |
393 checkType(type); | |
394 fShells[type]= null; | |
395 | |
396 switch (type) { | |
397 case LAYOUT_PROPOSAL_SELECTOR: | |
398 if (fContextType is LAYOUT_CONTEXT_SELECTOR && | |
399 Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) { | |
400 // Restore event notification to the tip popup. | |
134 | 401 addContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR); |
129 | 402 } |
403 break; | |
404 | |
405 case LAYOUT_CONTEXT_SELECTOR: | |
406 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) { | |
407 if (fProposalPopupOrientation is PROPOSAL_STACKED) | |
408 layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset()); | |
409 // Restore event notification to the proposal popup. | |
134 | 410 addContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR); |
129 | 411 } |
412 fContextType= LAYOUT_CONTEXT_INFO_POPUP; | |
413 break; | |
414 | |
415 case LAYOUT_CONTEXT_INFO_POPUP: | |
416 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) { | |
417 if (fContextInfoPopupOrientation is CONTEXT_INFO_BELOW) | |
418 layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset()); | |
419 } | |
420 fContextType= LAYOUT_CONTEXT_SELECTOR; | |
421 break; | |
422 } | |
423 } | |
424 | |
425 protected int getShellType(Widget shell) { | |
426 for (int i=0; i<fShells.length; i++) { | |
427 if (fShells[i] is shell) | |
428 return i; | |
429 } | |
430 return -1; | |
431 } | |
432 | |
433 protected void layout(int type, int offset) { | |
434 switch (type) { | |
435 case LAYOUT_PROPOSAL_SELECTOR: | |
436 layoutProposalSelector(offset); | |
437 break; | |
438 case LAYOUT_CONTEXT_SELECTOR: | |
439 layoutContextSelector(offset); | |
440 break; | |
441 case LAYOUT_CONTEXT_INFO_POPUP: | |
442 layoutContextInfoPopup(offset); | |
443 break; | |
444 } | |
445 } | |
446 | |
447 protected void layoutProposalSelector(int offset) { | |
448 if (fContextType is LAYOUT_CONTEXT_INFO_POPUP && | |
449 fContextInfoPopupOrientation is CONTEXT_INFO_BELOW && | |
450 Helper2.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) { | |
451 // Stack proposal selector beneath the tip box. | |
452 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR]; | |
453 Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP]; | |
454 shell.setLocation(getStackedLocation(shell, parent)); | |
455 } else if (fContextType !is LAYOUT_CONTEXT_SELECTOR || | |
456 !Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) { | |
457 // There are no other presentations to be concerned with, | |
458 // so place the proposal selector beneath the cursor line. | |
459 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR]; | |
460 shell.setLocation(getBelowLocation(shell, offset)); | |
461 } else { | |
462 switch (fProposalPopupOrientation) { | |
463 case PROPOSAL_REMOVE: { | |
464 // Remove the tip selector and place the | |
465 // proposal selector beneath the cursor line. | |
466 fShells[LAYOUT_CONTEXT_SELECTOR].dispose(); | |
467 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR]; | |
468 shell.setLocation(getBelowLocation(shell, offset)); | |
469 break; | |
470 } | |
471 case PROPOSAL_OVERLAY: { | |
472 // Overlay the tip selector with the proposal selector. | |
473 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR]; | |
474 shell.setLocation(getBelowLocation(shell, offset)); | |
475 break; | |
476 } | |
477 case PROPOSAL_STACKED: { | |
478 // Stack the proposal selector beneath the tip selector. | |
479 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR]; | |
480 Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR]; | |
481 shell.setLocation(getStackedLocation(shell, parent)); | |
482 break; | |
483 } | |
484 } | |
485 } | |
486 } | |
487 | |
488 protected void layoutContextSelector(int offset) { | |
489 // Always place the context selector beneath the cursor line. | |
490 Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR]; | |
491 shell.setLocation(getBelowLocation(shell, offset)); | |
492 | |
493 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) { | |
494 switch (fProposalPopupOrientation) { | |
495 case PROPOSAL_REMOVE: | |
496 // Remove the proposal selector. | |
497 fShells[LAYOUT_PROPOSAL_SELECTOR].dispose(); | |
498 break; | |
499 | |
500 case PROPOSAL_OVERLAY: | |
501 // The proposal selector has been overlaid by the tip selector. | |
502 break; | |
503 | |
504 case PROPOSAL_STACKED: { | |
505 // Stack the proposal selector beneath the tip selector. | |
506 shell= fShells[LAYOUT_PROPOSAL_SELECTOR]; | |
507 Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR]; | |
508 shell.setLocation(getStackedLocation(shell, parent)); | |
509 break; | |
510 } | |
511 } | |
512 } | |
513 } | |
514 | |
515 protected void layoutContextInfoPopup(int offset) { | |
516 switch (fContextInfoPopupOrientation) { | |
517 case CONTEXT_INFO_ABOVE: { | |
518 // Place the popup above the cursor line. | |
519 Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP]; | |
520 shell.setLocation(getAboveLocation(shell, offset)); | |
521 break; | |
522 } | |
523 case CONTEXT_INFO_BELOW: { | |
524 // Place the popup beneath the cursor line. | |
525 Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP]; | |
526 parent.setLocation(getBelowLocation(parent, offset)); | |
527 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) { | |
528 // Stack the proposal selector beneath the context info popup. | |
529 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR]; | |
530 shell.setLocation(getStackedLocation(shell, parent)); | |
531 } | |
532 break; | |
533 } | |
534 } | |
535 } | |
536 | |
537 protected void shiftHorizontalLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) { | |
538 if (location.x + shellBounds.width > displayBounds.width) | |
539 location.x= displayBounds.width - shellBounds.width; | |
540 | |
541 if (location.x < displayBounds.x) | |
542 location.x= displayBounds.x; | |
543 } | |
544 | |
545 protected void shiftVerticalLocation(Point location, Rectangle shellBounds, Rectangle displayBounds) { | |
546 if (location.y + shellBounds.height > displayBounds.height) | |
547 location.y= displayBounds.height - shellBounds.height; | |
548 | |
549 if (location.y < displayBounds.y) | |
550 location.y= displayBounds.y; | |
551 } | |
552 | |
553 protected Point getAboveLocation(Shell shell, int offset) { | |
554 StyledText text= fViewer.getTextWidget(); | |
555 Point location= text.getLocationAtOffset(offset); | |
556 location= text.toDisplay(location); | |
557 | |
558 Rectangle shellBounds= shell.getBounds(); | |
559 Rectangle displayBounds= shell.getDisplay().getClientArea(); | |
560 | |
561 location.y=location.y - shellBounds.height; | |
562 | |
563 shiftHorizontalLocation(location, shellBounds, displayBounds); | |
564 shiftVerticalLocation(location, shellBounds, displayBounds); | |
565 | |
566 return location; | |
567 } | |
568 | |
569 protected Point getBelowLocation(Shell shell, int offset) { | |
570 StyledText text= fViewer.getTextWidget(); | |
571 Point location= text.getLocationAtOffset(offset); | |
572 if (location.x < 0) location.x= 0; | |
573 if (location.y < 0) location.y= 0; | |
574 location= text.toDisplay(location); | |
575 | |
576 Rectangle shellBounds= shell.getBounds(); | |
577 Rectangle displayBounds= shell.getDisplay().getClientArea(); | |
578 | |
579 location.y= location.y + text.getLineHeight(offset); | |
580 shiftHorizontalLocation(location, shellBounds, displayBounds); | |
581 shiftVerticalLocation(location, shellBounds, displayBounds); | |
582 | |
583 return location; | |
584 } | |
585 | |
586 protected Point getStackedLocation(Shell shell, Shell parent) { | |
587 Point p= parent.getLocation(); | |
588 Point size= parent.getSize(); | |
589 p.x += size.x / 4; | |
590 p.y += size.y; | |
591 | |
592 p= parent.toDisplay(p); | |
593 | |
594 Rectangle shellBounds= shell.getBounds(); | |
595 Rectangle displayBounds= shell.getDisplay().getClientArea(); | |
596 shiftHorizontalLocation(p, shellBounds, displayBounds); | |
597 shiftVerticalLocation(p, shellBounds, displayBounds); | |
598 | |
599 return p; | |
600 } | |
601 | |
602 protected void adjustListeners(int type) { | |
603 switch (type) { | |
604 case LAYOUT_PROPOSAL_SELECTOR: | |
605 if (fContextType is LAYOUT_CONTEXT_SELECTOR && | |
606 Helper2.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) | |
607 // Disable event notification to the tip selector. | |
134 | 608 removeContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR); |
129 | 609 break; |
610 case LAYOUT_CONTEXT_SELECTOR: | |
611 if (Helper2.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) | |
612 // Disable event notification to the proposal selector. | |
134 | 613 removeContentAssistListener(cast(IContentAssistListener2) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR); |
129 | 614 break; |
615 case LAYOUT_CONTEXT_INFO_POPUP: | |
616 break; | |
617 } | |
618 } | |
619 } | |
620 | |
621 /** | |
622 * Internal key listener and event consumer. | |
623 */ | |
624 class InternalListener : VerifyKeyListener, IEventConsumer { | |
625 | |
626 /** | |
627 * Verifies key events by notifying the registered listeners. | |
628 * Each listener is allowed to indicate that the event has been | |
629 * handled and should not be further processed. | |
630 * | |
631 * @param e the verify event | |
632 * @see VerifyKeyListener#verifyKey(dwt.events.VerifyEvent) | |
633 */ | |
634 public void verifyKey(VerifyEvent e) { | |
158 | 635 IContentAssistListener2[] listeners= arraycast!(IContentAssistListener2)( fListeners.clone()); |
129 | 636 for (int i= 0; i < listeners.length; i++) { |
637 if (listeners[i] !is null) { | |
638 if (!listeners[i].verifyKey(e) || !e.doit) | |
639 return; | |
640 } | |
641 } | |
642 } | |
643 | |
644 /* | |
645 * @see IEventConsumer#processEvent | |
646 */ | |
647 public void processEvent(VerifyEvent event) { | |
648 | |
649 installKeyListener(); | |
650 | |
158 | 651 IContentAssistListener2[] listeners= arraycast!(IContentAssistListener2)(fListeners.clone()); |
129 | 652 for (int i= 0; i < listeners.length; i++) { |
653 if (listeners[i] !is null) { | |
654 listeners[i].processEvent(event); | |
655 if (!event.doit) | |
656 return; | |
657 } | |
658 } | |
659 } | |
660 } | |
661 | |
662 | |
663 // Content-Assist Listener types | |
664 final static int CONTEXT_SELECTOR= 0; | |
665 final static int PROPOSAL_SELECTOR= 1; | |
666 final static int CONTEXT_INFO_POPUP= 2; | |
667 | |
668 /** | |
669 * The popup priority: > info pop-ups, < standard content assist. | |
670 * Default value: <code>10</code>. | |
671 * | |
672 * @since 3.0 | |
673 */ | |
147 | 674 public static const int WIDGET_PRIORITY= 10; |
129 | 675 |
676 | |
147 | 677 private static const int DEFAULT_AUTO_ACTIVATION_DELAY= 500; |
129 | 678 |
679 private IInformationControlCreator fInformationControlCreator; | |
680 private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY; | |
681 private bool fIsAutoActivated= false; | |
682 private bool fIsAutoInserting= false; | |
683 private int fProposalPopupOrientation= PROPOSAL_OVERLAY; | |
684 private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE; | |
685 private Map fProcessors; | |
686 private String fPartitioning; | |
687 | |
688 private Color fContextInfoPopupBackground; | |
689 private Color fContextInfoPopupForeground; | |
690 private Color fContextSelectorBackground; | |
691 private Color fContextSelectorForeground; | |
692 | |
693 private ITextViewer fViewer; | |
694 private String fLastErrorMessage; | |
695 | |
696 private Closer fCloser; | |
697 private LayoutManager fLayoutManager; | |
698 private AutoAssistListener fAutoAssistListener; | |
699 private InternalListener fInternalListener; | |
700 private CompletionProposalPopup2 fProposalPopup; | |
701 private ContextInformationPopup2 fContextInfoPopup; | |
702 | |
703 private bool fKeyListenerHooked= false; | |
704 private IContentAssistListener2[] fListeners= new IContentAssistListener2[4]; | |
705 private int fCompletionPosition; | |
706 private String[] fProposalStrings; | |
707 private ICompletionProposal[] fProposals; | |
146 | 708 private const List fProposalListeners= new ArrayList(); |
158 | 709 |
129 | 710 /** |
711 * Tells whether colored label support is enabled. | |
712 * @since 3.4 | |
713 */ | |
714 private bool fIsColoredLabelsSupportEnabled= false; | |
158 | 715 |
129 | 716 |
717 /** | |
718 * Creates a new content assistant. The content assistant is not automatically activated, | |
719 * overlays the completion proposals with context information list if necessary, and | |
720 * shows the context information above the location at which it was activated. If auto | |
721 * activation will be enabled, without further configuration steps, this content assistant | |
722 * is activated after a 500 ms delay. It uses the default partitioning. | |
723 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
724 public this() { |
129 | 725 setContextInformationPopupOrientation(CONTEXT_INFO_ABOVE); |
726 setInformationControlCreator(getInformationControlCreator()); | |
727 | |
728 // JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools(); | |
729 // IColorManager manager= textTools.getColorManager(); | |
730 // | |
731 // IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore(); | |
732 // | |
733 // Color c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND, manager); | |
734 // setProposalSelectorForeground(c); | |
735 // | |
736 // c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND, manager); | |
737 // setProposalSelectorBackground(c); | |
738 } | |
739 | |
740 /** | |
741 * Creates an <code>IInformationControlCreator</code> to be used to display context information. | |
742 * | |
743 * @return an <code>IInformationControlCreator</code> to be used to display context information | |
744 */ | |
745 private IInformationControlCreator getInformationControlCreator() { | |
135 | 746 return new class() IInformationControlCreator { |
129 | 747 public IInformationControl createInformationControl(Shell parent) { |
748 return new DefaultInformationControl(parent, false); | |
749 } | |
750 }; | |
751 } | |
752 | |
753 /** | |
754 * Sets the document partitioning this content assistant is using. | |
755 * | |
756 * @param partitioning the document partitioning for this content assistant | |
757 */ | |
758 public void setDocumentPartitioning(String partitioning) { | |
759 Assert.isNotNull(partitioning); | |
760 fPartitioning= partitioning; | |
761 } | |
762 | |
763 /* | |
764 * @see dwtx.jface.text.contentassist.IContentAssistantExtension#getDocumentPartitioning() | |
765 * @since 3.0 | |
766 */ | |
767 public String getDocumentPartitioning() { | |
768 return fPartitioning; | |
769 } | |
770 | |
771 /** | |
772 * Registers a given content assist processor for a particular content type. | |
773 * If there is already a processor registered for this type, the new processor | |
774 * is registered instead of the old one. | |
775 * | |
776 * @param processor the content assist processor to register, or <code>null</code> to remove an existing one | |
777 * @param contentType the content type under which to register | |
778 */ | |
779 public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) { | |
780 | |
781 Assert.isNotNull(contentType); | |
782 | |
783 if (fProcessors is null) | |
784 fProcessors= new HashMap(); | |
785 | |
786 if (processor is null) | |
787 fProcessors.remove(contentType); | |
788 else | |
789 fProcessors.put(contentType, processor); | |
790 } | |
791 | |
792 /* | |
793 * @see IContentAssistant#getContentAssistProcessor | |
794 */ | |
795 public IContentAssistProcessor getContentAssistProcessor(String contentType) { | |
796 if (fProcessors is null) | |
797 return null; | |
798 | |
134 | 799 return cast(IContentAssistProcessor) fProcessors.get(contentType); |
129 | 800 } |
801 | |
802 /** | |
803 * Enables the content assistant's auto activation mode. | |
804 * | |
805 * @param enabled indicates whether auto activation is enabled or not | |
806 */ | |
807 public void enableAutoActivation(bool enabled) { | |
808 fIsAutoActivated= enabled; | |
809 manageAutoActivation(fIsAutoActivated); | |
810 } | |
811 | |
812 /** | |
813 * Enables the content assistant's auto insertion mode. If enabled, | |
814 * the content assistant inserts a proposal automatically if it is | |
815 * the only proposal. In the case of ambiguities, the user must | |
816 * make the choice. | |
817 * | |
818 * @param enabled indicates whether auto insertion is enabled or not | |
819 * @since 2.0 | |
820 */ | |
821 public void enableAutoInsert(bool enabled) { | |
822 fIsAutoInserting= enabled; | |
823 } | |
824 | |
825 /** | |
826 * Returns whether this content assistant is in the auto insertion | |
827 * mode or not. | |
828 * | |
829 * @return <code>true</code> if in auto insertion mode | |
830 * @since 2.0 | |
831 */ | |
832 bool isAutoInserting() { | |
833 return fIsAutoInserting; | |
834 } | |
835 | |
836 /** | |
837 * Installs and uninstall the listeners needed for auto-activation. | |
838 * @param start <code>true</code> if listeners must be installed, | |
839 * <code>false</code> if they must be removed | |
840 * @since 2.0 | |
841 */ | |
842 private void manageAutoActivation(bool start) { | |
843 if (start) { | |
844 | |
845 if (fViewer !is null && fAutoAssistListener is null) { | |
846 fAutoAssistListener= new AutoAssistListener(); | |
138 | 847 if ( cast(ITextViewerExtension)fViewer ) { |
134 | 848 ITextViewerExtension extension= cast(ITextViewerExtension) fViewer; |
129 | 849 extension.appendVerifyKeyListener(fAutoAssistListener); |
850 } else { | |
851 StyledText textWidget= fViewer.getTextWidget(); | |
852 if (Helper2.okToUse(textWidget)) | |
853 textWidget.addVerifyKeyListener(fAutoAssistListener); | |
854 } | |
855 } | |
856 | |
857 } else if (fAutoAssistListener !is null) { | |
858 | |
138 | 859 if ( cast(ITextViewerExtension)fViewer ) { |
134 | 860 ITextViewerExtension extension= cast(ITextViewerExtension) fViewer; |
129 | 861 extension.removeVerifyKeyListener(fAutoAssistListener); |
862 } else { | |
863 StyledText textWidget= fViewer.getTextWidget(); | |
864 if (Helper2.okToUse(textWidget)) | |
865 textWidget.removeVerifyKeyListener(fAutoAssistListener); | |
866 } | |
867 | |
868 fAutoAssistListener= null; | |
869 } | |
870 } | |
871 | |
872 /** | |
873 * Sets the delay after which the content assistant is automatically invoked | |
874 * if the cursor is behind an auto activation character. | |
875 * | |
876 * @param delay the auto activation delay | |
877 */ | |
878 public void setAutoActivationDelay(int delay) { | |
879 fAutoActivationDelay= delay; | |
880 } | |
881 | |
882 /** | |
883 * Sets the proposal pop-ups' orientation. | |
884 * The following values may be used: | |
885 * <ul> | |
886 * <li>PROPOSAL_OVERLAY<p> | |
887 * proposal popup windows should overlay each other | |
888 * </li> | |
889 * <li>PROPOSAL_REMOVE<p> | |
890 * any currently shown proposal popup should be closed | |
891 * </li> | |
892 * <li>PROPOSAL_STACKED<p> | |
893 * proposal popup windows should be vertical stacked, with no overlap, | |
894 * beneath the line containing the current cursor location | |
895 * </li> | |
896 * </ul> | |
897 * | |
898 * @param orientation the popup's orientation | |
899 */ | |
900 public void setProposalPopupOrientation(int orientation) { | |
901 fProposalPopupOrientation= orientation; | |
902 } | |
903 | |
904 /** | |
905 * Sets the context information popup's orientation. | |
906 * The following values may be used: | |
907 * <ul> | |
908 * <li>CONTEXT_ABOVE<p> | |
909 * context information popup should always appear above the line containing | |
910 * the current cursor location | |
911 * </li> | |
912 * <li>CONTEXT_BELOW<p> | |
913 * context information popup should always appear below the line containing | |
914 * the current cursor location | |
915 * </li> | |
916 * </ul> | |
917 * | |
918 * @param orientation the popup's orientation | |
919 */ | |
920 public void setContextInformationPopupOrientation(int orientation) { | |
921 fContextInfoPopupOrientation= orientation; | |
922 } | |
923 | |
924 /** | |
925 * Sets the context information popup's background color. | |
926 * | |
927 * @param background the background color | |
928 */ | |
929 public void setContextInformationPopupBackground(Color background) { | |
930 fContextInfoPopupBackground= background; | |
931 } | |
932 | |
933 /** | |
934 * Returns the background of the context information popup. | |
935 * | |
936 * @return the background of the context information popup | |
937 * @since 2.0 | |
938 */ | |
939 Color getContextInformationPopupBackground() { | |
940 return fContextInfoPopupBackground; | |
941 } | |
942 | |
943 /** | |
944 * Sets the context information popup's foreground color. | |
945 * | |
946 * @param foreground the foreground color | |
947 * @since 2.0 | |
948 */ | |
949 public void setContextInformationPopupForeground(Color foreground) { | |
950 fContextInfoPopupForeground= foreground; | |
951 } | |
952 | |
953 /** | |
954 * Returns the foreground of the context information popup. | |
955 * | |
956 * @return the foreground of the context information popup | |
957 * @since 2.0 | |
958 */ | |
959 Color getContextInformationPopupForeground() { | |
960 return fContextInfoPopupForeground; | |
961 } | |
962 | |
963 /** | |
964 * Sets the context selector's background color. | |
965 * | |
966 * @param background the background color | |
967 * @since 2.0 | |
968 */ | |
969 public void setContextSelectorBackground(Color background) { | |
970 fContextSelectorBackground= background; | |
971 } | |
972 | |
973 /** | |
974 * Returns the background of the context selector. | |
975 * | |
976 * @return the background of the context selector | |
977 * @since 2.0 | |
978 */ | |
979 Color getContextSelectorBackground() { | |
980 return fContextSelectorBackground; | |
981 } | |
982 | |
983 /** | |
984 * Sets the context selector's foreground color. | |
985 * | |
986 * @param foreground the foreground color | |
987 * @since 2.0 | |
988 */ | |
989 public void setContextSelectorForeground(Color foreground) { | |
990 fContextSelectorForeground= foreground; | |
991 } | |
992 | |
993 /** | |
994 * Returns the foreground of the context selector. | |
995 * | |
996 * @return the foreground of the context selector | |
997 * @since 2.0 | |
998 */ | |
999 Color getContextSelectorForeground() { | |
1000 return fContextSelectorForeground; | |
1001 } | |
1002 | |
1003 /** | |
1004 * Sets the information control creator for the additional information control. | |
1005 * | |
1006 * @param creator the information control creator for the additional information control | |
1007 * @since 2.0 | |
1008 */ | |
1009 public void setInformationControlCreator(IInformationControlCreator creator) { | |
1010 fInformationControlCreator= creator; | |
1011 } | |
1012 | |
1013 /* | |
1014 * @see IContentAssist#install | |
1015 */ | |
1016 public void install(ITextViewer textViewer) { | |
1017 Assert.isNotNull(textViewer); | |
1018 | |
1019 fViewer= textViewer; | |
1020 | |
1021 fLayoutManager= new LayoutManager(); | |
1022 fInternalListener= new InternalListener(); | |
1023 | |
1024 AdditionalInfoController2 controller= null; | |
1025 if (fInformationControlCreator !is null) { | |
1026 int delay= fAutoActivationDelay; | |
1027 if (delay is 0) | |
1028 delay= DEFAULT_AUTO_ACTIVATION_DELAY; | |
1029 delay= Math.round(delay * 1.5f); | |
1030 controller= new AdditionalInfoController2(fInformationControlCreator, delay); | |
1031 } | |
1032 fContextInfoPopup= new ContextInformationPopup2(this, fViewer); | |
1033 fProposalPopup= new CompletionProposalPopup2(this, fViewer, controller); | |
1034 | |
1035 manageAutoActivation(fIsAutoActivated); | |
1036 } | |
1037 | |
1038 /* | |
1039 * @see IContentAssist#uninstall | |
1040 */ | |
1041 public void uninstall() { | |
1042 | |
1043 if (fProposalPopup !is null) | |
1044 fProposalPopup.hide(); | |
1045 | |
1046 if (fContextInfoPopup !is null) | |
1047 fContextInfoPopup.hide(); | |
1048 | |
1049 manageAutoActivation(false); | |
1050 | |
1051 if (fCloser !is null) { | |
1052 fCloser.uninstall(); | |
1053 fCloser= null; | |
1054 } | |
1055 | |
1056 fViewer= null; | |
1057 } | |
1058 | |
1059 /** | |
1060 * Adds the given shell of the specified type to the layout. | |
1061 * Valid types are defined by <code>LayoutManager</code>. | |
1062 * | |
1063 * @param popup a content assist popup | |
1064 * @param shell the shell of the content-assist popup | |
1065 * @param type the type of popup | |
1066 * @param visibleOffset the offset at which to layout the popup relative to the offset of the viewer's visible region | |
1067 * @since 2.0 | |
1068 */ | |
1069 void addToLayout(Object popup, Shell shell, int type, int visibleOffset) { | |
1070 fLayoutManager.add(popup, shell, type, visibleOffset); | |
1071 } | |
1072 | |
1073 /** | |
1074 * Layouts the registered popup of the given type relative to the | |
1075 * given offset. The offset is relative to the offset of the viewer's visible region. | |
1076 * Valid types are defined by <code>LayoutManager</code>. | |
1077 * | |
1078 * @param type the type of popup to layout | |
1079 * @param visibleOffset the offset at which to layout relative to the offset of the viewer's visible region | |
1080 * @since 2.0 | |
1081 */ | |
1082 void layout(int type, int visibleOffset) { | |
1083 fLayoutManager.layout(type, visibleOffset); | |
1084 } | |
1085 | |
1086 /** | |
1087 * Notifies the controller that a popup has lost focus. | |
1088 * | |
1089 * @param e the focus event | |
1090 */ | |
1091 void popupFocusLost(FocusEvent e) { | |
1092 fCloser.focusLost(e); | |
1093 } | |
1094 | |
1095 /** | |
1096 * Returns the offset of the selection relative to the offset of the visible region. | |
1097 * | |
1098 * @return the offset of the selection relative to the offset of the visible region | |
1099 * @since 2.0 | |
1100 */ | |
1101 int getSelectionOffset() { | |
1102 StyledText text= fViewer.getTextWidget(); | |
1103 return text.getSelectionRange().x; | |
1104 } | |
1105 | |
1106 /** | |
1107 * Returns whether the widget token could be acquired. | |
1108 * The following are valid listener types: | |
1109 * <ul> | |
1110 * <li>AUTO_ASSIST | |
1111 * <li>CONTEXT_SELECTOR | |
1112 * <li>PROPOSAL_SELECTOR | |
1113 * <li>CONTEXT_INFO_POPUP | |
1114 * <ul> | |
1115 * @param type the listener type for which to acquire | |
1116 * @return <code>true</code> if the widget token could be acquired | |
1117 * @since 2.0 | |
1118 */ | |
1119 private bool acquireWidgetToken(int type) { | |
1120 switch (type) { | |
1121 case CONTEXT_SELECTOR: | |
1122 case PROPOSAL_SELECTOR: | |
138 | 1123 if ( cast(IWidgetTokenOwner)fViewer ) { |
134 | 1124 IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fViewer; |
129 | 1125 return owner.requestWidgetToken(this); |
138 | 1126 } else if ( cast(IWidgetTokenOwnerExtension)fViewer ) { |
134 | 1127 IWidgetTokenOwnerExtension extension= cast(IWidgetTokenOwnerExtension) fViewer; |
129 | 1128 return extension.requestWidgetToken(this, WIDGET_PRIORITY); |
1129 } | |
1130 } | |
1131 return true; | |
1132 } | |
1133 | |
1134 /** | |
1135 * Registers a content assist listener. | |
1136 * The following are valid listener types: | |
1137 * <ul> | |
1138 * <li>AUTO_ASSIST | |
1139 * <li>CONTEXT_SELECTOR | |
1140 * <li>PROPOSAL_SELECTOR | |
1141 * <li>CONTEXT_INFO_POPUP | |
1142 * <ul> | |
1143 * Returns whether the listener could be added successfully. A listener | |
1144 * can not be added if the widget token could not be acquired. | |
1145 * | |
1146 * @param listener the listener to register | |
1147 * @param type the type of listener | |
1148 * @return <code>true</code> if the listener could be added | |
1149 */ | |
1150 bool addContentAssistListener(IContentAssistListener2 listener, int type) { | |
1151 | |
1152 if (acquireWidgetToken(type)) { | |
1153 | |
1154 fListeners[type]= listener; | |
1155 | |
1156 if (getNumberOfListeners() is 1) { | |
1157 fCloser= new Closer(); | |
1158 fCloser.install(); | |
1159 fViewer.setEventConsumer(fInternalListener); | |
1160 installKeyListener(); | |
1161 } | |
1162 return true; | |
1163 } | |
1164 | |
1165 return false; | |
1166 } | |
1167 | |
1168 /** | |
1169 * Installs a key listener on the text viewer's widget. | |
1170 */ | |
1171 private void installKeyListener() { | |
1172 if (!fKeyListenerHooked) { | |
1173 StyledText text= fViewer.getTextWidget(); | |
1174 if (Helper2.okToUse(text)) { | |
1175 | |
138 | 1176 if ( cast(ITextViewerExtension)fViewer ) { |
134 | 1177 ITextViewerExtension e= cast(ITextViewerExtension) fViewer; |
129 | 1178 e.prependVerifyKeyListener(fInternalListener); |
1179 } else { | |
1180 text.addVerifyKeyListener(fInternalListener); | |
1181 } | |
1182 | |
1183 fKeyListenerHooked= true; | |
1184 } | |
1185 } | |
1186 } | |
1187 | |
1188 /** | |
1189 * Releases the previously acquired widget token if the token | |
1190 * is no longer necessary. | |
1191 * The following are valid listener types: | |
1192 * <ul> | |
1193 * <li>AUTO_ASSIST | |
1194 * <li>CONTEXT_SELECTOR | |
1195 * <li>PROPOSAL_SELECTOR | |
1196 * <li>CONTEXT_INFO_POPUP | |
1197 * <ul> | |
1198 * | |
1199 * @param type the listener type | |
1200 * @since 2.0 | |
1201 */ | |
1202 private void releaseWidgetToken(int type) { | |
1203 if (fListeners[CONTEXT_SELECTOR] is null && fListeners[PROPOSAL_SELECTOR] is null) { | |
138 | 1204 if ( cast(IWidgetTokenOwner)fViewer ) { |
134 | 1205 IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fViewer; |
129 | 1206 owner.releaseWidgetToken(this); |
1207 } | |
1208 } | |
1209 } | |
1210 | |
1211 /** | |
1212 * Unregisters a content assist listener. | |
1213 * | |
1214 * @param listener the listener to unregister | |
1215 * @param type the type of listener | |
1216 * | |
1217 * @see #addContentAssistListener | |
1218 */ | |
1219 void removeContentAssistListener(IContentAssistListener2 listener, int type) { | |
1220 fListeners[type]= null; | |
1221 | |
1222 if (getNumberOfListeners() is 0) { | |
1223 | |
1224 if (fCloser !is null) { | |
1225 fCloser.uninstall(); | |
1226 fCloser= null; | |
1227 } | |
1228 | |
1229 uninstallKeyListener(); | |
1230 fViewer.setEventConsumer(null); | |
1231 } | |
1232 | |
1233 releaseWidgetToken(type); | |
1234 } | |
1235 | |
1236 /** | |
1237 * Uninstall the key listener from the text viewer's widget. | |
1238 */ | |
1239 private void uninstallKeyListener() { | |
1240 if (fKeyListenerHooked) { | |
1241 StyledText text= fViewer.getTextWidget(); | |
1242 if (Helper2.okToUse(text)) { | |
1243 | |
138 | 1244 if ( cast(ITextViewerExtension)fViewer ) { |
134 | 1245 ITextViewerExtension e= cast(ITextViewerExtension) fViewer; |
129 | 1246 e.removeVerifyKeyListener(fInternalListener); |
1247 } else { | |
1248 text.removeVerifyKeyListener(fInternalListener); | |
1249 } | |
1250 | |
1251 fKeyListenerHooked= false; | |
1252 } | |
1253 } | |
1254 } | |
1255 | |
1256 /** | |
1257 * Returns the number of listeners. | |
1258 * | |
1259 * @return the number of listeners | |
1260 * @since 2.0 | |
1261 */ | |
1262 private int getNumberOfListeners() { | |
1263 int count= 0; | |
1264 for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) { | |
1265 if (fListeners[i] !is null) | |
1266 ++ count; | |
1267 } | |
1268 return count; | |
1269 } | |
1270 | |
1271 /* | |
1272 * @see IContentAssist#showPossibleCompletions | |
1273 */ | |
1274 public String showPossibleCompletions() { | |
1275 return fProposalPopup.showProposals(false); | |
1276 } | |
1277 | |
1278 /** | |
1279 * Hides the proposal popup. | |
1280 */ | |
1281 public void hidePossibleCompletions() { | |
1282 if (fProposalPopup !is null) | |
1283 fProposalPopup.hide(); | |
1284 } | |
1285 | |
1286 /** | |
1287 * Hides any open pop-ups. | |
1288 */ | |
1289 protected void hide() { | |
1290 if (fProposalPopup !is null) | |
1291 fProposalPopup.hide(); | |
1292 if (fContextInfoPopup !is null) | |
1293 fContextInfoPopup.hide(); | |
1294 } | |
1295 | |
1296 /** | |
1297 * Callback to signal this content assistant that the presentation of the possible completions has been stopped. | |
1298 * @since 2.1 | |
1299 */ | |
1300 protected void possibleCompletionsClosed() { | |
1301 } | |
1302 | |
1303 /* | |
1304 * @see IContentAssist#showContextInformation | |
1305 */ | |
1306 public String showContextInformation() { | |
1307 return fContextInfoPopup.showContextProposals(false); | |
1308 } | |
1309 | |
1310 | |
1311 /** | |
1312 * Callback to signal this content assistant that the presentation of the context information has been stopped. | |
1313 * @since 2.1 | |
1314 */ | |
1315 protected void contextInformationClosed() { | |
1316 } | |
1317 | |
1318 /** | |
1319 * Requests that the specified context information to be shown. | |
1320 * | |
1321 * @param contextInformation the context information to be shown | |
1322 * @param position the position to which the context information refers to | |
1323 * @since 2.0 | |
1324 */ | |
1325 void showContextInformation(IContextInformation contextInformation, int position) { | |
1326 fContextInfoPopup.showContextInformation(contextInformation, position); | |
1327 } | |
1328 | |
1329 /** | |
1330 * Returns the current content assist error message. | |
1331 * | |
1332 * @return an error message or <code>null</code> if no error has occurred | |
1333 */ | |
1334 String getErrorMessage() { | |
1335 return fLastErrorMessage; | |
1336 } | |
1337 | |
1338 /** | |
1339 * Returns the content assist processor for the content | |
1340 * type of the specified document position. | |
1341 * | |
1342 * @param viewer the text viewer | |
1343 * @param offset a offset within the document | |
1344 * @return a content-assist processor or <code>null</code> if none exists | |
1345 */ | |
1346 private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) { | |
1347 try { | |
1348 String type= TextUtilities.getContentType(viewer.getDocument(), getDocumentPartitioning(), offset, true); | |
1349 return getContentAssistProcessor(type); | |
1350 } catch (BadLocationException x) { | |
1351 } | |
1352 return null; | |
1353 } | |
1354 | |
1355 /** | |
1356 * Returns an array of completion proposals computed based on | |
1357 * the specified document position. The position is used to | |
1358 * determine the appropriate content assist processor to invoke. | |
1359 * | |
1360 * @param viewer the viewer for which to compute the proposals | |
1361 * @param position a document position | |
1362 * @return an array of completion proposals | |
1363 * | |
1364 * @see IContentAssistProcessor#computeCompletionProposals | |
1365 */ | |
1366 ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int position) { | |
1367 if (fProposals !is null) { | |
1368 return fProposals; | |
1369 } else if (fProposalStrings !is null) { | |
1370 ICompletionProposal[] result= new ICompletionProposal[fProposalStrings.length]; | |
1371 for (int i= 0; i < fProposalStrings.length; i++) { | |
1372 result[i]= new CompletionProposal(fProposalStrings[i], position, fProposalStrings[i].length(), fProposalStrings[i].length()); | |
1373 } | |
1374 return result; | |
1375 } else return null; | |
1376 } | |
1377 | |
1378 /** | |
1379 * Returns an array of context information objects computed based | |
1380 * on the specified document position. The position is used to determine | |
1381 * the appropriate content assist processor to invoke. | |
1382 * | |
1383 * @param viewer the viewer for which to compute the context information | |
1384 * @param position a document position | |
1385 * @return an array of context information objects | |
1386 * | |
1387 * @see IContentAssistProcessor#computeContextInformation | |
1388 */ | |
1389 IContextInformation[] computeContextInformation(ITextViewer viewer, int position) { | |
1390 fLastErrorMessage= null; | |
1391 | |
1392 IContextInformation[] result= null; | |
1393 | |
1394 IContentAssistProcessor p= getProcessor(viewer, position); | |
1395 if (p !is null) { | |
1396 result= p.computeContextInformation(viewer, position); | |
1397 fLastErrorMessage= p.getErrorMessage(); | |
1398 } | |
1399 | |
1400 return result; | |
1401 } | |
1402 | |
1403 /** | |
1404 * Returns the context information validator that should be used to | |
1405 * determine when the currently displayed context information should | |
1406 * be dismissed. The position is used to determine the appropriate | |
1407 * content assist processor to invoke. | |
1408 * | |
1409 * @param textViewer the text viewer | |
1410 * @param offset a document offset | |
1411 * @return an validator | |
1412 * | |
1413 * @see IContentAssistProcessor#getContextInformationValidator | |
1414 */ | |
1415 IContextInformationValidator getContextInformationValidator(ITextViewer textViewer, int offset) { | |
1416 IContentAssistProcessor p= getProcessor(textViewer, offset); | |
1417 return p !is null ? p.getContextInformationValidator() : null; | |
1418 } | |
1419 | |
1420 /** | |
1421 * Returns the context information presenter that should be used to | |
1422 * display context information. The position is used to determine the appropriate | |
1423 * content assist processor to invoke. | |
1424 * | |
1425 * @param textViewer the text viewer | |
1426 * @param offset a document offset | |
1427 * @return a presenter | |
1428 * @since 2.0 | |
1429 */ | |
1430 IContextInformationPresenter getContextInformationPresenter(ITextViewer textViewer, int offset) { | |
1431 IContextInformationValidator validator= getContextInformationValidator(textViewer, offset); | |
138 | 1432 if ( cast(IContextInformationPresenter)validator ) |
134 | 1433 return cast(IContextInformationPresenter) validator; |
129 | 1434 return null; |
1435 } | |
1436 | |
1437 /** | |
1438 * Returns the characters which when typed by the user should automatically | |
1439 * initiate proposing completions. The position is used to determine the | |
1440 * appropriate content assist processor to invoke. | |
1441 * | |
1442 * @param textViewer the text viewer | |
1443 * @param offset a document offset | |
1444 * @return the auto activation characters | |
1445 * | |
1446 * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters | |
1447 */ | |
1448 private char[] getCompletionProposalAutoActivationCharacters(ITextViewer textViewer, int offset) { | |
1449 IContentAssistProcessor p= getProcessor(textViewer, offset); | |
1450 return p !is null ? p.getCompletionProposalAutoActivationCharacters() : null; | |
1451 } | |
1452 | |
1453 /** | |
1454 * Returns the characters which when typed by the user should automatically | |
1455 * initiate the presentation of context information. The position is used | |
1456 * to determine the appropriate content assist processor to invoke. | |
1457 * | |
1458 * @param textViewer the text viewer | |
1459 * @param offset a document offset | |
1460 * @return the auto activation characters | |
1461 * | |
1462 * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters | |
1463 */ | |
1464 private char[] getContextInformationAutoActivationCharacters(ITextViewer textViewer, int offset) { | |
1465 IContentAssistProcessor p= getProcessor(textViewer, offset); | |
1466 return p !is null ? p.getContextInformationAutoActivationCharacters() : null; | |
1467 } | |
1468 | |
1469 /* | |
1470 * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner) | |
1471 * @since 2.0 | |
1472 */ | |
1473 public bool requestWidgetToken(IWidgetTokenOwner owner) { | |
1474 hidePossibleCompletions(); | |
1475 return true; | |
1476 } | |
1477 | |
1478 /** | |
1479 * @param completionPosition | |
1480 */ | |
1481 public void setCompletionPosition(int completionPosition) { | |
1482 fCompletionPosition= completionPosition; | |
1483 } | |
1484 | |
1485 /** | |
1486 * @return the completion position | |
1487 */ | |
1488 public int getCompletionPosition() { | |
1489 return fCompletionPosition; | |
1490 } | |
1491 | |
1492 /** | |
1493 * @param proposals | |
1494 */ | |
1495 public void setCompletions(String[] proposals) { | |
1496 fProposalStrings= proposals; | |
1497 } | |
1498 | |
1499 /** | |
1500 * @param proposals | |
1501 */ | |
1502 public void setCompletions(ICompletionProposal[] proposals) { | |
1503 fProposals= proposals; | |
1504 } | |
1505 | |
1506 /* | |
1507 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int) | |
1508 * @since 3.0 | |
1509 */ | |
1510 public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) { | |
1511 if (priority > WIDGET_PRIORITY) { | |
1512 hidePossibleCompletions(); | |
1513 return true; | |
1514 } | |
1515 return false; | |
1516 } | |
1517 | |
1518 /* | |
1519 * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner) | |
1520 * @since 3.0 | |
1521 */ | |
1522 public bool setFocus(IWidgetTokenOwner owner) { | |
1523 if (fProposalPopup !is null) { | |
1524 fProposalPopup.setFocus(); | |
1525 return fProposalPopup.hasFocus(); | |
1526 } | |
1527 return false; | |
1528 } | |
1529 | |
1530 /** | |
1531 * Returns whether any popups controlled by the receiver have the input focus. | |
1532 * | |
1533 * @return <code>true</code> if any of the managed popups have the focus, <code>false</code> otherwise | |
1534 */ | |
1535 public bool hasFocus() { | |
1536 return (fProposalPopup !is null && fProposalPopup.hasFocus()) | |
1537 || (fContextInfoPopup !is null && fContextInfoPopup.hasFocus()); | |
1538 } | |
1539 | |
1540 /* | |
1541 * @see dwtx.jface.text.contentassist.IContentAssistantExtension#completePrefix() | |
1542 */ | |
1543 public String completePrefix() { | |
1544 return null; | |
1545 } | |
1546 | |
1547 /** | |
1548 * @param proposal | |
1549 */ | |
1550 public void fireProposalChosen(ICompletionProposal proposal) { | |
1551 List list= new ArrayList(fProposalListeners); | |
1552 for (Iterator it= list.iterator(); it.hasNext();) { | |
134 | 1553 IProposalListener listener= cast(IProposalListener) it.next(); |
129 | 1554 listener.proposalChosen(proposal); |
1555 } | |
1556 | |
1557 } | |
1558 | |
1559 /** | |
1560 * @param listener | |
1561 */ | |
1562 public void removeProposalListener(IProposalListener listener) { | |
1563 fProposalListeners.remove(listener); | |
1564 } | |
1565 | |
1566 /** | |
1567 * @param listener | |
1568 */ | |
1569 public void addProposalListener(IProposalListener listener) { | |
1570 fProposalListeners.add(listener); | |
1571 } | |
158 | 1572 |
129 | 1573 /** |
1574 * Tells whether the support for colored labels is enabled. | |
158 | 1575 * |
129 | 1576 * @return <code>true</code> if the support for colored labels is enabled, <code>false</code> otherwise |
1577 * @since 3.4 | |
1578 */ | |
1579 bool isColoredLabelsSupportEnabled() { | |
1580 return fIsColoredLabelsSupportEnabled; | |
1581 } | |
1582 | |
1583 /** | |
1584 * Enables the support for colored labels in the proposal popup. | |
1585 * <p>Completion proposals can implement {@link ICompletionProposalExtension6} | |
1586 * to provide colored proposal labels.</p> | |
158 | 1587 * |
129 | 1588 * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup |
1589 * @since 3.4 | |
1590 */ | |
1591 public void enableColoredLabels(bool isEnabled) { | |
1592 fIsColoredLabelsSupportEnabled= isEnabled; | |
1593 } | |
1594 } | |
1595 |