comparison org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/ContentAssistant.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children 52184e4b815c
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
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 * Guy Gurfinkel, guy.g@zend.com - [content assist][api] provide better access to ContentAssistant - https://bugs.eclipse.org/bugs/show_bug.cgi?id=169954
11 * Anton Leherbauer (Wind River Systems) - [content assist][api] ContentAssistEvent should contain information about auto activation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=193728
12 * Port to the D programming language:
13 * Frank Benoit <benoit@tionex.de>
14 *******************************************************************************/
15 module org.eclipse.jface.text.contentassist.ContentAssistant;
16
17 import org.eclipse.jface.text.contentassist.ContentAssistEvent; // packageimport
18 import org.eclipse.jface.text.contentassist.Helper; // packageimport
19 import org.eclipse.jface.text.contentassist.PopupCloser; // packageimport
20 import org.eclipse.jface.text.contentassist.IContentAssistant; // packageimport
21 import org.eclipse.jface.text.contentassist.CompletionProposal; // packageimport
22 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; // packageimport
23 import org.eclipse.jface.text.contentassist.IContextInformationValidator; // packageimport
24 import org.eclipse.jface.text.contentassist.IContentAssistListener; // packageimport
25 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; // packageimport
26 import org.eclipse.jface.text.contentassist.ICompletionListener; // packageimport
27 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; // packageimport
28 import org.eclipse.jface.text.contentassist.IContentAssistantExtension4; // packageimport
29 import org.eclipse.jface.text.contentassist.ContextInformation; // packageimport
30 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3; // packageimport
31 import org.eclipse.jface.text.contentassist.ContextInformationValidator; // packageimport
32 import org.eclipse.jface.text.contentassist.ICompletionProposal; // packageimport
33 import org.eclipse.jface.text.contentassist.IContentAssistProcessor; // packageimport
34 import org.eclipse.jface.text.contentassist.AdditionalInfoController; // packageimport
35 import org.eclipse.jface.text.contentassist.IContextInformationPresenter; // packageimport
36 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; // packageimport
37 import org.eclipse.jface.text.contentassist.ICompletionListenerExtension; // packageimport
38 import org.eclipse.jface.text.contentassist.ContextInformationPopup; // packageimport
39 import org.eclipse.jface.text.contentassist.IContextInformationExtension; // packageimport
40 import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; // packageimport
41 import org.eclipse.jface.text.contentassist.ContentAssistSubjectControlAdapter; // packageimport
42 import org.eclipse.jface.text.contentassist.CompletionProposalPopup; // packageimport
43 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension; // packageimport
44 import org.eclipse.jface.text.contentassist.IContextInformation; // packageimport
45 import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; // packageimport
46 import org.eclipse.jface.text.contentassist.IContentAssistantExtension; // packageimport
47 import org.eclipse.jface.text.contentassist.JFaceTextMessages; // packageimport
48
49 import java.lang.all;
50 import java.util.Stack;
51 import java.util.Iterator;
52 import java.util.Map;
53 import java.util.HashMap;
54 import java.util.Set;
55 import java.lang.JThread;
56 import tango.core.sync.Mutex;
57 import tango.core.sync.Condition;
58
59 import org.eclipse.swt.SWT;
60 import org.eclipse.swt.SWTError;
61 import org.eclipse.swt.custom.VerifyKeyListener;
62 import org.eclipse.swt.events.ControlEvent;
63 import org.eclipse.swt.events.ControlListener;
64 import org.eclipse.swt.events.DisposeEvent;
65 import org.eclipse.swt.events.DisposeListener;
66 import org.eclipse.swt.events.FocusEvent;
67 import org.eclipse.swt.events.FocusListener;
68 import org.eclipse.swt.events.KeyAdapter;
69 import org.eclipse.swt.events.KeyEvent;
70 import org.eclipse.swt.events.KeyListener;
71 import org.eclipse.swt.events.MouseEvent;
72 import org.eclipse.swt.events.MouseListener;
73 import org.eclipse.swt.events.TraverseEvent;
74 import org.eclipse.swt.events.TraverseListener;
75 import org.eclipse.swt.events.VerifyEvent;
76 import org.eclipse.swt.graphics.Color;
77 import org.eclipse.swt.graphics.Point;
78 import org.eclipse.swt.graphics.Rectangle;
79 import org.eclipse.swt.widgets.Control;
80 import org.eclipse.swt.widgets.Display;
81 import org.eclipse.swt.widgets.Event;
82 import org.eclipse.swt.widgets.Listener;
83 import org.eclipse.swt.widgets.Monitor;
84 import org.eclipse.swt.widgets.Shell;
85 import org.eclipse.swt.widgets.Widget;
86 import org.eclipse.core.commands.IHandler;
87 import org.eclipse.core.runtime.Assert;
88 import org.eclipse.core.runtime.ListenerList;
89 import org.eclipse.jface.bindings.keys.KeySequence;
90 import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
91 import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
92 import org.eclipse.jface.dialogs.IDialogSettings;
93 import org.eclipse.jface.preference.JFacePreferences;
94 import org.eclipse.jface.text.BadLocationException;
95 import org.eclipse.jface.text.IDocument;
96 import org.eclipse.jface.text.IDocumentExtension3;
97 import org.eclipse.jface.text.IEventConsumer;
98 import org.eclipse.jface.text.IInformationControlCreator;
99 import org.eclipse.jface.text.ITextViewer;
100 import org.eclipse.jface.text.IViewportListener;
101 import org.eclipse.jface.text.IWidgetTokenKeeper;
102 import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
103 import org.eclipse.jface.text.IWidgetTokenOwner;
104 import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
105 import org.eclipse.jface.text.TextUtilities;
106 import org.eclipse.jface.util.Geometry;
107
108
109 /**
110 * The standard implementation of the <code>IContentAssistant</code> interface. Usually, clients
111 * instantiate this class and configure it before using it.
112 */
113 public class ContentAssistant : IContentAssistant, IContentAssistantExtension, IContentAssistantExtension2, IContentAssistantExtension3, IContentAssistantExtension4, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
114
115
116
117 /**
118 * Content assist command identifier for 'select next proposal'.
119 *
120 * @since 3.4
121 */
122 public static const String SELECT_NEXT_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectNextProposal"; //$NON-NLS-1$
123 /**
124 * Content assist command identifier for 'select previous proposal'.
125 *
126 * @since 3.4
127 */
128 public static const String SELECT_PREVIOUS_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectPreviousProposal"; //$NON-NLS-1$
129
130
131 /**
132 * A generic closer class used to monitor various interface events in order to determine whether
133 * content-assist should be terminated and all associated windows closed.
134 */
135 class Closer : ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {
136
137 /** The shell that a <code>ControlListener</code> is registered with. */
138 private Shell fShell;
139 /**
140 * The control that a <code>MouseListener</code>, a<code>FocusListener</code> and a
141 * <code>DisposeListener</code> are registered with.
142 */
143 private Control fControl;
144
145 /**
146 * Installs this closer on it's viewer's text widget.
147 */
148 protected void install() {
149 Control control= fContentAssistSubjectControlAdapter.getControl();
150 fControl= control;
151 if (Helper.okToUse(control)) {
152
153 Shell shell= control.getShell();
154 fShell= shell;
155 shell.addControlListener(this);
156
157 control.addMouseListener(this);
158 control.addFocusListener(this);
159
160 /*
161 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
162 * Internal Errors
163 */
164 control.addDisposeListener(this);
165 }
166 if (fViewer !is null)
167 fViewer.addViewportListener(this);
168 }
169
170 /**
171 * Uninstalls this closer from the viewer's text widget.
172 */
173 protected void uninstall() {
174 Control shell= fShell;
175 fShell= null;
176 if (Helper.okToUse(shell))
177 shell.removeControlListener(this);
178
179 Control control= fControl;
180 fControl= null;
181 if (Helper.okToUse(control)) {
182
183 control.removeMouseListener(this);
184 control.removeFocusListener(this);
185
186 /*
187 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
188 * Internal Errors
189 */
190 control.removeDisposeListener(this);
191 }
192
193 if (fViewer !is null)
194 fViewer.removeViewportListener(this);
195 }
196
197 /*
198 * @see ControlListener#controlResized(ControlEvent)
199 */
200 public void controlResized(ControlEvent e) {
201 hide();
202 }
203
204 /*
205 * @see ControlListener#controlMoved(ControlEvent)
206 */
207 public void controlMoved(ControlEvent e) {
208 hide();
209 }
210
211 /*
212 * @see MouseListener#mouseDown(MouseEvent)
213 */
214 public void mouseDown(MouseEvent e) {
215 hide();
216 }
217
218 /*
219 * @see MouseListener#mouseUp(MouseEvent)
220 */
221 public void mouseUp(MouseEvent e) {
222 }
223
224 /*
225 * @see MouseListener#mouseDoubleClick(MouseEvent)
226 */
227 public void mouseDoubleClick(MouseEvent e) {
228 hide();
229 }
230
231 /*
232 * @see FocusListener#focusGained(FocusEvent)
233 */
234 public void focusGained(FocusEvent e) {
235 }
236
237 /*
238 * @see FocusListener#focusLost(FocusEvent)
239 */
240 public void focusLost(FocusEvent e) {
241 Control control= fControl;
242 if (Helper.okToUse(control)) {
243 Display d= control.getDisplay();
244 if (d !is null) {
245 d.asyncExec(new class() Runnable {
246 public void run() {
247 if (!fProposalPopup.hasFocus() && (fContextInfoPopup is null || !fContextInfoPopup.hasFocus()))
248 hide();
249 }
250 });
251 }
252 }
253 }
254
255 /*
256 * @seeDisposeListener#widgetDisposed(DisposeEvent)
257 */
258 public void widgetDisposed(DisposeEvent e) {
259 /*
260 * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal
261 * Errors
262 */
263 hide();
264 }
265
266 /*
267 * @see IViewportListener#viewportChanged(int)
268 */
269 public void viewportChanged(int topIndex) {
270 hide();
271 }
272 }
273
274 /**
275 * An implementation of <code>IContentAssistListener</code>, this class is used to monitor
276 * key events in support of automatic activation of the content assistant. If enabled, the
277 * implementation utilizes a thread to watch for input characters matching the activation
278 * characters specified by the content assist processor, and if detected, will wait the
279 * indicated delay interval before activating the content assistant.
280 *
281 * @since 3.4 protected, was added in 2.1 as private class
282 */
283 protected class AutoAssistListener : KeyAdapter , KeyListener, Runnable, VerifyKeyListener {
284
285 // SWT intf impl
286 public void keyReleased(KeyEvent e) {
287 super.keyReleased(e);
288 }
289
290 private JThread fThread;
291 private bool fIsReset= false;
292 private Mutex fMutex;
293 private Condition fMutex_cond;
294 private int fShowStyle;
295
296 private const static int SHOW_PROPOSALS= 1;
297 private const static int SHOW_CONTEXT_INFO= 2;
298
299 protected this() {
300 fMutex = new Mutex();
301 fMutex_cond = new Condition(fMutex);
302 }
303
304 protected void start(int showStyle) {
305 fShowStyle= showStyle;
306 fThread= new JThread(this);
307 fThread.setName( JFaceTextMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
308 fThread.start();
309 }
310
311 public override void run() {
312 try {
313 while (true) {
314 synchronized (fMutex) {
315 if (fAutoActivationDelay !is 0)
316 fMutex_cond.wait(fAutoActivationDelay);
317 if (fIsReset) {
318 fIsReset= false;
319 continue;
320 }
321 }
322 showAssist(fShowStyle);
323 break;
324 }
325 } catch (InterruptedException e) {
326 }
327 fThread= null;
328 }
329
330 protected void reset(int showStyle) {
331 synchronized (fMutex) {
332 fShowStyle= showStyle;
333 fIsReset= true;
334 fMutex_cond.notifyAll();
335 }
336 }
337
338 protected void stop() {
339 JThread threadToStop= fThread;
340 if (threadToStop !is null && threadToStop.isAlive())
341 threadToStop.interrupt();
342 }
343
344 private bool contains(char[] characters, char character) {
345 if (characters !is null) {
346 for (int i= 0; i < characters.length; i++) {
347 if (character is characters[i])
348 return true;
349 }
350 }
351 return false;
352 }
353
354 public void keyPressed(KeyEvent e) {
355 // Only act on typed characters and ignore modifier-only events
356 if (e.character is 0 && (e.keyCode & SWT.KEYCODE_BIT) is 0)
357 return;
358
359 if (e.character !is 0 && (e.stateMask is SWT.ALT))
360 return;
361
362 // Only act on characters that are trigger candidates. This
363 // avoids computing the model selection on every keystroke
364 if (computeAllAutoActivationTriggers().indexOf(e.character) < 0) {
365 stop();
366 return;
367 }
368
369 int showStyle;
370 int pos= fContentAssistSubjectControlAdapter.getSelectedRange().x;
371 char[] activation;
372
373 activation= fContentAssistSubjectControlAdapter.getCompletionProposalAutoActivationCharacters(this.outer, pos);
374
375 if (contains(activation, e.character) && !isProposalPopupActive())
376 showStyle= SHOW_PROPOSALS;
377 else {
378 activation= fContentAssistSubjectControlAdapter.getContextInformationAutoActivationCharacters(this.outer, pos);
379 if (contains(activation, e.character) && !isContextInfoPopupActive())
380 showStyle= SHOW_CONTEXT_INFO;
381 else {
382 stop();
383 return;
384 }
385 }
386
387 if (fThread !is null && fThread.isAlive())
388 reset(showStyle);
389 else
390 start(showStyle);
391 }
392
393 /*
394 * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
395 */
396 public void verifyKey(VerifyEvent event) {
397 keyPressed(event);
398 }
399
400 protected void showAssist(int showStyle) {
401 final Control control= fContentAssistSubjectControlAdapter.getControl();
402 if (control is null)
403 return;
404
405 final Display d= control.getDisplay();
406 if (d is null)
407 return;
408
409 try {
410 d.syncExec(new class() Runnable {
411 public void run() {
412 if (isProposalPopupActive())
413 return;
414
415 if (control.isDisposed() || !control.isFocusControl())
416 return;
417
418 if (showStyle is SHOW_PROPOSALS) {
419 if (!prepareToShowCompletions(true))
420 return;
421 fProposalPopup.showProposals(true);
422 fLastAutoActivation= System.currentTimeMillis();
423 } else if (showStyle is SHOW_CONTEXT_INFO && fContextInfoPopup !is null) {
424 promoteKeyListener();
425 fContextInfoPopup.showContextProposals(true);
426 }
427 }
428 });
429 } catch (SWTError e) {
430 }
431 }
432 }
433
434 /**
435 * The layout manager layouts the various windows associated with the content assistant based on
436 * the settings of the content assistant.
437 */
438 class LayoutManager : Listener {
439
440 // Presentation types.
441 /** The presentation type for the proposal selection popup. */
442 public const static int LAYOUT_PROPOSAL_SELECTOR= 0;
443 /** The presentation type for the context selection popup. */
444 public const static int LAYOUT_CONTEXT_SELECTOR= 1;
445 /** The presentation type for the context information hover . */
446 public const static int LAYOUT_CONTEXT_INFO_POPUP= 2;
447
448 int fContextType= LAYOUT_CONTEXT_SELECTOR;
449 Shell[] fShells;
450 Object[] fPopups;
451
452 this(){
453 fShells= new Shell[3];
454 fPopups= new Object[3];
455 }
456
457 protected void add(Object popup, Shell shell, int type, int offset) {
458 Assert.isNotNull(popup);
459 Assert.isTrue(shell !is null && !shell.isDisposed());
460 checkType(type);
461
462 if (fShells[type] !is shell) {
463 if (fShells[type] !is null)
464 fShells[type].removeListener(SWT.Dispose, this);
465 shell.addListener(SWT.Dispose, this);
466 fShells[type]= shell;
467 }
468
469 fPopups[type]= popup;
470 if (type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP)
471 fContextType= type;
472
473 layout(type, offset);
474 adjustListeners(type);
475 }
476
477 protected void checkType(int type) {
478 Assert.isTrue(type is LAYOUT_PROPOSAL_SELECTOR ||
479 type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP);
480 }
481
482 public void handleEvent(Event event) {
483 Widget source= event.widget;
484 source.removeListener(SWT.Dispose, this);
485
486 int type= getShellType(source);
487 checkType(type);
488 fShells[type]= null;
489
490 switch (type) {
491 case LAYOUT_PROPOSAL_SELECTOR:
492 if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
493 Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
494 // Restore event notification to the tip popup.
495 addContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
496 }
497 break;
498
499 case LAYOUT_CONTEXT_SELECTOR:
500 if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
501 if (fProposalPopupOrientation is PROPOSAL_STACKED)
502 layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
503 // Restore event notification to the proposal popup.
504 addContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
505 }
506 fContextType= LAYOUT_CONTEXT_INFO_POPUP;
507 break;
508
509 case LAYOUT_CONTEXT_INFO_POPUP:
510 if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
511 if (fContextInfoPopupOrientation is CONTEXT_INFO_BELOW)
512 layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
513 }
514 fContextType= LAYOUT_CONTEXT_SELECTOR;
515 break;
516 default:
517 }
518 }
519
520 protected int getShellType(Widget shell) {
521 for (int i= 0; i < fShells.length; i++) {
522 if (fShells[i] is shell)
523 return i;
524 }
525 return -1;
526 }
527
528 /**
529 * Layouts the popup defined by <code>type</code> at the given widget offset.
530 *
531 * @param type the kind of popup to layout
532 * @param offset the widget offset
533 */
534 protected void layout(int type, int offset) {
535 switch (type) {
536 case LAYOUT_PROPOSAL_SELECTOR:
537 layoutProposalSelector(offset);
538 break;
539 case LAYOUT_CONTEXT_SELECTOR:
540 layoutContextSelector(offset);
541 break;
542 case LAYOUT_CONTEXT_INFO_POPUP:
543 layoutContextInfoPopup(offset);
544 break;
545 default:
546 }
547 }
548
549 protected void layoutProposalSelector(int offset) {
550 if (fContextType is LAYOUT_CONTEXT_INFO_POPUP &&
551 fContextInfoPopupOrientation is CONTEXT_INFO_BELOW &&
552 Helper.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
553 // Stack proposal selector beneath the tip box.
554 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
555 Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
556 shell.setLocation(getStackedLocation(shell, parent));
557 } else if (fContextType !is LAYOUT_CONTEXT_SELECTOR ||
558 !Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
559 // There are no other presentations to be concerned with,
560 // so place the proposal selector beneath the cursor line.
561 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
562 CompletionProposalPopup popup= cast(CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR];
563 shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
564 } else {
565 CompletionProposalPopup popup= (cast(CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR]);
566 switch (fProposalPopupOrientation) {
567 case PROPOSAL_REMOVE: {
568 // Remove the tip selector and place the
569 // proposal selector beneath the cursor line.
570 fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
571 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
572 shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
573 break;
574 }
575 case PROPOSAL_OVERLAY: {
576 // Overlay the tip selector with the proposal selector.
577 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
578 shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
579 break;
580 }
581 case PROPOSAL_STACKED: {
582 // Stack the proposal selector beneath the tip selector.
583 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
584 Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
585 shell.setLocation(getStackedLocation(shell, parent));
586 break;
587 }
588 default:
589 }
590 }
591 }
592
593 protected void layoutContextSelector(int offset) {
594 // Always place the context selector beneath the cursor line.
595 Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
596 shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, null));
597
598 if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
599 switch (fProposalPopupOrientation) {
600 case PROPOSAL_REMOVE:
601 // Remove the proposal selector.
602 fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
603 break;
604
605 case PROPOSAL_OVERLAY:
606 // The proposal selector has been overlaid by the tip selector.
607 break;
608
609 case PROPOSAL_STACKED: {
610 // Stack the proposal selector beneath the tip selector.
611 shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
612 Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
613 shell.setLocation(getStackedLocation(shell, parent));
614 break;
615 }
616 default:
617 }
618 }
619 }
620
621 protected void layoutContextInfoPopup(int offset) {
622 switch (fContextInfoPopupOrientation) {
623 case CONTEXT_INFO_ABOVE: {
624 // Place the popup above the cursor line.
625 Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
626 shell.setBounds(computeBoundsAboveBelow(shell, shell.getSize(), offset));
627 break;
628 }
629 case CONTEXT_INFO_BELOW: {
630 // Place the popup beneath the cursor line.
631 Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
632 parent.setBounds(computeBoundsBelowAbove(parent, parent.getSize(), offset, null));
633 if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
634 // Stack the proposal selector beneath the context info popup.
635 Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
636 shell.setLocation(getStackedLocation(shell, parent));
637 }
638 break;
639 }
640 default:
641 }
642 }
643
644 /**
645 * Moves <code>point</code> such that <code>rectangle</code> does not bleed outside of
646 * <code>bounds</code>. All coordinates must have the same reference.
647 *
648 * @param point the point to move if needed
649 * @param shellSize the size of the shell that may be moved
650 * @param bounds the bounds
651 * @since 3.3
652 */
653 protected void constrainLocation(Point point, Point shellSize, Rectangle bounds) {
654 if (point.x + shellSize.x > bounds.x + bounds.width)
655 point.x= bounds.x + bounds.width - shellSize.x;
656
657 if (point.x < bounds.x)
658 point.x= bounds.x;
659
660 if (point.y + shellSize.y > bounds.y + bounds.height)
661 point.y= bounds.y + bounds.height - shellSize.y;
662
663 if (point.y < bounds.y)
664 point.y= bounds.y;
665 }
666
667 protected Rectangle constrainHorizontally(Rectangle rect, Rectangle bounds) {
668 // clip width
669 if (rect.width > bounds.width)
670 rect.width= bounds.width;
671
672 if (rect.x + rect.width > bounds.x + bounds.width)
673 rect.x= bounds.x + bounds.width - rect.width;
674 if (rect.x < bounds.x)
675 rect.x= bounds.x;
676
677 return rect;
678 }
679
680 /**
681 * Returns the display bounds for <code>shell</code> such that it appears right above
682 * <code>offset</code>, or below it if above is not suitable. The returned bounds lie
683 * within the monitor at the caret location and never overlap with the caret line.
684 *
685 * @param shell the shell to compute the placement for
686 * @param preferred the preferred size for <code>shell</code>
687 * @param offset the caret offset in the subject control
688 * @return the point right above <code>offset</code> in display coordinates
689 * @since 3.3
690 */
691 protected Rectangle computeBoundsAboveBelow(Shell shell, Point preferred, int offset) {
692 Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
693 Display display= subjectControl.getDisplay();
694 Rectangle caret= getCaretRectangle(offset);
695 org.eclipse.swt.widgets.Monitor.Monitor monitor= getClosestMonitor(display, caret);
696 Rectangle bounds= monitor.getClientArea();
697 Geometry.moveInside(caret, bounds);
698
699 int spaceAbove= caret.y - bounds.y;
700 int caretLowerY= caret.y + caret.height;
701 int spaceBelow= bounds.y + bounds.height - caretLowerY;
702 Rectangle rect;
703 if (spaceAbove >= preferred.y)
704 rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
705 else if (spaceBelow >= preferred.y)
706 rect= new Rectangle(caret.x, caretLowerY, preferred.x, preferred.y);
707 // we can't fit in the preferred size - squeeze into larger area
708 else if (spaceBelow <= spaceAbove)
709 rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
710 else
711 rect= new Rectangle(caret.x, caretLowerY, preferred.x, spaceBelow);
712
713 return constrainHorizontally(rect, bounds);
714 }
715 package Rectangle computeBoundsAboveBelow_package(Shell shell, Point preferred, int offset) {
716 return computeBoundsAboveBelow(shell, preferred, offset);
717 }
718
719 /**
720 * Returns the display bounds for <code>shell</code> such that it appears right below
721 * <code>offset</code>, or above it if below is not suitable. The returned bounds lie
722 * within the monitor at the caret location and never overlap with the caret line.
723 *
724 * @param shell the shell to compute the placement for
725 * @param preferred the preferred size for <code>shell</code>
726 * @param offset the caret offset in the subject control
727 * @param popup a popup to inform if the location was switched to above, <code>null</code> to do nothing
728 * @return the point right below <code>offset</code> in display coordinates
729 * @since 3.3
730 */
731 protected Rectangle computeBoundsBelowAbove(Shell shell, Point preferred, int offset, CompletionProposalPopup popup) {
732 Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
733 Display display= subjectControl.getDisplay();
734 Rectangle caret= getCaretRectangle(offset);
735 org.eclipse.swt.widgets.Monitor.Monitor monitor= getClosestMonitor(display, caret);
736 Rectangle bounds= monitor.getClientArea();
737 Geometry.moveInside(caret, bounds);
738
739 int threshold= popup is null ? Integer.MAX_VALUE : popup.getMinimalHeight();
740 int spaceAbove= caret.y - bounds.y;
741 int spaceBelow= bounds.y + bounds.height - (caret.y + caret.height);
742 Rectangle rect;
743 bool switched= false;
744 if (spaceBelow >= preferred.y)
745 rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, preferred.y);
746 // squeeze in below if we have at least threshold space
747 else if (spaceBelow >= threshold)
748 rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
749 else if (spaceAbove >= preferred.y) {
750 rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
751 switched= true;
752 } else if (spaceBelow >= spaceAbove) {
753 // we can't fit in the preferred size - squeeze into larger area
754 rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
755 } else {
756 rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
757 switched= true;
758 }
759
760 if (popup !is null)
761 popup.switchedPositionToAbove(switched);
762
763 return constrainHorizontally(rect, bounds);
764 }
765 package Rectangle computeBoundsBelowAbove_package(Shell shell, Point preferred, int offset, CompletionProposalPopup popup) {
766 return computeBoundsBelowAbove(shell, preferred, offset, popup );
767 }
768
769 private Rectangle getCaretRectangle(int offset) {
770 Point location= fContentAssistSubjectControlAdapter.getLocationAtOffset(offset);
771 Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
772 Point controlSize= subjectControl.getSize();
773 constrainLocation(location, new Point(0, 0), new Rectangle(0, 0, controlSize.x, controlSize.y));
774 location= subjectControl.toDisplay(location);
775 Rectangle subjectRectangle= new Rectangle(location.x, location.y, 1, fContentAssistSubjectControlAdapter.getLineHeight());
776 return subjectRectangle;
777 }
778
779 protected Point getStackedLocation(Shell shell, Shell parent) {
780 Point p= parent.getLocation();
781 Point size= parent.getSize();
782 p.x += size.x / 4;
783 p.y += size.y;
784
785 p= parent.toDisplay(p);
786
787 Point shellSize= shell.getSize();
788 org.eclipse.swt.widgets.Monitor.Monitor monitor= getClosestMonitor(parent.getDisplay(), new Rectangle(p.x, p.y, 0, 0));
789 Rectangle displayBounds= monitor.getClientArea();
790 constrainLocation(p, shellSize, displayBounds);
791
792 return p;
793 }
794
795 protected void adjustListeners(int type) {
796 switch (type) {
797 case LAYOUT_PROPOSAL_SELECTOR:
798 if (fContextType is LAYOUT_CONTEXT_SELECTOR &&
799 Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
800 // Disable event notification to the tip selector.
801 removeContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
802 break;
803 case LAYOUT_CONTEXT_SELECTOR:
804 if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
805 // Disable event notification to the proposal selector.
806 removeContentAssistListener(cast(IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
807 break;
808 case LAYOUT_CONTEXT_INFO_POPUP:
809 break;
810 default:
811 }
812 }
813
814 /**
815 * Copied from org.eclipse.jface.window.Window.
816 * Returns the monitor whose client area contains the given point. If no
817 * monitor contains the point, returns the monitor that is closest to the
818 * point. If this is ever made public, it should be moved into a separate
819 * utility class.
820 *
821 * @param toSearch
822 * point to find (display coordinates)
823 * @param rectangle
824 * rectangle to find (display coordinates)
825 * @return the monitor closest to the given point
826 * @since 3.3
827 */
828 private org.eclipse.swt.widgets.Monitor.Monitor getClosestMonitor(Display toSearch, Rectangle rectangle) {
829 int closest = Integer.MAX_VALUE;
830
831 Point toFind= Geometry.centerPoint(rectangle);
832 org.eclipse.swt.widgets.Monitor.Monitor[] monitors = toSearch.getMonitors();
833 org.eclipse.swt.widgets.Monitor.Monitor result = monitors[0];
834
835 for (int idx = 0; idx < monitors.length; idx++) {
836 org.eclipse.swt.widgets.Monitor.Monitor current = monitors[idx];
837
838 Rectangle clientArea = current.getClientArea();
839
840 if (clientArea.contains(toFind)) {
841 return current;
842 }
843
844 int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
845 if (distance < closest) {
846 closest = distance;
847 result = current;
848 }
849 }
850
851 return result;
852 }
853 }
854
855 /**
856 * Internal key listener and event consumer.
857 */
858 class InternalListener : VerifyKeyListener, IEventConsumer {
859
860 /**
861 * Verifies key events by notifying the registered listeners. Each listener is allowed to
862 * indicate that the event has been handled and should not be further processed.
863 *
864 * @param e the verify event
865 * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
866 */
867 public void verifyKey(VerifyEvent e) {
868 IContentAssistListener[] listeners= arraycast!(IContentAssistListener)( fListeners.dup );
869 for (int i= 0; i < listeners.length; i++) {
870 if (listeners[i] !is null) {
871 if (!listeners[i].verifyKey(e) || !e.doit)
872 break;
873 }
874 }
875 if (fAutoAssistListener !is null)
876 fAutoAssistListener.keyPressed(e);
877 }
878
879 /*
880 * @see IEventConsumer#processEvent
881 */
882 public void processEvent(VerifyEvent event) {
883
884 installKeyListener();
885
886 IContentAssistListener[] listeners= arraycast!(IContentAssistListener)( fListeners.dup );
887 for (int i= 0; i < listeners.length; i++) {
888 if (listeners[i] !is null) {
889 listeners[i].processEvent(event);
890 if (!event.doit)
891 return;
892 }
893 }
894 }
895 }
896
897 /**
898 * Dialog store constants.
899 *
900 * @since 3.0
901 */
902 public static const String STORE_SIZE_X= "size.x"; //$NON-NLS-1$
903 public static const String STORE_SIZE_Y= "size.y"; //$NON-NLS-1$
904
905 // Content-Assist Listener types
906 const static int CONTEXT_SELECTOR= 0;
907 const static int PROPOSAL_SELECTOR= 1;
908 const static int CONTEXT_INFO_POPUP= 2;
909
910 /**
911 * The popup priority: &gt; linked position proposals and hover pop-ups. Default value:
912 * <code>20</code>;
913 *
914 * @since 3.0
915 */
916 public static const int WIDGET_PRIORITY= 20;
917
918 private static const int DEFAULT_AUTO_ACTIVATION_DELAY= 500;
919
920 private IInformationControlCreator fInformationControlCreator;
921 private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY;
922 private bool fIsAutoActivated= false;
923 private bool fIsAutoInserting= false;
924 private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
925 private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
926 private Map fProcessors;
927
928 /**
929 * The partitioning.
930 *
931 * @since 3.0
932 */
933 private String fPartitioning;
934
935 private Color fContextInfoPopupBackground;
936 private Color fContextInfoPopupForeground;
937 private Color fContextSelectorBackground;
938 private Color fContextSelectorForeground;
939 private Color fProposalSelectorBackground;
940 private Color fProposalSelectorForeground;
941
942 private ITextViewer fViewer;
943 private String fLastErrorMessage;
944
945 private Closer fCloser;
946 LayoutManager fLayoutManager;
947 private AutoAssistListener fAutoAssistListener;
948 private InternalListener fInternalListener;
949 private CompletionProposalPopup fProposalPopup;
950 private ContextInformationPopup fContextInfoPopup;
951
952 /**
953 * Flag which tells whether a verify key listener is hooked.
954 *
955 * @since 3.0
956 */
957 private bool fVerifyKeyListenerHooked= false;
958 private IContentAssistListener[] fListeners;
959 /**
960 * The content assist subject control.
961 *
962 * @since 3.0
963 */
964 private IContentAssistSubjectControl fContentAssistSubjectControl;
965 /**
966 * The content assist subject control's shell.
967 *
968 * @since 3.2
969 */
970 private Shell fContentAssistSubjectControlShell;
971 /**
972 * The content assist subject control's shell traverse listener.
973 *
974 * @since 3.2
975 */
976 private TraverseListener fCASCSTraverseListener;
977 /**
978 * The content assist subject control adapter.
979 *
980 * @since 3.0
981 */
982 private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
983 /**
984 * The dialog settings for the control's bounds.
985 *
986 * @since 3.0
987 */
988 private IDialogSettings fDialogSettings;
989 /**
990 * Prefix completion setting.
991 *
992 * @since 3.0
993 */
994 private bool fIsPrefixCompletionEnabled= false;
995 /**
996 * The list of completion listeners.
997 *
998 * @since 3.2
999 */
1000 private ListenerList fCompletionListeners;
1001 /**
1002 * The message to display at the bottom of the proposal popup.
1003 *
1004 * @since 3.2
1005 */
1006 private String fMessage= ""; //$NON-NLS-1$
1007 /**
1008 * The cycling mode property.
1009 *
1010 * @since 3.2
1011 */
1012 private bool fIsRepetitionMode= false;
1013 /**
1014 * The show empty property.
1015 *
1016 * @since 3.2
1017 */
1018 private bool fShowEmptyList= false;
1019 /**
1020 * The message line property.
1021 *
1022 * @since 3.2
1023 */
1024 private bool fIsStatusLineVisible;
1025 /**
1026 * The last system time when auto activation performed.
1027 *
1028 * @since 3.2
1029 */
1030 private long fLastAutoActivation= Long.MIN_VALUE;
1031 /**
1032 * The iteration key sequence to listen for, or <code>null</code>.
1033 *
1034 * @since 3.2
1035 */
1036 private KeySequence fRepeatedInvocationKeySequence;
1037
1038 /**
1039 * Maps handler to command identifiers.
1040 *
1041 * @since 3.4
1042 */
1043 private Map fHandlers;
1044
1045 /**
1046 * Tells whether colored labels support is enabled.
1047 *
1048 * @since 3.4
1049 */
1050 private bool fIsColoredLabelsSupportEnabled= false;
1051
1052
1053 /**
1054 * Creates a new content assistant. The content assistant is not automatically activated,
1055 * overlays the completion proposals with context information list if necessary, and shows the
1056 * context information above the location at which it was activated. If auto activation will be
1057 * enabled, without further configuration steps, this content assistant is activated after a 500
1058 * milliseconds delay. It uses the default partitioning.
1059 */
1060 public this() {
1061 fListeners= new IContentAssistListener[4];
1062 fCompletionListeners= new ListenerList(ListenerList.IDENTITY);
1063 fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
1064 }
1065
1066 /**
1067 * Sets the document partitioning this content assistant is using.
1068 *
1069 * @param partitioning the document partitioning for this content assistant
1070 * @since 3.0
1071 */
1072 public void setDocumentPartitioning(String partitioning) {
1073 Assert.isNotNull(partitioning);
1074 fPartitioning= partitioning;
1075 }
1076
1077 /*
1078 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#getDocumentPartitioning()
1079 * @since 3.0
1080 */
1081 public String getDocumentPartitioning() {
1082 return fPartitioning;
1083 }
1084
1085 /**
1086 * Registers a given content assist processor for a particular content type. If there is already
1087 * a processor registered for this type, the new processor is registered instead of the old one.
1088 *
1089 * @param processor the content assist processor to register, or <code>null</code> to remove
1090 * an existing one
1091 * @param contentType the content type under which to register
1092 */
1093 public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) {
1094
1095 Assert.isNotNull(contentType);
1096
1097 if (fProcessors is null)
1098 fProcessors= new HashMap();
1099
1100 if (processor is null)
1101 fProcessors.remove(contentType);
1102 else
1103 fProcessors.put(contentType, cast(Object)processor);
1104 }
1105
1106 /*
1107 * @see IContentAssistant#getContentAssistProcessor
1108 */
1109 public IContentAssistProcessor getContentAssistProcessor(String contentType) {
1110 if (fProcessors is null)
1111 return null;
1112
1113 return cast(IContentAssistProcessor) fProcessors.get(contentType);
1114 }
1115
1116 /**
1117 * Computes the sorted set of all auto activation trigger characters.
1118 *
1119 * @return the sorted set of all auto activation trigger characters
1120 * @since 3.1
1121 */
1122 private String computeAllAutoActivationTriggers() {
1123 if (fProcessors is null)
1124 return ""; //$NON-NLS-1$
1125
1126 StringBuffer buf= new StringBuffer(5);
1127 Iterator iter= fProcessors.entrySet().iterator();
1128 while (iter.hasNext()) {
1129 Map.Entry entry= cast(Map.Entry) iter.next();
1130 IContentAssistProcessor processor= cast(IContentAssistProcessor) entry.getValue();
1131 char[] triggers= processor.getCompletionProposalAutoActivationCharacters();
1132 if (triggers !is null)
1133 buf.append(triggers);
1134 triggers= processor.getContextInformationAutoActivationCharacters();
1135 if (triggers !is null)
1136 buf.append(triggers);
1137 }
1138 return buf.toString();
1139 }
1140
1141 /**
1142 * Enables the content assistant's auto activation mode.
1143 *
1144 * @param enabled indicates whether auto activation is enabled or not
1145 */
1146 public void enableAutoActivation(bool enabled) {
1147 fIsAutoActivated= enabled;
1148 manageAutoActivation(fIsAutoActivated);
1149 }
1150
1151 /**
1152 * Enables the content assistant's auto insertion mode. If enabled, the content assistant
1153 * inserts a proposal automatically if it is the only proposal. In the case of ambiguities, the
1154 * user must make the choice.
1155 *
1156 * @param enabled indicates whether auto insertion is enabled or not
1157 * @since 2.0
1158 */
1159 public void enableAutoInsert(bool enabled) {
1160 fIsAutoInserting= enabled;
1161 }
1162
1163 /**
1164 * Returns whether this content assistant is in the auto insertion mode or not.
1165 *
1166 * @return <code>true</code> if in auto insertion mode
1167 * @since 2.0
1168 */
1169 bool isAutoInserting() {
1170 return fIsAutoInserting;
1171 }
1172
1173 /**
1174 * Installs and uninstall the listeners needed for auto activation.
1175 *
1176 * @param start <code>true</code> if listeners must be installed, <code>false</code> if they
1177 * must be removed
1178 * @since 2.0
1179 */
1180 private void manageAutoActivation(bool start) {
1181 if (start) {
1182
1183 if ((fContentAssistSubjectControlAdapter !is null) && fAutoAssistListener is null) {
1184 fAutoAssistListener= createAutoAssistListener();
1185 // For details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
1186 if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
1187 fContentAssistSubjectControlAdapter.appendVerifyKeyListener(fAutoAssistListener);
1188 else
1189 fContentAssistSubjectControlAdapter.addKeyListener(fAutoAssistListener);
1190 }
1191
1192 } else if (fAutoAssistListener !is null) {
1193 // For details see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
1194 if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
1195 fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fAutoAssistListener);
1196 else
1197 fContentAssistSubjectControlAdapter.removeKeyListener(fAutoAssistListener);
1198 fAutoAssistListener= null;
1199 }
1200 }
1201
1202 /**
1203 * This method allows subclasses to provide their own {@link AutoAssistListener}.
1204 *
1205 * @return a new auto assist listener
1206 * @since 3.4
1207 */
1208 protected AutoAssistListener createAutoAssistListener() {
1209 return new AutoAssistListener();
1210 }
1211
1212 /**
1213 * Sets the delay after which the content assistant is automatically invoked if the cursor is
1214 * behind an auto activation character.
1215 *
1216 * @param delay the auto activation delay
1217 */
1218 public void setAutoActivationDelay(int delay) {
1219 fAutoActivationDelay= delay;
1220 }
1221
1222 /**
1223 * Gets the delay after which the content assistant is automatically invoked if the cursor is
1224 * behind an auto activation character.
1225 *
1226 * @return the auto activation delay
1227 * @since 3.4
1228 */
1229 public int getAutoActivationDelay() {
1230 return fAutoActivationDelay;
1231 }
1232
1233 /**
1234 * Sets the proposal pop-ups' orientation. The following values may be used:
1235 * <ul>
1236 * <li>PROPOSAL_OVERLAY<p>
1237 * proposal popup windows should overlay each other
1238 * </li>
1239 * <li>PROPOSAL_REMOVE<p>
1240 * any currently shown proposal popup should be closed
1241 * </li>
1242 * <li>PROPOSAL_STACKED<p>
1243 * proposal popup windows should be vertical stacked, with no overlap,
1244 * beneath the line containing the current cursor location
1245 * </li>
1246 * </ul>
1247 *
1248 * @param orientation the popup's orientation
1249 */
1250 public void setProposalPopupOrientation(int orientation) {
1251 fProposalPopupOrientation= orientation;
1252 }
1253
1254 /**
1255 * Sets the context information popup's orientation.
1256 * The following values may be used:
1257 * <ul>
1258 * <li>CONTEXT_ABOVE<p>
1259 * context information popup should always appear above the line containing
1260 * the current cursor location
1261 * </li>
1262 * <li>CONTEXT_BELOW<p>
1263 * context information popup should always appear below the line containing
1264 * the current cursor location
1265 * </li>
1266 * </ul>
1267 *
1268 * @param orientation the popup's orientation
1269 */
1270 public void setContextInformationPopupOrientation(int orientation) {
1271 fContextInfoPopupOrientation= orientation;
1272 }
1273
1274 /**
1275 * Sets the context information popup's background color.
1276 *
1277 * @param background the background color
1278 */
1279 public void setContextInformationPopupBackground(Color background) {
1280 fContextInfoPopupBackground= background;
1281 }
1282
1283 /**
1284 * Returns the background of the context information popup.
1285 *
1286 * @return the background of the context information popup
1287 * @since 2.0
1288 */
1289 Color getContextInformationPopupBackground() {
1290 return fContextInfoPopupBackground;
1291 }
1292
1293 /**
1294 * Sets the context information popup's foreground color.
1295 *
1296 * @param foreground the foreground color
1297 * @since 2.0
1298 */
1299 public void setContextInformationPopupForeground(Color foreground) {
1300 fContextInfoPopupForeground= foreground;
1301 }
1302
1303 /**
1304 * Returns the foreground of the context information popup.
1305 *
1306 *
1307 * @return the foreground of the context information popup
1308 * @since 2.0
1309 */
1310 Color getContextInformationPopupForeground() {
1311 return fContextInfoPopupForeground;
1312 }
1313
1314 /**
1315 * Sets the proposal selector's background color.
1316 * <p>
1317 * <strong>Note:</strong> As of 3.4, you should only call this
1318 * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_BACKGROUND_COLOR}.
1319 * </p>
1320 *
1321 * @param background the background color
1322 * @since 2.0
1323 */
1324 public void setProposalSelectorBackground(Color background) {
1325 fProposalSelectorBackground= background;
1326 }
1327
1328 /**
1329 * Returns the custom background color of the proposal selector.
1330 *
1331 * @return the background of the proposal selector or <code>null</code> if not set
1332 * @since 2.0
1333 */
1334 Color getProposalSelectorBackground() {
1335 return fProposalSelectorBackground;
1336 }
1337
1338 /**
1339 * Sets the proposal's foreground color.
1340 * <p>
1341 * <strong>Note:</strong> As of 3.4, you should only call this
1342 * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_FOREGROUND_COLOR}.
1343 * </p>
1344 *
1345 * @param foreground the foreground color
1346 * @since 2.0
1347 */
1348 public void setProposalSelectorForeground(Color foreground) {
1349 fProposalSelectorForeground= foreground;
1350 }
1351
1352 /**
1353 * Returns the custom foreground color of the proposal selector.
1354 *
1355 * @return the foreground of the proposal selector or <code>null</code> if not set
1356 * @since 2.0
1357 */
1358 Color getProposalSelectorForeground() {
1359 return fProposalSelectorForeground;
1360 }
1361
1362 /**
1363 * Sets the context selector's background color.
1364 *
1365 * @param background the background color
1366 * @since 2.0
1367 */
1368 public void setContextSelectorBackground(Color background) {
1369 fContextSelectorBackground= background;
1370 }
1371
1372 /**
1373 * Returns the background of the context selector.
1374 *
1375 * @return the background of the context selector
1376 * @since 2.0
1377 */
1378 Color getContextSelectorBackground() {
1379 return fContextSelectorBackground;
1380 }
1381
1382 /**
1383 * Sets the context selector's foreground color.
1384 *
1385 * @param foreground the foreground color
1386 * @since 2.0
1387 */
1388 public void setContextSelectorForeground(Color foreground) {
1389 fContextSelectorForeground= foreground;
1390 }
1391
1392 /**
1393 * Returns the foreground of the context selector.
1394 *
1395 * @return the foreground of the context selector
1396 * @since 2.0
1397 */
1398 Color getContextSelectorForeground() {
1399 return fContextSelectorForeground;
1400 }
1401
1402 /**
1403 * Sets the information control creator for the additional information control.
1404 *
1405 * @param creator the information control creator for the additional information control
1406 * @since 2.0
1407 */
1408 public void setInformationControlCreator(IInformationControlCreator creator) {
1409 fInformationControlCreator= creator;
1410 }
1411
1412 /*
1413 * @see IControlContentAssistant#install(IContentAssistSubjectControl)
1414 * @since 3.0
1415 */
1416 protected void install(IContentAssistSubjectControl contentAssistSubjectControl) {
1417 fContentAssistSubjectControl= contentAssistSubjectControl;
1418 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
1419 install();
1420 }
1421
1422 /*
1423 * @see IContentAssist#install
1424 * @since 3.0
1425 */
1426 public void install(ITextViewer textViewer) {
1427 fViewer= textViewer;
1428 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
1429 install();
1430 }
1431
1432 protected void install() {
1433
1434 fLayoutManager= new LayoutManager();
1435 fInternalListener= new InternalListener();
1436
1437 AdditionalInfoController controller= null;
1438 if (fInformationControlCreator !is null) {
1439 int delay= fAutoActivationDelay;
1440 if (delay is 0)
1441 delay= DEFAULT_AUTO_ACTIVATION_DELAY;
1442 delay= cast(int)Math.round(delay * 1.5f);
1443 controller= new AdditionalInfoController(fInformationControlCreator, delay);
1444 }
1445
1446 fContextInfoPopup= fContentAssistSubjectControlAdapter.createContextInfoPopup(this);
1447 fProposalPopup= fContentAssistSubjectControlAdapter.createCompletionProposalPopup(this, controller);
1448
1449 registerHandler(SELECT_NEXT_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_NEXT));
1450 registerHandler(SELECT_PREVIOUS_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_PREVIOUS));
1451
1452 if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
1453 fContentAssistSubjectControlShell= fContentAssistSubjectControlAdapter.getControl().getShell();
1454 fCASCSTraverseListener= new class() TraverseListener {
1455 public void keyTraversed(TraverseEvent e) {
1456 if (e.detail is SWT.TRAVERSE_ESCAPE && isProposalPopupActive())
1457 e.doit= false;
1458 }
1459 };
1460 fContentAssistSubjectControlShell.addTraverseListener(fCASCSTraverseListener);
1461 }
1462
1463 manageAutoActivation(fIsAutoActivated);
1464 }
1465
1466 /*
1467 * @see IContentAssist#uninstall
1468 */
1469 public void uninstall() {
1470 hide();
1471 manageAutoActivation(false);
1472
1473 if (fHandlers !is null) {
1474 fHandlers.clear();
1475 fHandlers= null;
1476 }
1477
1478 if (fCloser !is null) {
1479 fCloser.uninstall();
1480 fCloser= null;
1481 }
1482
1483 if (Helper.okToUse(fContentAssistSubjectControlShell))
1484 fContentAssistSubjectControlShell.removeTraverseListener(fCASCSTraverseListener);
1485 fCASCSTraverseListener= null;
1486 fContentAssistSubjectControlShell= null;
1487
1488 fViewer= null;
1489 fContentAssistSubjectControl= null;
1490 fContentAssistSubjectControlAdapter= null;
1491 }
1492
1493 /**
1494 * Adds the given shell of the specified type to the layout. Valid types are defined by
1495 * <code>LayoutManager</code>.
1496 *
1497 * @param popup a content assist popup
1498 * @param shell the shell of the content-assist popup
1499 * @param type the type of popup
1500 * @param visibleOffset the offset at which to layout the popup relative to the offset of the
1501 * viewer's visible region
1502 * @since 2.0
1503 */
1504 void addToLayout(Object popup, Shell shell, int type, int visibleOffset) {
1505 fLayoutManager.add(popup, shell, type, visibleOffset);
1506 }
1507
1508 /**
1509 * Layouts the registered popup of the given type relative to the given offset. The offset is
1510 * relative to the offset of the viewer's visible region. Valid types are defined by
1511 * <code>LayoutManager</code>.
1512 *
1513 * @param type the type of popup to layout
1514 * @param visibleOffset the offset at which to layout relative to the offset of the viewer's
1515 * visible region
1516 * @since 2.0
1517 */
1518 void layout(int type, int visibleOffset) {
1519 fLayoutManager.layout(type, visibleOffset);
1520 }
1521
1522 /**
1523 * Returns the layout manager.
1524 *
1525 * @return the layout manager
1526 * @since 3.3
1527 */
1528 LayoutManager getLayoutManager() {
1529 return fLayoutManager;
1530 }
1531
1532 /**
1533 * Notifies the controller that a popup has lost focus.
1534 *
1535 * @param e the focus event
1536 */
1537 void popupFocusLost(FocusEvent e) {
1538 fCloser.focusLost(e);
1539 }
1540
1541 /**
1542 * Returns the offset of the selection relative to the offset of the visible region.
1543 *
1544 * @return the offset of the selection relative to the offset of the visible region
1545 * @since 2.0
1546 */
1547 int getSelectionOffset() {
1548 return fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
1549 }
1550
1551 /**
1552 * Returns whether the widget token could be acquired. The following are valid listener types:
1553 * <ul>
1554 * <li>AUTO_ASSIST</li>
1555 * <li>CONTEXT_SELECTOR</li>
1556 * <li>PROPOSAL_SELECTOR</li>
1557 * <li>CONTEXT_INFO_POPUP</li>
1558 * </ul>
1559 *
1560 * @param type the listener type for which to acquire
1561 * @return <code>true</code> if the widget token could be acquired
1562 * @since 2.0
1563 */
1564 private bool acquireWidgetToken(int type) {
1565 switch (type) {
1566 case CONTEXT_SELECTOR:
1567 case PROPOSAL_SELECTOR:
1568 if ( cast(IWidgetTokenOwnerExtension)fContentAssistSubjectControl ) {
1569 IWidgetTokenOwnerExtension extension= cast(IWidgetTokenOwnerExtension) fContentAssistSubjectControl;
1570 return extension.requestWidgetToken(this, WIDGET_PRIORITY);
1571 } else if ( cast(IWidgetTokenOwner)fContentAssistSubjectControl ) {
1572 IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fContentAssistSubjectControl;
1573 return owner.requestWidgetToken(this);
1574 } else if ( cast(IWidgetTokenOwnerExtension)fViewer ) {
1575 IWidgetTokenOwnerExtension extension= cast(IWidgetTokenOwnerExtension) fViewer;
1576 return extension.requestWidgetToken(this, WIDGET_PRIORITY);
1577 } else if ( cast(IWidgetTokenOwner)fViewer ) {
1578 IWidgetTokenOwner owner= cast(IWidgetTokenOwner) fViewer;
1579 return owner.requestWidgetToken(this);
1580 }
1581 default:
1582 }
1583 return true;
1584 }
1585
1586 /**
1587 * Registers a content assist listener. The following are valid listener types:
1588 * <ul>
1589 * <li>AUTO_ASSIST</li>
1590 * <li>CONTEXT_SELECTOR</li>
1591 * <li>PROPOSAL_SELECTOR</li>
1592 * <li>CONTEXT_INFO_POPUP</li>
1593 * </ul>
1594 * Returns whether the listener could be added successfully. A listener can not be added if the
1595 * widget token could not be acquired.
1596 *
1597 * @param listener the listener to register
1598 * @param type the type of listener
1599 * @return <code>true</code> if the listener could be added
1600 */
1601 bool addContentAssistListener(IContentAssistListener listener, int type) {
1602
1603 if (acquireWidgetToken(type)) {
1604
1605 fListeners[type]= listener;
1606
1607 if (fCloser is null && getNumberOfListeners() is 1) {
1608 fCloser= new Closer();
1609 fCloser.install();
1610 fContentAssistSubjectControlAdapter.setEventConsumer(fInternalListener);
1611 installKeyListener();
1612 } else
1613 promoteKeyListener();
1614 return true;
1615 }
1616
1617 return false;
1618 }
1619
1620 /**
1621 * Re-promotes the key listener to the first position, using prependVerifyKeyListener. This
1622 * ensures no other instance is filtering away the keystrokes underneath, if we've been up for a
1623 * while (e.g. when the context info is showing.
1624 *
1625 * @since 3.0
1626 */
1627 private void promoteKeyListener() {
1628 uninstallVerifyKeyListener();
1629 installKeyListener();
1630 }
1631
1632 /**
1633 * Installs a key listener on the text viewer's widget.
1634 */
1635 private void installKeyListener() {
1636 if (!fVerifyKeyListenerHooked) {
1637 if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
1638 fVerifyKeyListenerHooked= fContentAssistSubjectControlAdapter.prependVerifyKeyListener(fInternalListener);
1639 }
1640 }
1641 }
1642
1643 /**
1644 * Releases the previously acquired widget token if the token is no longer necessary. The
1645 * following are valid listener types:
1646 * <ul>
1647 * <li>AUTO_ASSIST</li>
1648 * <li>CONTEXT_SELECTOR</li>
1649 * <li>PROPOSAL_SELECTOR</li>
1650 * <li>CONTEXT_INFO_POPUP</li>
1651 * </ul>
1652 *
1653 * @param type the listener type
1654 * @since 2.0
1655 */
1656 private void releaseWidgetToken(int type) {
1657 if (fListeners[CONTEXT_SELECTOR] is null && fListeners[PROPOSAL_SELECTOR] is null) {
1658 IWidgetTokenOwner owner= null;
1659 if ( cast(IWidgetTokenOwner)fContentAssistSubjectControl )
1660 owner= cast(IWidgetTokenOwner) fContentAssistSubjectControl;
1661 else if ( cast(IWidgetTokenOwner)fViewer )
1662 owner= cast(IWidgetTokenOwner) fViewer;
1663 if (owner !is null)
1664 owner.releaseWidgetToken(this);
1665 }
1666 }
1667
1668 /**
1669 * Unregisters a content assist listener.
1670 *
1671 * @param listener the listener to unregister
1672 * @param type the type of listener
1673 * @see #addContentAssistListener(IContentAssistListener, int)
1674 */
1675 void removeContentAssistListener(IContentAssistListener listener, int type) {
1676 fListeners[type]= null;
1677
1678 if (getNumberOfListeners() is 0) {
1679
1680 if (fCloser !is null) {
1681 fCloser.uninstall();
1682 fCloser= null;
1683 }
1684
1685 uninstallVerifyKeyListener();
1686 fContentAssistSubjectControlAdapter.setEventConsumer(null);
1687 }
1688
1689 releaseWidgetToken(type);
1690 }
1691
1692 /**
1693 * Uninstall the key listener from the text viewer's widget.
1694 *
1695 * @since 3.0
1696 */
1697 private void uninstallVerifyKeyListener() {
1698 if (fVerifyKeyListenerHooked) {
1699 if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl()))
1700 fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fInternalListener);
1701 fVerifyKeyListenerHooked= false;
1702 }
1703 }
1704
1705 /**
1706 * Returns the number of listeners.
1707 *
1708 * @return the number of listeners
1709 * @since 2.0
1710 */
1711 private int getNumberOfListeners() {
1712 int count= 0;
1713 for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) {
1714 if (fListeners[i] !is null)
1715 ++count;
1716 }
1717 return count;
1718 }
1719
1720 /*
1721 * @see IContentAssist#showPossibleCompletions
1722 */
1723 public String showPossibleCompletions() {
1724 if (!prepareToShowCompletions(false))
1725 return null;
1726 if (fIsPrefixCompletionEnabled)
1727 return fProposalPopup.incrementalComplete();
1728 return fProposalPopup.showProposals(false);
1729 }
1730
1731 /*
1732 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#completePrefix()
1733 * @since 3.0
1734 */
1735 public String completePrefix() {
1736 if (!prepareToShowCompletions(false))
1737 return null;
1738 return fProposalPopup.incrementalComplete();
1739 }
1740
1741 /**
1742 * Prepares to show content assist proposals. It returns false if auto activation has kicked in
1743 * recently.
1744 *
1745 * @param isAutoActivated whether completion was triggered by auto activation
1746 * @return <code>true</code> if the caller should continue and show the proposals,
1747 * <code>false</code> otherwise.
1748 * @since 3.2
1749 */
1750 private bool prepareToShowCompletions(bool isAutoActivated) {
1751 long current= System.currentTimeMillis();
1752 int gracePeriod= Math.max(fAutoActivationDelay, 200);
1753 if (current < fLastAutoActivation + gracePeriod)
1754 return false;
1755
1756 promoteKeyListener();
1757 fireSessionBeginEvent(isAutoActivated);
1758 return true;
1759 }
1760
1761 /**
1762 * Callback to signal this content assistant that the presentation of the possible completions
1763 * has been stopped.
1764 *
1765 * @since 2.1
1766 */
1767 protected void possibleCompletionsClosed() {
1768 fLastAutoActivation= Long.MIN_VALUE;
1769 storeCompletionProposalPopupSize();
1770 }
1771 package void possibleCompletionsClosed_package() {
1772 possibleCompletionsClosed();
1773 }
1774
1775 /*
1776 * @see IContentAssist#showContextInformation
1777 */
1778 public String showContextInformation() {
1779 promoteKeyListener();
1780 if (fContextInfoPopup !is null)
1781 return fContextInfoPopup.showContextProposals(false);
1782 return null;
1783 }
1784
1785 /**
1786 * Callback to signal this content assistant that the presentation of the context information
1787 * has been stopped.
1788 *
1789 * @since 2.1
1790 */
1791 protected void contextInformationClosed() {
1792 }
1793 package void contextInformationClosed_package() {
1794 contextInformationClosed();
1795 }
1796
1797 /**
1798 * Requests that the specified context information to be shown.
1799 *
1800 * @param contextInformation the context information to be shown
1801 * @param offset the offset to which the context information refers to
1802 * @since 2.0
1803 */
1804 void showContextInformation(IContextInformation contextInformation, int offset) {
1805 if (fContextInfoPopup !is null)
1806 fContextInfoPopup.showContextInformation(contextInformation, offset);
1807 }
1808
1809 /**
1810 * Returns the current content assist error message.
1811 *
1812 * @return an error message or <code>null</code> if no error has occurred
1813 */
1814 String getErrorMessage() {
1815 return fLastErrorMessage;
1816 }
1817
1818 /**
1819 * Returns the content assist processor for the content type of the specified document position.
1820 *
1821 * @param viewer the text viewer
1822 * @param offset a offset within the document
1823 * @return a content-assist processor or <code>null</code> if none exists
1824 * @since 3.0
1825 */
1826 private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) {
1827 try {
1828
1829 IDocument document= viewer.getDocument();
1830 String type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);
1831
1832 return getContentAssistProcessor(type);
1833
1834 } catch (BadLocationException x) {
1835 }
1836
1837 return null;
1838 }
1839
1840 /**
1841 * Returns the content assist processor for the content type of the specified document position.
1842 *
1843 * @param contentAssistSubjectControl the content assist subject control
1844 * @param offset a offset within the document
1845 * @return a content-assist processor or <code>null</code> if none exists
1846 * @since 3.0
1847 */
1848 private IContentAssistProcessor getProcessor(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1849 try {
1850
1851 IDocument document= contentAssistSubjectControl.getDocument();
1852 String type;
1853 if (document !is null)
1854 type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);
1855 else
1856 type= IDocument.DEFAULT_CONTENT_TYPE;
1857
1858 return getContentAssistProcessor(type);
1859
1860 } catch (BadLocationException x) {
1861 }
1862
1863 return null;
1864 }
1865
1866 /**
1867 * Returns an array of completion proposals computed based on the specified document position.
1868 * The position is used to determine the appropriate content assist processor to invoke.
1869 *
1870 * @param contentAssistSubjectControl the content assist subject control
1871 * @param offset a document offset
1872 * @return an array of completion proposals
1873 * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
1874 * @since 3.0
1875 */
1876 ICompletionProposal[] computeCompletionProposals(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1877 fLastErrorMessage= null;
1878
1879 ICompletionProposal[] result= null;
1880
1881 IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1882 if ( cast(ISubjectControlContentAssistProcessor)p ) {
1883 result= (cast(ISubjectControlContentAssistProcessor) p).computeCompletionProposals(contentAssistSubjectControl, offset);
1884 fLastErrorMessage= p.getErrorMessage();
1885 }
1886
1887 return result;
1888 }
1889
1890 /**
1891 * Returns an array of completion proposals computed based on the specified document position.
1892 * The position is used to determine the appropriate content assist processor to invoke.
1893 *
1894 * @param viewer the viewer for which to compute the proposals
1895 * @param offset a document offset
1896 * @return an array of completion proposals or <code>null</code> if no proposals are possible
1897 * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
1898 */
1899 ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
1900 fLastErrorMessage= null;
1901
1902 ICompletionProposal[] result= null;
1903
1904 IContentAssistProcessor p= getProcessor(viewer, offset);
1905 if (p !is null) {
1906 result= p.computeCompletionProposals(viewer, offset);
1907 fLastErrorMessage= p.getErrorMessage();
1908 }
1909
1910 return result;
1911 }
1912
1913 /**
1914 * Returns an array of context information objects computed based on the specified document
1915 * position. The position is used to determine the appropriate content assist processor to
1916 * invoke.
1917 *
1918 * @param viewer the viewer for which to compute the context information
1919 * @param offset a document offset
1920 * @return an array of context information objects
1921 * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
1922 */
1923 IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
1924 fLastErrorMessage= null;
1925
1926 IContextInformation[] result= null;
1927
1928 IContentAssistProcessor p= getProcessor(viewer, offset);
1929 if (p !is null) {
1930 result= p.computeContextInformation(viewer, offset);
1931 fLastErrorMessage= p.getErrorMessage();
1932 }
1933
1934 return result;
1935 }
1936
1937 /**
1938 * Returns an array of context information objects computed based on the specified document
1939 * position. The position is used to determine the appropriate content assist processor to
1940 * invoke.
1941 *
1942 * @param contentAssistSubjectControl the content assist subject control
1943 * @param offset a document offset
1944 * @return an array of context information objects
1945 * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
1946 * @since 3.0
1947 */
1948 IContextInformation[] computeContextInformation(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1949 fLastErrorMessage= null;
1950
1951 IContextInformation[] result= null;
1952
1953 IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1954 if ( cast(ISubjectControlContentAssistProcessor)p ) {
1955 result= (cast(ISubjectControlContentAssistProcessor) p).computeContextInformation(contentAssistSubjectControl, offset);
1956 fLastErrorMessage= p.getErrorMessage();
1957 }
1958
1959 return result;
1960 }
1961
1962 /**
1963 * Returns the context information validator that should be used to determine when the currently
1964 * displayed context information should be dismissed. The position is used to determine the
1965 * appropriate content assist processor to invoke.
1966 *
1967 * @param viewer the text viewer
1968 * @param offset a document offset
1969 * @return an validator
1970 * @see IContentAssistProcessor#getContextInformationValidator()
1971 * @since 3.0
1972 */
1973 IContextInformationValidator getContextInformationValidator(ITextViewer viewer, int offset) {
1974 IContentAssistProcessor p= getProcessor(viewer, offset);
1975 return p !is null ? p.getContextInformationValidator() : null;
1976 }
1977
1978 /**
1979 * Returns the context information validator that should be used to determine when the currently
1980 * displayed context information should be dismissed. The position is used to determine the
1981 * appropriate content assist processor to invoke.
1982 *
1983 * @param contentAssistSubjectControl the content assist subject control
1984 * @param offset a document offset
1985 * @return an validator
1986 * @see IContentAssistProcessor#getContextInformationValidator()
1987 * @since 3.0
1988 */
1989 IContextInformationValidator getContextInformationValidator(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1990 IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1991 return p !is null ? p.getContextInformationValidator() : null;
1992 }
1993
1994 /**
1995 * Returns the context information presenter that should be used to display context information.
1996 * The position is used to determine the appropriate content assist processor to invoke.
1997 *
1998 * @param viewer the text viewer
1999 * @param offset a document offset
2000 * @return a presenter
2001 * @since 2.0
2002 */
2003 IContextInformationPresenter getContextInformationPresenter(ITextViewer viewer, int offset) {
2004 IContextInformationValidator validator= getContextInformationValidator(viewer, offset);
2005 if ( cast(IContextInformationPresenter)validator )
2006 return cast(IContextInformationPresenter) validator;
2007 return null;
2008 }
2009
2010 /**
2011 * Returns the context information presenter that should be used to display context information.
2012 * The position is used to determine the appropriate content assist processor to invoke.
2013 *
2014 * @param contentAssistSubjectControl the content assist subject control
2015 * @param offset a document offset
2016 * @return a presenter
2017 * @since 3.0
2018 */
2019 IContextInformationPresenter getContextInformationPresenter(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
2020 IContextInformationValidator validator= getContextInformationValidator(contentAssistSubjectControl, offset);
2021 if ( cast(IContextInformationPresenter)validator )
2022 return cast(IContextInformationPresenter) validator;
2023 return null;
2024 }
2025
2026 /**
2027 * Returns the characters which when typed by the user should automatically initiate proposing
2028 * completions. The position is used to determine the appropriate content assist processor to
2029 * invoke.
2030 *
2031 * @param contentAssistSubjectControl the content assist subject control
2032 * @param offset a document offset
2033 * @return the auto activation characters
2034 * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
2035 * @since 3.0
2036 */
2037 char[] getCompletionProposalAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
2038 IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
2039 return p !is null ? p.getCompletionProposalAutoActivationCharacters() : null;
2040 }
2041
2042 /**
2043 * Returns the characters which when typed by the user should automatically initiate proposing
2044 * completions. The position is used to determine the appropriate content assist processor to
2045 * invoke.
2046 *
2047 * @param viewer the text viewer
2048 * @param offset a document offset
2049 * @return the auto activation characters
2050 * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
2051 */
2052 char[] getCompletionProposalAutoActivationCharacters(ITextViewer viewer, int offset) {
2053 IContentAssistProcessor p= getProcessor(viewer, offset);
2054 return p !is null ? p.getCompletionProposalAutoActivationCharacters() : null;
2055 }
2056
2057 /**
2058 * Returns the characters which when typed by the user should automatically initiate the
2059 * presentation of context information. The position is used to determine the appropriate
2060 * content assist processor to invoke.
2061 *
2062 * @param viewer the text viewer
2063 * @param offset a document offset
2064 * @return the auto activation characters
2065 * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
2066 * @since 3.0
2067 */
2068 char[] getContextInformationAutoActivationCharacters(ITextViewer viewer, int offset) {
2069 IContentAssistProcessor p= getProcessor(viewer, offset);
2070 return p !is null ? p.getContextInformationAutoActivationCharacters() : null;
2071 }
2072
2073 /**
2074 * Returns the characters which when typed by the user should automatically initiate the
2075 * presentation of context information. The position is used to determine the appropriate
2076 * content assist processor to invoke.
2077 *
2078 * @param contentAssistSubjectControl the content assist subject control
2079 * @param offset a document offset
2080 * @return the auto activation characters
2081 * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
2082 * @since 3.0
2083 */
2084 char[] getContextInformationAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
2085 IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
2086 return p !is null ? p.getContextInformationAutoActivationCharacters() : null;
2087 }
2088
2089 /*
2090 * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
2091 * @since 2.0
2092 */
2093 public bool requestWidgetToken(IWidgetTokenOwner owner) {
2094 return false;
2095 }
2096
2097 /*
2098 * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner,
2099 * int)
2100 * @since 3.0
2101 */
2102 public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
2103 if (priority > WIDGET_PRIORITY) {
2104 hide();
2105 return true;
2106 }
2107 return false;
2108 }
2109
2110 /*
2111 * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
2112 * @since 3.0
2113 */
2114 public bool setFocus(IWidgetTokenOwner owner) {
2115 if (fProposalPopup !is null) {
2116 fProposalPopup.setFocus();
2117 return fProposalPopup.hasFocus();
2118 }
2119 return false;
2120 }
2121
2122 /**
2123 * Hides any open pop-ups.
2124 *
2125 * @since 3.0
2126 */
2127 protected void hide() {
2128 if (fProposalPopup !is null)
2129 fProposalPopup.hide();
2130
2131 if (fContextInfoPopup !is null)
2132 fContextInfoPopup.hide();
2133 }
2134 package void hide_package() {
2135 hide();
2136 }
2137
2138 // ------ control's size handling dialog settings ------
2139
2140 /**
2141 * Tells this information control manager to open the information control with the values
2142 * contained in the given dialog settings and to store the control's last valid size in the
2143 * given dialog settings.
2144 * <p>
2145 * Note: This API is only valid if the information control implements
2146 * {@link org.eclipse.jface.text.IInformationControlExtension3}. Not following this restriction
2147 * will later result in an {@link UnsupportedOperationException}.
2148 * </p>
2149 * <p>
2150 * The constants used to store the values are:
2151 * <ul>
2152 * <li>{@link ContentAssistant#STORE_SIZE_X}</li>
2153 * <li>{@link ContentAssistant#STORE_SIZE_Y}</li>
2154 * </ul>
2155 * </p>
2156 *
2157 * @param dialogSettings
2158 * @since 3.0
2159 */
2160 public void setRestoreCompletionProposalSize(IDialogSettings dialogSettings) {
2161 Assert.isTrue(dialogSettings !is null);
2162 fDialogSettings= dialogSettings;
2163 }
2164
2165 /**
2166 * Stores the content assist pop-up's size.
2167 */
2168 protected void storeCompletionProposalPopupSize() {
2169 if (fDialogSettings is null || fProposalPopup is null)
2170 return;
2171
2172 Point size= fProposalPopup.getSize();
2173 if (size is null)
2174 return;
2175
2176 fDialogSettings.put(STORE_SIZE_X, size.x);
2177 fDialogSettings.put(STORE_SIZE_Y, size.y);
2178 }
2179
2180 /**
2181 * Restores the content assist pop-up's size.
2182 *
2183 * @return the stored size
2184 * @since 3.0
2185 */
2186 protected Point restoreCompletionProposalPopupSize() {
2187 if (fDialogSettings is null)
2188 return null;
2189
2190 Point size= new Point(-1, -1);
2191
2192 try {
2193 size.x= fDialogSettings.getInt(STORE_SIZE_X);
2194 size.y= fDialogSettings.getInt(STORE_SIZE_Y);
2195 } catch (NumberFormatException ex) {
2196 size.x= -1;
2197 size.y= -1;
2198 }
2199
2200 // sanity check
2201 if (size.x is -1 && size.y is -1)
2202 return null;
2203
2204 Rectangle maxBounds= null;
2205 if (fContentAssistSubjectControl !is null && Helper.okToUse(fContentAssistSubjectControl.getControl()))
2206 maxBounds= fContentAssistSubjectControl.getControl().getDisplay().getBounds();
2207 else {
2208 // fallback
2209 Display display= Display.getCurrent();
2210 if (display is null)
2211 display= Display.getDefault();
2212 if (display !is null && !display.isDisposed())
2213 maxBounds= display.getBounds();
2214 }
2215
2216 if (size.x > -1 && size.y > -1) {
2217 if (maxBounds !is null) {
2218 size.x= Math.min(size.x, maxBounds.width);
2219 size.y= Math.min(size.y, maxBounds.height);
2220 }
2221
2222 // Enforce an absolute minimal size
2223 size.x= Math.max(size.x, 30);
2224 size.y= Math.max(size.y, 30);
2225 }
2226
2227 return size;
2228 }
2229 package Point restoreCompletionProposalPopupSize_package() {
2230 return restoreCompletionProposalPopupSize();
2231 }
2232
2233 /**
2234 * Sets the prefix completion property. If enabled, content assist delegates completion to
2235 * prefix completion.
2236 *
2237 * @param enabled <code>true</code> to enable prefix completion, <code>false</code> to
2238 * disable
2239 */
2240 public void enablePrefixCompletion(bool enabled) {
2241 fIsPrefixCompletionEnabled= enabled;
2242 }
2243
2244 /**
2245 * Returns the prefix completion state.
2246 *
2247 * @return <code>true</code> if prefix completion is enabled, <code>false</code> otherwise
2248 * @since 3.2
2249 */
2250 bool isPrefixCompletionEnabled() {
2251 return fIsPrefixCompletionEnabled;
2252 }
2253
2254 /**
2255 * Returns whether the content assistant proposal popup has the focus.
2256 *
2257 * @return <code>true</code> if the proposal popup has the focus
2258 * @since 3.0
2259 */
2260 public bool hasProposalPopupFocus() {
2261 return fProposalPopup.hasFocus();
2262 }
2263
2264 /*
2265 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#addCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
2266 * @since 3.2
2267 */
2268 public void addCompletionListener(ICompletionListener listener) {
2269 Assert.isLegal(listener !is null);
2270 fCompletionListeners.add(cast(Object)listener);
2271 }
2272
2273 /*
2274 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#removeCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
2275 * @since 3.2
2276 */
2277 public void removeCompletionListener(ICompletionListener listener) {
2278 fCompletionListeners.remove(cast(Object)listener);
2279 }
2280
2281 /**
2282 * Fires a session begin event to all registered {@link ICompletionListener}s.
2283 *
2284 * @param isAutoActivated <code>true</code> if this session was triggered by auto activation
2285 * @since 3.2
2286 */
2287 void fireSessionBeginEvent(bool isAutoActivated) {
2288 if (fContentAssistSubjectControlAdapter !is null && !isProposalPopupActive()) {
2289 IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
2290 ContentAssistEvent event= new ContentAssistEvent(this, processor, isAutoActivated);
2291 Object[] listeners= fCompletionListeners.getListeners();
2292 for (int i= 0; i < listeners.length; i++) {
2293 ICompletionListener listener= cast(ICompletionListener)listeners[i];
2294 listener.assistSessionStarted(event);
2295 }
2296 }
2297 }
2298
2299 /**
2300 * Fires a session restart event to all registered {@link ICompletionListener}s.
2301 *
2302 * @since 3.4
2303 */
2304 void fireSessionRestartEvent() {
2305 if (fContentAssistSubjectControlAdapter !is null) {
2306 IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
2307 ContentAssistEvent event= new ContentAssistEvent(this, processor);
2308 Object[] listeners= fCompletionListeners.getListeners();
2309 for (int i= 0; i < listeners.length; i++) {
2310 ICompletionListener listener= cast(ICompletionListener)listeners[i];
2311 if ( cast(ICompletionListenerExtension)listener )
2312 (cast(ICompletionListenerExtension)listener).assistSessionRestarted(event);
2313 }
2314 }
2315 }
2316
2317 /**
2318 * Fires a session end event to all registered {@link ICompletionListener}s.
2319 *
2320 * @since 3.2
2321 */
2322 void fireSessionEndEvent() {
2323 if (fContentAssistSubjectControlAdapter !is null) {
2324 IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
2325 ContentAssistEvent event= new ContentAssistEvent(this, processor);
2326 Object[] listeners= fCompletionListeners.getListeners();
2327 for (int i= 0; i < listeners.length; i++) {
2328 ICompletionListener listener= cast(ICompletionListener)listeners[i];
2329 listener.assistSessionEnded(event);
2330 }
2331 }
2332 }
2333
2334 /*
2335 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setRepeatedInvocationMode(bool)
2336 * @since 3.2
2337 */
2338 public void setRepeatedInvocationMode(bool cycling) {
2339 fIsRepetitionMode= cycling;
2340 }
2341
2342 /**
2343 * Returns <code>true</code> if repeated invocation mode is enabled, <code>false</code>
2344 * otherwise.
2345 *
2346 * @return <code>true</code> if repeated invocation mode is enabled, <code>false</code>
2347 * otherwise
2348 * @since 3.2
2349 */
2350 bool isRepeatedInvocationMode() {
2351 return fIsRepetitionMode;
2352 }
2353
2354 /*
2355 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setShowEmptyList(bool)
2356 * @since 3.2
2357 */
2358 public void setShowEmptyList(bool showEmpty) {
2359 fShowEmptyList= showEmpty;
2360 }
2361
2362 /**
2363 * Returns <code>true</code> if empty lists should be displayed, <code>false</code>
2364 * otherwise.
2365 *
2366 * @return <code>true</code> if empty lists should be displayed, <code>false</code>
2367 * otherwise
2368 * @since 3.2
2369 */
2370 bool isShowEmptyList() {
2371 return fShowEmptyList;
2372 }
2373
2374 /*
2375 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusLineVisible(bool)
2376 * @since 3.2
2377 */
2378 public void setStatusLineVisible(bool show) {
2379 fIsStatusLineVisible= show;
2380 if (fProposalPopup !is null)
2381 fProposalPopup.setStatusLineVisible(show);
2382 }
2383
2384 /**
2385 * Returns <code>true</code> if a message line should be displayed, <code>false</code>
2386 * otherwise.
2387 *
2388 * @return <code>true</code> if a message line should be displayed, <code>false</code>
2389 * otherwise
2390 * @since 3.2
2391 */
2392 bool isStatusLineVisible() {
2393 return fIsStatusLineVisible;
2394 }
2395
2396 /*
2397 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusMessage(java.lang.String)
2398 * @since 3.2
2399 */
2400 public void setStatusMessage(String message) {
2401 Assert.isLegal(message !is null);
2402 fMessage= message;
2403 if (fProposalPopup !is null)
2404 fProposalPopup.setMessage(message);
2405 }
2406
2407 /**
2408 * Returns the affordance caption for the cycling affordance at the bottom of the pop-up.
2409 *
2410 * @return the affordance caption for the cycling affordance at the bottom of the pop-up
2411 * @since 3.2
2412 */
2413 String getStatusMessage() {
2414 return fMessage;
2415 }
2416
2417 /*
2418 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setEmptyMessage(java.lang.String)
2419 * @since 3.2
2420 */
2421 public void setEmptyMessage(String message) {
2422 Assert.isLegal(message !is null);
2423 if (fProposalPopup !is null)
2424 fProposalPopup.setEmptyMessage(message);
2425 }
2426
2427 /**
2428 * Fires a selection event, see {@link ICompletionListener}.
2429 *
2430 * @param proposal the selected proposal, possibly <code>null</code>
2431 * @param smartToggle true if the smart toggle is on
2432 * @since 3.2
2433 */
2434 void fireSelectionEvent(ICompletionProposal proposal, bool smartToggle) {
2435 Object[] listeners= fCompletionListeners.getListeners();
2436 for (int i= 0; i < listeners.length; i++) {
2437 ICompletionListener listener= cast(ICompletionListener)listeners[i];
2438 listener.selectionChanged(proposal, smartToggle);
2439 }
2440 }
2441
2442 /*
2443 * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension3#setInvocationTrigger(org.eclipse.jface.bindings.keys.KeySequence)
2444 * @since 3.2
2445 */
2446 public void setRepeatedInvocationTrigger(KeySequence sequence) {
2447 fRepeatedInvocationKeySequence= sequence;
2448 }
2449
2450 /**
2451 * Returns the repeated invocation key sequence.
2452 *
2453 * @return the repeated invocation key sequence or <code>null</code>, if none
2454 * @since 3.2
2455 */
2456 KeySequence getRepeatedInvocationKeySequence() {
2457 return fRepeatedInvocationKeySequence;
2458 }
2459
2460 /**
2461 * Returns whether proposal popup is active.
2462 *
2463 * @return <code>true</code> if the proposal popup is active, <code>false</code> otherwise
2464 * @since 3.4
2465 */
2466 protected bool isProposalPopupActive(){
2467 return fProposalPopup !is null && fProposalPopup.isActive();
2468 }
2469
2470 /**
2471 * Returns whether the context information popup is active.
2472 *
2473 * @return <code>true</code> if the context information popup is active, <code>false</code> otherwise
2474 * @since 3.4
2475 */
2476 protected bool isContextInfoPopupActive(){
2477 return fContextInfoPopup !is null && fContextInfoPopup.isActive();
2478 }
2479
2480 /**
2481 * {@inheritDoc}
2482 *
2483 * @since 3.4
2484 */
2485 public final IHandler getHandler(String commandId) {
2486 if (fHandlers is null)
2487 throw new IllegalStateException();
2488
2489 IHandler handler= cast(IHandler)fHandlers.get(commandId);
2490 if (handler !is null)
2491 return handler;
2492
2493 Assert.isLegal(false);
2494 return null;
2495 }
2496
2497 /**
2498 * Registers the given handler under the given command identifier.
2499 *
2500 * @param commandId the command identifier
2501 * @param handler the handler
2502 * @since 3.4
2503 */
2504 protected final void registerHandler(String commandId, IHandler handler) {
2505 if (fHandlers is null)
2506 fHandlers= new HashMap(2);
2507 fHandlers.put(commandId, cast(Object)handler);
2508 }
2509
2510 /**
2511 * Tells whether the support for colored labels is enabled.
2512 *
2513 * @return <code>true</code> if the support for colored labels is enabled, <code>false</code> otherwise
2514 * @since 3.4
2515 */
2516 bool isColoredLabelsSupportEnabled() {
2517 return fIsColoredLabelsSupportEnabled;
2518 }
2519
2520 /**
2521 * Enables the support for colored labels in the proposal popup.
2522 * <p>Completion proposals can implement {@link ICompletionProposalExtension6}
2523 * to provide colored proposal labels.</p>
2524 *
2525 * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup
2526 * @since 3.4
2527 */
2528 public void enableColoredLabels(bool isEnabled) {
2529 fIsColoredLabelsSupportEnabled= isEnabled;
2530 }
2531
2532 }