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