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