KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > contentassist > ContentAssistant


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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  *******************************************************************************/

11 package org.eclipse.jface.text.contentassist;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.Map.Entry;
19
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.SWTError;
22 import org.eclipse.swt.custom.VerifyKeyListener;
23 import org.eclipse.swt.events.ControlEvent;
24 import org.eclipse.swt.events.ControlListener;
25 import org.eclipse.swt.events.DisposeEvent;
26 import org.eclipse.swt.events.DisposeListener;
27 import org.eclipse.swt.events.FocusEvent;
28 import org.eclipse.swt.events.FocusListener;
29 import org.eclipse.swt.events.KeyAdapter;
30 import org.eclipse.swt.events.KeyEvent;
31 import org.eclipse.swt.events.KeyListener;
32 import org.eclipse.swt.events.MouseEvent;
33 import org.eclipse.swt.events.MouseListener;
34 import org.eclipse.swt.events.TraverseEvent;
35 import org.eclipse.swt.events.TraverseListener;
36 import org.eclipse.swt.events.VerifyEvent;
37 import org.eclipse.swt.graphics.Color;
38 import org.eclipse.swt.graphics.Point;
39 import org.eclipse.swt.graphics.Rectangle;
40 import org.eclipse.swt.widgets.Control;
41 import org.eclipse.swt.widgets.Display;
42 import org.eclipse.swt.widgets.Event;
43 import org.eclipse.swt.widgets.Listener;
44 import org.eclipse.swt.widgets.Monitor;
45 import org.eclipse.swt.widgets.Shell;
46 import org.eclipse.swt.widgets.Widget;
47
48 import org.eclipse.core.runtime.Assert;
49
50 import org.eclipse.jface.text.BadLocationException;
51 import org.eclipse.jface.text.IDocument;
52 import org.eclipse.jface.text.IDocumentExtension3;
53 import org.eclipse.jface.text.IEventConsumer;
54 import org.eclipse.jface.text.IInformationControlCreator;
55 import org.eclipse.jface.text.ITextViewer;
56 import org.eclipse.jface.text.IViewportListener;
57 import org.eclipse.jface.text.IWidgetTokenKeeper;
58 import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
59 import org.eclipse.jface.text.IWidgetTokenOwner;
60 import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
61 import org.eclipse.jface.text.TextUtilities;
62
63 import org.eclipse.jface.bindings.keys.KeySequence;
64 import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
65 import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
66 import org.eclipse.jface.dialogs.IDialogSettings;
67 import org.eclipse.jface.util.Geometry;
68
69 /**
70  * The standard implementation of the <code>IContentAssistant</code> interface. Usually, clients
71  * instantiate this class and configure it before using it.
72  */

73 public class ContentAssistant implements IContentAssistant, IContentAssistantExtension, IContentAssistantExtension2, IContentAssistantExtension3, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
74
75     /**
76      * A generic closer class used to monitor various interface events in order to determine whether
77      * content-assist should be terminated and all associated windows closed.
78      */

79     class Closer implements ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {
80
81         /** The shell that a <code>ControlListener</code> is registered with. */
82         private Shell fShell;
83         /**
84          * The control that a <code>MouseListener</code>, a<code>FocusListener</code> and a
85          * <code>DisposeListener</code> are registered with.
86          */

87         private Control fControl;
88
89         /**
90          * Installs this closer on it's viewer's text widget.
91          */

92         protected void install() {
93             Control control= fContentAssistSubjectControlAdapter.getControl();
94             fControl= control;
95             if (Helper.okToUse(control)) {
96
97                 Shell shell= control.getShell();
98                 fShell= shell;
99                 shell.addControlListener(this);
100
101                 control.addMouseListener(this);
102                 control.addFocusListener(this);
103
104                 /*
105                  * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
106                  * Internal Errors
107                  */

108                 control.addDisposeListener(this);
109             }
110             if (fViewer != null)
111                 fViewer.addViewportListener(this);
112         }
113
114         /**
115          * Uninstalls this closer from the viewer's text widget.
116          */

117         protected void uninstall() {
118             Control shell= fShell;
119             fShell= null;
120             if (Helper.okToUse(shell))
121                 shell.removeControlListener(this);
122
123             Control control= fControl;
124             fControl= null;
125             if (Helper.okToUse(control)) {
126
127                 control.removeMouseListener(this);
128                 control.removeFocusListener(this);
129
130                 /*
131                  * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
132                  * Internal Errors
133                  */

134                 control.removeDisposeListener(this);
135             }
136
137             if (fViewer != null)
138                 fViewer.removeViewportListener(this);
139         }
140
141         /*
142          * @see ControlListener#controlResized(ControlEvent)
143          */

144         public void controlResized(ControlEvent e) {
145             hide();
146         }
147
148         /*
149          * @see ControlListener#controlMoved(ControlEvent)
150          */

151         public void controlMoved(ControlEvent e) {
152             hide();
153         }
154
155         /*
156          * @see MouseListener#mouseDown(MouseEvent)
157          */

158         public void mouseDown(MouseEvent e) {
159             hide();
160         }
161
162         /*
163          * @see MouseListener#mouseUp(MouseEvent)
164          */

165         public void mouseUp(MouseEvent e) {
166         }
167
168         /*
169          * @see MouseListener#mouseDoubleClick(MouseEvent)
170          */

171         public void mouseDoubleClick(MouseEvent e) {
172             hide();
173         }
174
175         /*
176          * @see FocusListener#focusGained(FocusEvent)
177          */

178         public void focusGained(FocusEvent e) {
179         }
180
181         /*
182          * @see FocusListener#focusLost(FocusEvent)
183          */

184         public void focusLost(FocusEvent e) {
185             Control control= fControl;
186             if (Helper.okToUse(control)) {
187                 Display d= control.getDisplay();
188                 if (d != null) {
189                     d.asyncExec(new Runnable JavaDoc() {
190                         public void run() {
191                             if (!fProposalPopup.hasFocus() && (fContextInfoPopup == null || !fContextInfoPopup.hasFocus()))
192                                 hide();
193                         }
194                     });
195                 }
196             }
197         }
198
199         /*
200          * @seeDisposeListener#widgetDisposed(DisposeEvent)
201          */

202         public void widgetDisposed(DisposeEvent e) {
203             /*
204              * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal
205              * Errors
206              */

207             hide();
208         }
209
210         /*
211          * @see IViewportListener#viewportChanged(int)
212          */

213         public void viewportChanged(int topIndex) {
214             hide();
215         }
216     }
217
218     /**
219      * An implementation of <code>IContentAssistListener</code>, this class is used to monitor
220      * key events in support of automatic activation of the content assistant. If enabled, the
221      * implementation utilizes a thread to watch for input characters matching the activation
222      * characters specified by the content assist processor, and if detected, will wait the
223      * indicated delay interval before activating the content assistant.
224      */

225     class AutoAssistListener extends KeyAdapter implements KeyListener, Runnable JavaDoc, VerifyKeyListener {
226
227         private Thread JavaDoc fThread;
228         private boolean fIsReset= false;
229         private Object JavaDoc fMutex= new Object JavaDoc();
230         private int fShowStyle;
231
232         private final static int SHOW_PROPOSALS= 1;
233         private final static int SHOW_CONTEXT_INFO= 2;
234
235         protected AutoAssistListener() {
236         }
237
238         protected void start(int showStyle) {
239             fShowStyle= showStyle;
240             fThread= new Thread JavaDoc(this, JFaceTextMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
241
fThread.start();
242         }
243
244         public void run() {
245             try {
246                 while (true) {
247                     synchronized (fMutex) {
248                         if (fAutoActivationDelay != 0)
249                             fMutex.wait(fAutoActivationDelay);
250                         if (fIsReset) {
251                             fIsReset= false;
252                             continue;
253                         }
254                     }
255                     showAssist(fShowStyle);
256                     break;
257                 }
258             } catch (InterruptedException JavaDoc e) {
259             }
260             fThread= null;
261         }
262
263         protected void reset(int showStyle) {
264             synchronized (fMutex) {
265                 fShowStyle= showStyle;
266                 fIsReset= true;
267                 fMutex.notifyAll();
268             }
269         }
270
271         protected void stop() {
272             Thread JavaDoc threadToStop= fThread;
273             if (threadToStop != null && threadToStop.isAlive())
274                 threadToStop.interrupt();
275         }
276
277         private boolean contains(char[] characters, char character) {
278             if (characters != null) {
279                 for (int i= 0; i < characters.length; i++) {
280                     if (character == characters[i])
281                         return true;
282                 }
283             }
284             return false;
285         }
286
287         public void keyPressed(KeyEvent e) {
288             // Only act on typed characters and ignore modifier-only events
289
if (e.character == 0 && (e.keyCode & SWT.KEYCODE_BIT) == 0)
290                 return;
291             
292             if (e.character != 0 && (e.stateMask == SWT.ALT))
293                 return;
294             
295             // Only act on characters that are trigger candidates. This
296
// avoids computing the model selection on every keystroke
297
if (computeAllAutoActivationTriggers().indexOf(e.character) < 0) {
298                 stop();
299                 return;
300             }
301
302             int showStyle;
303             int pos= fContentAssistSubjectControlAdapter.getSelectedRange().x;
304             char[] activation;
305
306             activation= fContentAssistSubjectControlAdapter.getCompletionProposalAutoActivationCharacters(ContentAssistant.this, pos);
307
308             if (contains(activation, e.character) && !fProposalPopup.isActive())
309                 showStyle= SHOW_PROPOSALS;
310             else {
311                 activation= fContentAssistSubjectControlAdapter.getContextInformationAutoActivationCharacters(ContentAssistant.this, pos);
312                 if (contains(activation, e.character) && fContextInfoPopup != null && !fContextInfoPopup.isActive())
313                     showStyle= SHOW_CONTEXT_INFO;
314                 else {
315                     stop();
316                     return;
317                 }
318             }
319
320             if (fThread != null && fThread.isAlive())
321                 reset(showStyle);
322             else
323                 start(showStyle);
324         }
325
326         /*
327          * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
328          */

329         public void verifyKey(VerifyEvent event) {
330             keyPressed(event);
331         }
332
333         protected void showAssist(final int showStyle) {
334             final Control control= fContentAssistSubjectControlAdapter.getControl();
335             if (control == null)
336                 return;
337             
338             final Display d= control.getDisplay();
339             if (d == null)
340                 return;
341             
342             try {
343                 d.syncExec(new Runnable JavaDoc() {
344                     public void run() {
345                         if (fProposalPopup.isActive())
346                             return;
347                         
348                         if (control.isDisposed() || !control.isFocusControl())
349                             return;
350                         
351                         if (showStyle == SHOW_PROPOSALS) {
352                             if (!prepareToShowCompletions())
353                                 return;
354                             fProposalPopup.showProposals(true);
355                             fLastAutoActivation= System.currentTimeMillis();
356                         } else if (showStyle == SHOW_CONTEXT_INFO && fContextInfoPopup != null) {
357                             promoteKeyListener();
358                             fContextInfoPopup.showContextProposals(true);
359                         }
360                     }
361                 });
362             } catch (SWTError e) {
363             }
364         }
365     }
366
367     /**
368      * The layout manager layouts the various windows associated with the content assistant based on
369      * the settings of the content assistant.
370      */

371     class LayoutManager implements Listener {
372
373         // Presentation types.
374
/** The presentation type for the proposal selection popup. */
375         public final static int LAYOUT_PROPOSAL_SELECTOR= 0;
376         /** The presentation type for the context selection popup. */
377         public final static int LAYOUT_CONTEXT_SELECTOR= 1;
378         /** The presentation type for the context information hover . */
379         public final static int LAYOUT_CONTEXT_INFO_POPUP= 2;
380
381         int fContextType= LAYOUT_CONTEXT_SELECTOR;
382         Shell[] fShells= new Shell[3];
383         Object JavaDoc[] fPopups= new Object JavaDoc[3];
384
385         protected void add(Object JavaDoc popup, Shell shell, int type, int offset) {
386             Assert.isNotNull(popup);
387             Assert.isTrue(shell != null && !shell.isDisposed());
388             checkType(type);
389
390             if (fShells[type] != shell) {
391                 if (fShells[type] != null)
392                     fShells[type].removeListener(SWT.Dispose, this);
393                 shell.addListener(SWT.Dispose, this);
394                 fShells[type]= shell;
395             }
396
397             fPopups[type]= popup;
398             if (type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP)
399                 fContextType= type;
400
401             layout(type, offset);
402             adjustListeners(type);
403         }
404
405         protected void checkType(int type) {
406             Assert.isTrue(type == LAYOUT_PROPOSAL_SELECTOR ||
407                 type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP);
408         }
409
410         public void handleEvent(Event event) {
411             Widget source= event.widget;
412             source.removeListener(SWT.Dispose, this);
413
414             int type= getShellType(source);
415             checkType(type);
416             fShells[type]= null;
417
418             switch (type) {
419                 case LAYOUT_PROPOSAL_SELECTOR:
420                     if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
421                             Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
422                         // Restore event notification to the tip popup.
423
addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
424                     }
425                     break;
426
427                 case LAYOUT_CONTEXT_SELECTOR:
428                     if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
429                         if (fProposalPopupOrientation == PROPOSAL_STACKED)
430                             layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
431                         // Restore event notification to the proposal popup.
432
addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
433                     }
434                     fContextType= LAYOUT_CONTEXT_INFO_POPUP;
435                     break;
436
437                 case LAYOUT_CONTEXT_INFO_POPUP:
438                     if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
439                         if (fContextInfoPopupOrientation == CONTEXT_INFO_BELOW)
440                             layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
441                     }
442                     fContextType= LAYOUT_CONTEXT_SELECTOR;
443                     break;
444             }
445         }
446
447         protected int getShellType(Widget shell) {
448             for (int i= 0; i < fShells.length; i++) {
449                 if (fShells[i] == shell)
450                     return i;
451             }
452             return -1;
453         }
454
455         /**
456          * Layouts the popup defined by <code>type</code> at the given widget offset.
457          *
458          * @param type the kind of popup to layout
459          * @param offset the widget offset
460          */

461         protected void layout(int type, int offset) {
462             switch (type) {
463                 case LAYOUT_PROPOSAL_SELECTOR:
464                     layoutProposalSelector(offset);
465                     break;
466                 case LAYOUT_CONTEXT_SELECTOR:
467                     layoutContextSelector(offset);
468                     break;
469                 case LAYOUT_CONTEXT_INFO_POPUP:
470                     layoutContextInfoPopup(offset);
471                     break;
472             }
473         }
474
475         protected void layoutProposalSelector(int offset) {
476             if (fContextType == LAYOUT_CONTEXT_INFO_POPUP &&
477                     fContextInfoPopupOrientation == CONTEXT_INFO_BELOW &&
478                     Helper.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
479                 // Stack proposal selector beneath the tip box.
480
Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
481                 Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
482                 shell.setLocation(getStackedLocation(shell, parent));
483             } else if (fContextType != LAYOUT_CONTEXT_SELECTOR ||
484                         !Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
485                 // There are no other presentations to be concerned with,
486
// so place the proposal selector beneath the cursor line.
487
Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
488                 CompletionProposalPopup popup= (CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR];
489                 shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
490             } else {
491                 CompletionProposalPopup popup= ((CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR]);
492                 switch (fProposalPopupOrientation) {
493                     case PROPOSAL_REMOVE: {
494                         // Remove the tip selector and place the
495
// proposal selector beneath the cursor line.
496
fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
497                         Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
498                         shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
499                         break;
500                     }
501                     case PROPOSAL_OVERLAY: {
502                         // Overlay the tip selector with the proposal selector.
503
Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
504                         shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
505                         break;
506                     }
507                     case PROPOSAL_STACKED: {
508                         // Stack the proposal selector beneath the tip selector.
509
Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
510                         Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
511                         shell.setLocation(getStackedLocation(shell, parent));
512                         break;
513                     }
514                 }
515             }
516         }
517
518         protected void layoutContextSelector(int offset) {
519             // Always place the context selector beneath the cursor line.
520
Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
521             shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, null));
522
523             if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
524                 switch (fProposalPopupOrientation) {
525                     case PROPOSAL_REMOVE:
526                         // Remove the proposal selector.
527
fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
528                         break;
529
530                     case PROPOSAL_OVERLAY:
531                         // The proposal selector has been overlaid by the tip selector.
532
break;
533
534                     case PROPOSAL_STACKED: {
535                         // Stack the proposal selector beneath the tip selector.
536
shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
537                         Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
538                         shell.setLocation(getStackedLocation(shell, parent));
539                         break;
540                     }
541                 }
542             }
543         }
544
545         protected void layoutContextInfoPopup(int offset) {
546             switch (fContextInfoPopupOrientation) {
547                 case CONTEXT_INFO_ABOVE: {
548                     // Place the popup above the cursor line.
549
Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
550                     shell.setBounds(computeBoundsAboveBelow(shell, shell.getSize(), offset));
551                     break;
552                 }
553                 case CONTEXT_INFO_BELOW: {
554                     // Place the popup beneath the cursor line.
555
Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
556                     parent.setBounds(computeBoundsBelowAbove(parent, parent.getSize(), offset, null));
557                     if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
558                         // Stack the proposal selector beneath the context info popup.
559
Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
560                         shell.setLocation(getStackedLocation(shell, parent));
561                     }
562                     break;
563                 }
564             }
565         }
566
567         /**
568          * Moves <code>point</code> such that <code>rectangle</code> does not bleed outside of
569          * <code>bounds</code>. All coordinates must have the same reference.
570          *
571          * @param point the point to move if needed
572          * @param shellSize the size of the shell that may be moved
573          * @param bounds the bounds
574          * @since 3.3
575          */

576         protected void constrainLocation(Point point, Point shellSize, Rectangle bounds) {
577             if (point.x + shellSize.x > bounds.x + bounds.width)
578                 point.x= bounds.x + bounds.width - shellSize.x;
579
580             if (point.x < bounds.x)
581                 point.x= bounds.x;
582             
583             if (point.y + shellSize.y > bounds.y + bounds.height)
584                 point.y= bounds.y + bounds.height - shellSize.y;
585             
586             if (point.y < bounds.y)
587                 point.y= bounds.y;
588         }
589
590         protected Rectangle constrainHorizontally(Rectangle rect, Rectangle bounds) {
591             // clip width
592
if (rect.width > bounds.width)
593                 rect.width= bounds.width;
594             
595             if (rect.x + rect.width > bounds.x + bounds.width)
596                 rect.x= bounds.x + bounds.width - rect.width;
597             if (rect.x < bounds.x)
598                 rect.x= bounds.x;
599             
600             return rect;
601         }
602         
603         /**
604          * Returns the display bounds for <code>shell</code> such that it appears right above
605          * <code>offset</code>, or below it if above is not suitable. The returned bounds lie
606          * within the monitor at the caret location and never overlap with the caret line.
607          *
608          * @param shell the shell to compute the placement for
609          * @param preferred the preferred size for <code>shell</code>
610          * @param offset the caret offset in the subject control
611          * @return the point right above <code>offset</code> in display coordinates
612          * @since 3.3
613          */

614         protected Rectangle computeBoundsAboveBelow(Shell shell, Point preferred, int offset) {
615             Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
616             Display display= subjectControl.getDisplay();
617             Rectangle caret= getCaretRectangle(offset);
618             Monitor monitor= getClosestMonitor(display, caret);
619             Rectangle bounds= monitor.getClientArea();
620             Geometry.moveInside(caret, bounds);
621             
622             int spaceAbove= caret.y - bounds.y;
623             int caretLowerY= caret.y + caret.height;
624             int spaceBelow= bounds.y + bounds.height - caretLowerY;
625             Rectangle rect;
626             if (spaceAbove >= preferred.y)
627                 rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
628             else if (spaceBelow >= preferred.y)
629                 rect= new Rectangle(caret.x, caretLowerY, preferred.x, preferred.y);
630             // we can't fit in the preferred size - squeeze into larger area
631
else if (spaceBelow <= spaceAbove)
632                 rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
633             else
634                 rect= new Rectangle(caret.x, caretLowerY, preferred.x, spaceBelow);
635             
636             return constrainHorizontally(rect, bounds);
637         }
638
639         /**
640          * Returns the display bounds for <code>shell</code> such that it appears right below
641          * <code>offset</code>, or above it if below is not suitable. The returned bounds lie
642          * within the monitor at the caret location and never overlap with the caret line.
643          *
644          * @param shell the shell to compute the placement for
645          * @param preferred the preferred size for <code>shell</code>
646          * @param offset the caret offset in the subject control
647          * @param popup a popup to inform if the location was switched to above, <code>null</code> to do nothing
648          * @return the point right below <code>offset</code> in display coordinates
649          * @since 3.3
650          */

651         protected Rectangle computeBoundsBelowAbove(Shell shell, Point preferred, int offset, CompletionProposalPopup popup) {
652             Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
653             Display display= subjectControl.getDisplay();
654             Rectangle caret= getCaretRectangle(offset);
655             Monitor monitor= getClosestMonitor(display, caret);
656             Rectangle bounds= monitor.getClientArea();
657             Geometry.moveInside(caret, bounds);
658
659             int threshold= popup == null ? Integer.MAX_VALUE : popup.getMinimalHeight();
660             int spaceAbove= caret.y - bounds.y;
661             int spaceBelow= bounds.y + bounds.height - (caret.y + caret.height);
662             Rectangle rect;
663             boolean switched= false;
664             if (spaceBelow >= preferred.y)
665                 rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, preferred.y);
666             // squeeze in below if we have at least threshold space
667
else if (spaceBelow >= threshold)
668                 rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
669             else if (spaceAbove >= preferred.y) {
670                 rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
671                 switched= true;
672             } else if (spaceBelow >= spaceAbove) {
673                 // we can't fit in the preferred size - squeeze into larger area
674
rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
675             } else {
676                 rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
677                 switched= true;
678             }
679             
680             if (popup != null)
681                 popup.switchedPositionToAbove(switched);
682             
683             return constrainHorizontally(rect, bounds);
684         }
685
686         private Rectangle getCaretRectangle(int offset) {
687             Point location= fContentAssistSubjectControlAdapter.getLocationAtOffset(offset);
688             Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
689             Point controlSize= subjectControl.getSize();
690             constrainLocation(location, new Point(0, 0), new Rectangle(0, 0, controlSize.x, controlSize.y));
691             location= subjectControl.toDisplay(location);
692             Rectangle subjectRectangle= new Rectangle(location.x, location.y, 1, fContentAssistSubjectControlAdapter.getLineHeight());
693             return subjectRectangle;
694         }
695         
696         protected Point getStackedLocation(Shell shell, Shell parent) {
697             Point p= parent.getLocation();
698             Point size= parent.getSize();
699             p.x += size.x / 4;
700             p.y += size.y;
701
702             p= parent.toDisplay(p);
703
704             Point shellSize= shell.getSize();
705             Monitor monitor= getClosestMonitor(parent.getDisplay(), new Rectangle(p.x, p.y, 0, 0));
706             Rectangle displayBounds= monitor.getClientArea();
707             constrainLocation(p, shellSize, displayBounds);
708
709             return p;
710         }
711
712         protected void adjustListeners(int type) {
713             switch (type) {
714                 case LAYOUT_PROPOSAL_SELECTOR:
715                     if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
716                             Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
717                         // Disable event notification to the tip selector.
718
removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
719                     break;
720                 case LAYOUT_CONTEXT_SELECTOR:
721                     if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
722                         // Disable event notification to the proposal selector.
723
removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
724                     break;
725                 case LAYOUT_CONTEXT_INFO_POPUP:
726                     break;
727             }
728         }
729         
730         /**
731          * Copied from org.eclipse.jface.window.Window.
732          * Returns the monitor whose client area contains the given point. If no
733          * monitor contains the point, returns the monitor that is closest to the
734          * point. If this is ever made public, it should be moved into a separate
735          * utility class.
736          *
737          * @param toSearch
738          * point to find (display coordinates)
739          * @param rectangle
740          * rectangle to find (display coordinates)
741          * @return the montor closest to the given point
742          * @since 3.3
743          */

744         private Monitor getClosestMonitor(Display toSearch, Rectangle rectangle) {
745             int closest = Integer.MAX_VALUE;
746
747             Point toFind= Geometry.centerPoint(rectangle);
748             Monitor[] monitors = toSearch.getMonitors();
749             Monitor result = monitors[0];
750
751             for (int idx = 0; idx < monitors.length; idx++) {
752                 Monitor current = monitors[idx];
753
754                 Rectangle clientArea = current.getClientArea();
755
756                 if (clientArea.contains(toFind)) {
757                     return current;
758                 }
759
760                 int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
761                 if (distance < closest) {
762                     closest = distance;
763                     result = current;
764                 }
765             }
766
767             return result;
768         }
769     }
770
771     /**
772      * Internal key listener and event consumer.
773      */

774     class InternalListener implements VerifyKeyListener, IEventConsumer {
775
776         /**
777          * Verifies key events by notifying the registered listeners. Each listener is allowed to
778          * indicate that the event has been handled and should not be further processed.
779          *
780          * @param e the verify event
781          * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
782          */

783         public void verifyKey(VerifyEvent e) {
784             IContentAssistListener[] listeners= (IContentAssistListener[]) fListeners.clone();
785             for (int i= 0; i < listeners.length; i++) {
786                 if (listeners[i] != null) {
787                     if (!listeners[i].verifyKey(e) || !e.doit)
788                         break;
789                 }
790             }
791             if (fAutoAssistListener != null)
792                 fAutoAssistListener.keyPressed(e);
793         }
794
795         /*
796          * @see IEventConsumer#processEvent
797          */

798         public void processEvent(VerifyEvent event) {
799
800             installKeyListener();
801
802             IContentAssistListener[] listeners= (IContentAssistListener[]) fListeners.clone();
803             for (int i= 0; i < listeners.length; i++) {
804                 if (listeners[i] != null) {
805                     listeners[i].processEvent(event);
806                     if (!event.doit)
807                         return;
808                 }
809             }
810         }
811     }
812
813     /**
814      * Dialog store constants.
815      *
816      * @since 3.0
817      */

818     public static final String JavaDoc STORE_SIZE_X= "size.x"; //$NON-NLS-1$
819
public static final String JavaDoc STORE_SIZE_Y= "size.y"; //$NON-NLS-1$
820

821     // Content-Assist Listener types
822
final static int CONTEXT_SELECTOR= 0;
823     final static int PROPOSAL_SELECTOR= 1;
824     final static int CONTEXT_INFO_POPUP= 2;
825
826     /**
827      * The popup priority: &gt; linked position proposals and hover pop-ups. Default value:
828      * <code>20</code>;
829      *
830      * @since 3.0
831      */

832     public static final int WIDGET_PRIORITY= 20;
833
834     private static final int DEFAULT_AUTO_ACTIVATION_DELAY= 500;
835
836     private IInformationControlCreator fInformationControlCreator;
837     private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY;
838     private boolean fIsAutoActivated= false;
839     private boolean fIsAutoInserting= false;
840     private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
841     private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
842     private Map JavaDoc fProcessors;
843
844     /**
845      * The partitioning.
846      *
847      * @since 3.0
848      */

849     private String JavaDoc fPartitioning;
850
851     private Color fContextInfoPopupBackground;
852     private Color fContextInfoPopupForeground;
853     private Color fContextSelectorBackground;
854     private Color fContextSelectorForeground;
855     private Color fProposalSelectorBackground;
856     private Color fProposalSelectorForeground;
857
858     private ITextViewer fViewer;
859     private String JavaDoc fLastErrorMessage;
860
861     private Closer fCloser;
862     LayoutManager fLayoutManager;
863     private AutoAssistListener fAutoAssistListener;
864     private InternalListener fInternalListener;
865     private CompletionProposalPopup fProposalPopup;
866     private ContextInformationPopup fContextInfoPopup;
867
868     /**
869      * Flag which tells whether a verify key listener is hooked.
870      *
871      * @since 3.0
872      */

873     private boolean fVerifyKeyListenerHooked= false;
874     private IContentAssistListener[] fListeners= new IContentAssistListener[4];
875     /**
876      * The content assist subject control.
877      *
878      * @since 3.0
879      */

880     private IContentAssistSubjectControl fContentAssistSubjectControl;
881     /**
882      * The content assist subject control's shell.
883      *
884      * @since 3.2
885      */

886     private Shell fContentAssistSubjectControlShell;
887     /**
888      * The content assist subject control's shell traverse listener.
889      *
890      * @since 3.2
891      */

892     private TraverseListener fCASCSTraverseListener;
893     /**
894      * The content assist subject control adapter.
895      *
896      * @since 3.0
897      */

898     private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
899     /**
900      * The dialog settings for the control's bounds.
901      *
902      * @since 3.0
903      */

904     private IDialogSettings fDialogSettings;
905     /**
906      * Prefix completion setting.
907      *
908      * @since 3.0
909      */

910     private boolean fIsPrefixCompletionEnabled= false;
911     /**
912      * The list of completion listeners.
913      *
914      * @since 3.2
915      */

916     private List JavaDoc fCompletionListeners= new ArrayList JavaDoc();
917     /**
918      * The message to display at the bottom of the proposal popup.
919      *
920      * @since 3.2
921      */

922     private String JavaDoc fMessage= ""; //$NON-NLS-1$
923
/**
924      * The cycling mode property.
925      *
926      * @since 3.2
927      */

928     private boolean fIsRepetitionMode= false;
929     /**
930      * The show empty property.
931      *
932      * @since 3.2
933      */

934     private boolean fShowEmptyList= false;
935     /**
936      * The message line property.
937      *
938      * @since 3.2
939      */

940     private boolean fIsStatusLineVisible;
941     /**
942      * The last system time when auto activation performed.
943      * @since 3.2
944      */

945     private long fLastAutoActivation= Long.MIN_VALUE;
946     /**
947      * The iteration key sequence to listen for, or <code>null</code>.
948      *
949      * @since 3.2
950      */

951     private KeySequence fTriggerSequence;
952
953
954     /**
955      * Creates a new content assistant. The content assistant is not automatically activated,
956      * overlays the completion proposals with context information list if necessary, and shows the
957      * context information above the location at which it was activated. If auto activation will be
958      * enabled, without further configuration steps, this content assistant is activated after a 500
959      * milliseconds delay. It uses the default partitioning.
960      */

961     public ContentAssistant() {
962         fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
963     }
964
965     /**
966      * Sets the document partitioning this content assistant is using.
967      *
968      * @param partitioning the document partitioning for this content assistant
969      * @since 3.0
970      */

971     public void setDocumentPartitioning(String JavaDoc partitioning) {
972         Assert.isNotNull(partitioning);
973         fPartitioning= partitioning;
974     }
975
976     /*
977      * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#getDocumentPartitioning()
978      * @since 3.0
979      */

980     public String JavaDoc getDocumentPartitioning() {
981         return fPartitioning;
982     }
983
984     /**
985      * Registers a given content assist processor for a particular content type. If there is already
986      * a processor registered for this type, the new processor is registered instead of the old one.
987      *
988      * @param processor the content assist processor to register, or <code>null</code> to remove
989      * an existing one
990      * @param contentType the content type under which to register
991      */

992     public void setContentAssistProcessor(IContentAssistProcessor processor, String JavaDoc contentType) {
993
994         Assert.isNotNull(contentType);
995
996         if (fProcessors == null)
997             fProcessors= new HashMap JavaDoc();
998
999         if (processor == null)
1000            fProcessors.remove(contentType);
1001        else
1002            fProcessors.put(contentType, processor);
1003    }
1004
1005    /*
1006     * @see IContentAssistant#getContentAssistProcessor
1007     */

1008    public IContentAssistProcessor getContentAssistProcessor(String JavaDoc contentType) {
1009        if (fProcessors == null)
1010            return null;
1011
1012        return (IContentAssistProcessor) fProcessors.get(contentType);
1013    }
1014
1015    /**
1016     * Computes the sorted set of all auto activation trigger characters.
1017     *
1018     * @return the sorted set of all auto activation trigger characters
1019     * @since 3.1
1020     */

1021    private String JavaDoc computeAllAutoActivationTriggers() {
1022        if (fProcessors == null)
1023            return ""; //$NON-NLS-1$
1024

1025        StringBuffer JavaDoc buf= new StringBuffer JavaDoc(5);
1026        Iterator JavaDoc iter= fProcessors.entrySet().iterator();
1027        while (iter.hasNext()) {
1028            Entry entry= (Entry) iter.next();
1029            IContentAssistProcessor processor= (IContentAssistProcessor) entry.getValue();
1030            char[] triggers= processor.getCompletionProposalAutoActivationCharacters();
1031            if (triggers != null)
1032                buf.append(triggers);
1033            triggers= processor.getContextInformationAutoActivationCharacters();
1034            if (triggers != null)
1035                buf.append(triggers);
1036        }
1037        return buf.toString();
1038    }
1039
1040    /**
1041     * Enables the content assistant's auto activation mode.
1042     *
1043     * @param enabled indicates whether auto activation is enabled or not
1044     */

1045    public void enableAutoActivation(boolean enabled) {
1046        fIsAutoActivated= enabled;
1047        manageAutoActivation(fIsAutoActivated);
1048    }
1049
1050    /**
1051     * Enables the content assistant's auto insertion mode. If enabled, the content assistant
1052     * inserts a proposal automatically if it is the only proposal. In the case of ambiguities, the
1053     * user must make the choice.
1054     *
1055     * @param enabled indicates whether auto insertion is enabled or not
1056     * @since 2.0
1057     */

1058    public void enableAutoInsert(boolean enabled) {
1059        fIsAutoInserting= enabled;
1060    }
1061
1062    /**
1063     * Returns whether this content assistant is in the auto insertion mode or not.
1064     *
1065     * @return <code>true</code> if in auto insertion mode
1066     * @since 2.0
1067     */

1068    boolean isAutoInserting() {
1069        return fIsAutoInserting;
1070    }
1071
1072    /**
1073     * Installs and uninstall the listeners needed for auto-activation.
1074     *
1075     * @param start <code>true</code> if listeners must be installed, <code>false</code> if they
1076     * must be removed
1077     * @since 2.0
1078     */

1079    private void manageAutoActivation(boolean start) {
1080        if (start) {
1081
1082            if ((fContentAssistSubjectControlAdapter != null) && fAutoAssistListener == null) {
1083                fAutoAssistListener= new AutoAssistListener();
1084                // For details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
1085
if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
1086                    fContentAssistSubjectControlAdapter.appendVerifyKeyListener(fAutoAssistListener);
1087                else
1088                    fContentAssistSubjectControlAdapter.addKeyListener(fAutoAssistListener);
1089            }
1090
1091        } else if (fAutoAssistListener != null) {
1092            // For details see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
1093
if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
1094                fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fAutoAssistListener);
1095            else
1096                fContentAssistSubjectControlAdapter.removeKeyListener(fAutoAssistListener);
1097            fAutoAssistListener= null;
1098        }
1099    }
1100
1101    /**
1102     * Sets the delay after which the content assistant is automatically invoked if the cursor is
1103     * behind an auto activation character.
1104     *
1105     * @param delay the auto activation delay
1106     */

1107    public void setAutoActivationDelay(int delay) {
1108        fAutoActivationDelay= delay;
1109    }
1110
1111    /**
1112     * Sets the proposal pop-ups' orientation. The following values may be used:
1113     * <ul>
1114     * <li>PROPOSAL_OVERLAY<p>
1115     * proposal popup windows should overlay each other
1116     * </li>
1117     * <li>PROPOSAL_REMOVE<p>
1118     * any currently shown proposal popup should be closed
1119     * </li>
1120     * <li>PROPOSAL_STACKED<p>
1121     * proposal popup windows should be vertical stacked, with no overlap,
1122     * beneath the line containing the current cursor location
1123     * </li>
1124     * </ul>
1125     *
1126     * @param orientation the popup's orientation
1127     */

1128    public void setProposalPopupOrientation(int orientation) {
1129        fProposalPopupOrientation= orientation;
1130    }
1131
1132    /**
1133     * Sets the context information popup's orientation.
1134     * The following values may be used:
1135     * <ul>
1136     * <li>CONTEXT_ABOVE<p>
1137     * context information popup should always appear above the line containing
1138     * the current cursor location
1139     * </li>
1140     * <li>CONTEXT_BELOW<p>
1141     * context information popup should always appear below the line containing
1142     * the current cursor location
1143     * </li>
1144     * </ul>
1145     *
1146     * @param orientation the popup's orientation
1147     */

1148    public void setContextInformationPopupOrientation(int orientation) {
1149        fContextInfoPopupOrientation= orientation;
1150    }
1151
1152    /**
1153     * Sets the context information popup's background color.
1154     *
1155     * @param background the background color
1156     */

1157    public void setContextInformationPopupBackground(Color background) {
1158        fContextInfoPopupBackground= background;
1159    }
1160
1161    /**
1162     * Returns the background of the context information popup.
1163     *
1164     * @return the background of the context information popup
1165     * @since 2.0
1166     */

1167    Color getContextInformationPopupBackground() {
1168        return fContextInfoPopupBackground;
1169    }
1170
1171    /**
1172     * Sets the context information popup's foreground color.
1173     *
1174     * @param foreground the foreground color
1175     * @since 2.0
1176     */

1177    public void setContextInformationPopupForeground(Color foreground) {
1178        fContextInfoPopupForeground= foreground;
1179    }
1180
1181    /**
1182     * Returns the foreground of the context information popup.
1183     *
1184     * @return the foreground of the context information popup
1185     * @since 2.0
1186     */

1187    Color getContextInformationPopupForeground() {
1188        return fContextInfoPopupForeground;
1189    }
1190
1191    /**
1192     * Sets the proposal selector's background color.
1193     *
1194     * @param background the background color
1195     * @since 2.0
1196     */

1197    public void setProposalSelectorBackground(Color background) {
1198        fProposalSelectorBackground= background;
1199    }
1200
1201    /**
1202     * Returns the background of the proposal selector.
1203     *
1204     * @return the background of the proposal selector
1205     * @since 2.0
1206     */

1207    Color getProposalSelectorBackground() {
1208        return fProposalSelectorBackground;
1209    }
1210
1211    /**
1212     * Sets the proposal's foreground color.
1213     *
1214     * @param foreground the foreground color
1215     * @since 2.0
1216     */

1217    public void setProposalSelectorForeground(Color foreground) {
1218        fProposalSelectorForeground= foreground;
1219    }
1220
1221    /**
1222     * Returns the foreground of the proposal selector.
1223     *
1224     * @return the foreground of the proposal selector
1225     * @since 2.0
1226     */

1227    Color getProposalSelectorForeground() {
1228        return fProposalSelectorForeground;
1229    }
1230
1231    /**
1232     * Sets the context selector's background color.
1233     *
1234     * @param background the background color
1235     * @since 2.0
1236     */

1237    public void setContextSelectorBackground(Color background) {
1238        fContextSelectorBackground= background;
1239    }
1240
1241    /**
1242     * Returns the background of the context selector.
1243     *
1244     * @return the background of the context selector
1245     * @since 2.0
1246     */

1247    Color getContextSelectorBackground() {
1248        return fContextSelectorBackground;
1249    }
1250
1251    /**
1252     * Sets the context selector's foreground color.
1253     *
1254     * @param foreground the foreground color
1255     * @since 2.0
1256     */

1257    public void setContextSelectorForeground(Color foreground) {
1258        fContextSelectorForeground= foreground;
1259    }
1260
1261    /**
1262     * Returns the foreground of the context selector.
1263     *
1264     * @return the foreground of the context selector
1265     * @since 2.0
1266     */

1267    Color getContextSelectorForeground() {
1268        return fContextSelectorForeground;
1269    }
1270
1271    /**
1272     * Sets the information control creator for the additional information control.
1273     *
1274     * @param creator the information control creator for the additional information control
1275     * @since 2.0
1276     */

1277    public void setInformationControlCreator(IInformationControlCreator creator) {
1278        fInformationControlCreator= creator;
1279    }
1280
1281    /*
1282     * @see IControlContentAssistant#install(IContentAssistSubjectControl)
1283     * @since 3.0
1284     */

1285    protected void install(IContentAssistSubjectControl contentAssistSubjectControl) {
1286        fContentAssistSubjectControl= contentAssistSubjectControl;
1287        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
1288        install();
1289    }
1290
1291    /*
1292     * @see IContentAssist#install
1293     * @since 3.0
1294     */

1295    public void install(ITextViewer textViewer) {
1296        fViewer= textViewer;
1297        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
1298        install();
1299    }
1300
1301    protected void install() {
1302
1303        fLayoutManager= new LayoutManager();
1304        fInternalListener= new InternalListener();
1305
1306        AdditionalInfoController controller= null;
1307        if (fInformationControlCreator != null) {
1308            int delay= fAutoActivationDelay;
1309            if (delay == 0)
1310                delay= DEFAULT_AUTO_ACTIVATION_DELAY;
1311            delay= Math.round(delay * 1.5f);
1312            controller= new AdditionalInfoController(fInformationControlCreator, delay);
1313        }
1314
1315        fContextInfoPopup= fContentAssistSubjectControlAdapter.createContextInfoPopup(this);
1316        fProposalPopup= fContentAssistSubjectControlAdapter.createCompletionProposalPopup(this, controller);
1317
1318        if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
1319            fContentAssistSubjectControlShell= fContentAssistSubjectControlAdapter.getControl().getShell();
1320            fCASCSTraverseListener= new TraverseListener() {
1321                public void keyTraversed(TraverseEvent e) {
1322                    if (e.detail == SWT.TRAVERSE_ESCAPE && fProposalPopup != null && fProposalPopup.isActive())
1323                        e.doit= false;
1324                }
1325            };
1326            fContentAssistSubjectControlShell.addTraverseListener(fCASCSTraverseListener);
1327        }
1328
1329        manageAutoActivation(fIsAutoActivated);
1330    }
1331
1332    /*
1333     * @see IContentAssist#uninstall
1334     */

1335    public void uninstall() {
1336        hide();
1337        manageAutoActivation(false);
1338
1339        if (fCloser != null) {
1340            fCloser.uninstall();
1341            fCloser= null;
1342        }
1343
1344        if (Helper.okToUse(fContentAssistSubjectControlShell))
1345            fContentAssistSubjectControlShell.removeTraverseListener(fCASCSTraverseListener);
1346        fCASCSTraverseListener= null;
1347        fContentAssistSubjectControlShell= null;
1348
1349        fViewer= null;
1350        fContentAssistSubjectControl= null;
1351        fContentAssistSubjectControlAdapter= null;
1352    }
1353
1354    /**
1355     * Adds the given shell of the specified type to the layout. Valid types are defined by
1356     * <code>LayoutManager</code>.
1357     *
1358     * @param popup a content assist popup
1359     * @param shell the shell of the content-assist popup
1360     * @param type the type of popup
1361     * @param visibleOffset the offset at which to layout the popup relative to the offset of the
1362     * viewer's visible region
1363     * @since 2.0
1364     */

1365    void addToLayout(Object JavaDoc popup, Shell shell, int type, int visibleOffset) {
1366        fLayoutManager.add(popup, shell, type, visibleOffset);
1367    }
1368
1369    /**
1370     * Layouts the registered popup of the given type relative to the given offset. The offset is
1371     * relative to the offset of the viewer's visible region. Valid types are defined by
1372     * <code>LayoutManager</code>.
1373     *
1374     * @param type the type of popup to layout
1375     * @param visibleOffset the offset at which to layout relative to the offset of the viewer's
1376     * visible region
1377     * @since 2.0
1378     */

1379    void layout(int type, int visibleOffset) {
1380        fLayoutManager.layout(type, visibleOffset);
1381    }
1382    
1383    /**
1384     * Returns the layout manager.
1385     *
1386     * @return the layout manager
1387     * @since 3.3
1388     */

1389    LayoutManager getLayoutManager() {
1390        return fLayoutManager;
1391    }
1392
1393    /**
1394     * Notifies the controller that a popup has lost focus.
1395     *
1396     * @param e the focus event
1397     */

1398    void popupFocusLost(FocusEvent e) {
1399        fCloser.focusLost(e);
1400    }
1401
1402    /**
1403     * Returns the offset of the selection relative to the offset of the visible region.
1404     *
1405     * @return the offset of the selection relative to the offset of the visible region
1406     * @since 2.0
1407     */

1408    int getSelectionOffset() {
1409        return fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
1410    }
1411
1412    /**
1413     * Returns whether the widget token could be acquired. The following are valid listener types:
1414     * <ul>
1415     * <li>AUTO_ASSIST</li>
1416     * <li>CONTEXT_SELECTOR</li>
1417     * <li>PROPOSAL_SELECTOR</li>
1418     * <li>CONTEXT_INFO_POPUP</li>
1419     * </ul>
1420     *
1421     * @param type the listener type for which to acquire
1422     * @return <code>true</code> if the widget token could be acquired
1423     * @since 2.0
1424     */

1425    private boolean acquireWidgetToken(int type) {
1426        switch (type) {
1427            case CONTEXT_SELECTOR:
1428            case PROPOSAL_SELECTOR:
1429                if (fContentAssistSubjectControl instanceof IWidgetTokenOwnerExtension) {
1430                    IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fContentAssistSubjectControl;
1431                    return extension.requestWidgetToken(this, WIDGET_PRIORITY);
1432                } else if (fContentAssistSubjectControl instanceof IWidgetTokenOwner) {
1433                    IWidgetTokenOwner owner= (IWidgetTokenOwner) fContentAssistSubjectControl;
1434                    return owner.requestWidgetToken(this);
1435                } else if (fViewer instanceof IWidgetTokenOwnerExtension) {
1436                    IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fViewer;
1437                    return extension.requestWidgetToken(this, WIDGET_PRIORITY);
1438                } else if (fViewer instanceof IWidgetTokenOwner) {
1439                    IWidgetTokenOwner owner= (IWidgetTokenOwner) fViewer;
1440                    return owner.requestWidgetToken(this);
1441                }
1442        }
1443        return true;
1444    }
1445
1446    /**
1447     * Registers a content assist listener. The following are valid listener types:
1448     * <ul>
1449     * <li>AUTO_ASSIST</li>
1450     * <li>CONTEXT_SELECTOR</li>
1451     * <li>PROPOSAL_SELECTOR</li>
1452     * <li>CONTEXT_INFO_POPUP</li>
1453     * </ul>
1454     * Returns whether the listener could be added successfully. A listener can not be added if the
1455     * widget token could not be acquired.
1456     *
1457     * @param listener the listener to register
1458     * @param type the type of listener
1459     * @return <code>true</code> if the listener could be added
1460     */

1461    boolean addContentAssistListener(IContentAssistListener listener, int type) {
1462
1463        if (acquireWidgetToken(type)) {
1464
1465            fListeners[type]= listener;
1466
1467            if (fCloser == null && getNumberOfListeners() == 1) {
1468                fCloser= new Closer();
1469                fCloser.install();
1470                fContentAssistSubjectControlAdapter.setEventConsumer(fInternalListener);
1471                installKeyListener();
1472            } else
1473                promoteKeyListener();
1474            return true;
1475        }
1476
1477        return false;
1478    }
1479
1480    /**
1481     * Re-promotes the key listener to the first position, using prependVerifyKeyListener. This
1482     * ensures no other instance is filtering away the keystrokes underneath, if we've been up for a
1483     * while (e.g. when the context info is showing.
1484     *
1485     * @since 3.0
1486     */

1487    private void promoteKeyListener() {
1488        uninstallVerifyKeyListener();
1489        installKeyListener();
1490    }
1491
1492    /**
1493     * Installs a key listener on the text viewer's widget.
1494     */

1495    private void installKeyListener() {
1496        if (!fVerifyKeyListenerHooked) {
1497            if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
1498                fVerifyKeyListenerHooked= fContentAssistSubjectControlAdapter.prependVerifyKeyListener(fInternalListener);
1499            }
1500        }
1501    }
1502
1503    /**
1504     * Releases the previously acquired widget token if the token is no longer necessary. The
1505     * following are valid listener types:
1506     * <ul>
1507     * <li>AUTO_ASSIST</li>
1508     * <li>CONTEXT_SELECTOR</li>
1509     * <li>PROPOSAL_SELECTOR</li>
1510     * <li>CONTEXT_INFO_POPUP</li>
1511     * </ul>
1512     *
1513     * @param type the listener type
1514     * @since 2.0
1515     */

1516    private void releaseWidgetToken(int type) {
1517        if (fListeners[CONTEXT_SELECTOR] == null && fListeners[PROPOSAL_SELECTOR] == null) {
1518            IWidgetTokenOwner owner= null;
1519            if (fContentAssistSubjectControl instanceof IWidgetTokenOwner)
1520                owner= (IWidgetTokenOwner) fContentAssistSubjectControl;
1521            else if (fViewer instanceof IWidgetTokenOwner)
1522                owner= (IWidgetTokenOwner) fViewer;
1523            if (owner != null)
1524                owner.releaseWidgetToken(this);
1525        }
1526    }
1527
1528    /**
1529     * Unregisters a content assist listener.
1530     *
1531     * @param listener the listener to unregister
1532     * @param type the type of listener
1533     * @see #addContentAssistListener(IContentAssistListener, int)
1534     */

1535    void removeContentAssistListener(IContentAssistListener listener, int type) {
1536        fListeners[type]= null;
1537
1538        if (getNumberOfListeners() == 0) {
1539
1540            if (fCloser != null) {
1541                fCloser.uninstall();
1542                fCloser= null;
1543            }
1544
1545            uninstallVerifyKeyListener();
1546            fContentAssistSubjectControlAdapter.setEventConsumer(null);
1547        }
1548
1549        releaseWidgetToken(type);
1550    }
1551
1552    /**
1553     * Uninstall the key listener from the text viewer's widget.
1554     *
1555     * @since 3.0
1556     */

1557    private void uninstallVerifyKeyListener() {
1558        if (fVerifyKeyListenerHooked) {
1559            if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl()))
1560                fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fInternalListener);
1561            fVerifyKeyListenerHooked= false;
1562        }
1563    }
1564
1565    /**
1566     * Returns the number of listeners.
1567     *
1568     * @return the number of listeners
1569     * @since 2.0
1570     */

1571    private int getNumberOfListeners() {
1572        int count= 0;
1573        for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) {
1574            if (fListeners[i] != null)
1575                ++count;
1576        }
1577        return count;
1578    }
1579
1580    /*
1581     * @see IContentAssist#showPossibleCompletions
1582     */

1583    public String JavaDoc showPossibleCompletions() {
1584        if (!prepareToShowCompletions())
1585            return null;
1586        if (fIsPrefixCompletionEnabled)
1587            return fProposalPopup.incrementalComplete();
1588        return fProposalPopup.showProposals(false);
1589    }
1590
1591    /*
1592     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#completePrefix()
1593     * @since 3.0
1594     */

1595    public String JavaDoc completePrefix() {
1596        if (!prepareToShowCompletions())
1597            return null;
1598        return fProposalPopup.incrementalComplete();
1599    }
1600
1601    /**
1602     * Prepares to show content assist proposals. It returns false if auto-activation has kicked in
1603     * recently.
1604     *
1605     * @return <code>true</code> if the caller should continue and show the proposals,
1606     * <code>false</code> otherwise.
1607     * @since 3.2
1608     */

1609    private boolean prepareToShowCompletions() {
1610        long current= System.currentTimeMillis();
1611        int gracePeriod= Math.max(fAutoActivationDelay, 200);
1612        if (current < fLastAutoActivation + gracePeriod)
1613            return false;
1614        
1615        promoteKeyListener();
1616        fireSessionBeginEvent();
1617        return true;
1618    }
1619
1620    /**
1621     * Callback to signal this content assistant that the presentation of the possible completions
1622     * has been stopped.
1623     *
1624     * @since 2.1
1625     */

1626    protected void possibleCompletionsClosed() {
1627        fLastAutoActivation= Long.MIN_VALUE;
1628        storeCompletionProposalPopupSize();
1629    }
1630
1631    /*
1632     * @see IContentAssist#showContextInformation
1633     */

1634    public String JavaDoc showContextInformation() {
1635        promoteKeyListener();
1636        if (fContextInfoPopup != null)
1637            return fContextInfoPopup.showContextProposals(false);
1638        return null;
1639    }
1640
1641    /**
1642     * Callback to signal this content assistant that the presentation of the context information
1643     * has been stopped.
1644     *
1645     * @since 2.1
1646     */

1647    protected void contextInformationClosed() {
1648    }
1649
1650    /**
1651     * Requests that the specified context information to be shown.
1652     *
1653     * @param contextInformation the context information to be shown
1654     * @param offset the offset to which the context information refers to
1655     * @since 2.0
1656     */

1657    void showContextInformation(IContextInformation contextInformation, int offset) {
1658        if (fContextInfoPopup != null)
1659            fContextInfoPopup.showContextInformation(contextInformation, offset);
1660    }
1661
1662    /**
1663     * Returns the current content assist error message.
1664     *
1665     * @return an error message or <code>null</code> if no error has occurred
1666     */

1667    String JavaDoc getErrorMessage() {
1668        return fLastErrorMessage;
1669    }
1670
1671    /**
1672     * Returns the content assist processor for the content type of the specified document position.
1673     *
1674     * @param viewer the text viewer
1675     * @param offset a offset within the document
1676     * @return a content-assist processor or <code>null</code> if none exists
1677     * @since 3.0
1678     */

1679    private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) {
1680        try {
1681
1682            IDocument document= viewer.getDocument();
1683            String JavaDoc type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);
1684
1685            return getContentAssistProcessor(type);
1686
1687        } catch (BadLocationException x) {
1688        }
1689
1690        return null;
1691    }
1692
1693    /**
1694     * Returns the content assist processor for the content type of the specified document position.
1695     *
1696     * @param contentAssistSubjectControl the content assist subject control
1697     * @param offset a offset within the document
1698     * @return a content-assist processor or <code>null</code> if none exists
1699     * @since 3.0
1700     */

1701    private IContentAssistProcessor getProcessor(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1702        try {
1703
1704            IDocument document= contentAssistSubjectControl.getDocument();
1705            String JavaDoc type;
1706            if (document != null)
1707                type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);
1708            else
1709                type= IDocument.DEFAULT_CONTENT_TYPE;
1710
1711            return getContentAssistProcessor(type);
1712
1713        } catch (BadLocationException x) {
1714        }
1715
1716        return null;
1717    }
1718
1719    /**
1720     * Returns an array of completion proposals computed based on the specified document position.
1721     * The position is used to determine the appropriate content assist processor to invoke.
1722     *
1723     * @param contentAssistSubjectControl the content assist subject control
1724     * @param offset a document offset
1725     * @return an array of completion proposals
1726     * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
1727     * @since 3.0
1728     */

1729    ICompletionProposal[] computeCompletionProposals(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1730        fLastErrorMessage= null;
1731
1732        ICompletionProposal[] result= null;
1733
1734        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1735        if (p instanceof ISubjectControlContentAssistProcessor) {
1736            result= ((ISubjectControlContentAssistProcessor) p).computeCompletionProposals(contentAssistSubjectControl, offset);
1737            fLastErrorMessage= p.getErrorMessage();
1738        }
1739
1740        return result;
1741    }
1742
1743    /**
1744     * Returns an array of completion proposals computed based on the specified document position.
1745     * The position is used to determine the appropriate content assist processor to invoke.
1746     *
1747     * @param viewer the viewer for which to compute the proposals
1748     * @param offset a document offset
1749     * @return an array of completion proposals or <code>null</code> if no proposals are possible
1750     * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
1751     */

1752    ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
1753        fLastErrorMessage= null;
1754
1755        ICompletionProposal[] result= null;
1756
1757        IContentAssistProcessor p= getProcessor(viewer, offset);
1758        if (p != null) {
1759            result= p.computeCompletionProposals(viewer, offset);
1760            fLastErrorMessage= p.getErrorMessage();
1761        }
1762
1763        return result;
1764    }
1765
1766    /**
1767     * Returns an array of context information objects computed based on the specified document
1768     * position. The position is used to determine the appropriate content assist processor to
1769     * invoke.
1770     *
1771     * @param viewer the viewer for which to compute the context information
1772     * @param offset a document offset
1773     * @return an array of context information objects
1774     * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
1775     */

1776    IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
1777        fLastErrorMessage= null;
1778
1779        IContextInformation[] result= null;
1780
1781        IContentAssistProcessor p= getProcessor(viewer, offset);
1782        if (p != null) {
1783            result= p.computeContextInformation(viewer, offset);
1784            fLastErrorMessage= p.getErrorMessage();
1785        }
1786
1787        return result;
1788    }
1789
1790    /**
1791     * Returns an array of context information objects computed based on the specified document
1792     * position. The position is used to determine the appropriate content assist processor to
1793     * invoke.
1794     *
1795     * @param contentAssistSubjectControl the content assist subject control
1796     * @param offset a document offset
1797     * @return an array of context information objects
1798     * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
1799     * @since 3.0
1800     */

1801    IContextInformation[] computeContextInformation(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1802        fLastErrorMessage= null;
1803
1804        IContextInformation[] result= null;
1805
1806        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1807        if (p instanceof ISubjectControlContentAssistProcessor) {
1808            result= ((ISubjectControlContentAssistProcessor) p).computeContextInformation(contentAssistSubjectControl, offset);
1809            fLastErrorMessage= p.getErrorMessage();
1810        }
1811
1812        return result;
1813    }
1814
1815    /**
1816     * Returns the context information validator that should be used to determine when the currently
1817     * displayed context information should be dismissed. The position is used to determine the
1818     * appropriate content assist processor to invoke.
1819     *
1820     * @param viewer the text viewer
1821     * @param offset a document offset
1822     * @return an validator
1823     * @see IContentAssistProcessor#getContextInformationValidator()
1824     * @since 3.0
1825     */

1826    IContextInformationValidator getContextInformationValidator(ITextViewer viewer, int offset) {
1827        IContentAssistProcessor p= getProcessor(viewer, offset);
1828        return p != null ? p.getContextInformationValidator() : null;
1829    }
1830
1831    /**
1832     * Returns the context information validator that should be used to determine when the currently
1833     * displayed context information should be dismissed. The position is used to determine the
1834     * appropriate content assist processor to invoke.
1835     *
1836     * @param contentAssistSubjectControl the content assist subject control
1837     * @param offset a document offset
1838     * @return an validator
1839     * @see IContentAssistProcessor#getContextInformationValidator()
1840     * @since 3.0
1841     */

1842    IContextInformationValidator getContextInformationValidator(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1843        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1844        return p != null ? p.getContextInformationValidator() : null;
1845    }
1846
1847    /**
1848     * Returns the context information presenter that should be used to display context information.
1849     * The position is used to determine the appropriate content assist processor to invoke.
1850     *
1851     * @param viewer the text viewer
1852     * @param offset a document offset
1853     * @return a presenter
1854     * @since 2.0
1855     */

1856    IContextInformationPresenter getContextInformationPresenter(ITextViewer viewer, int offset) {
1857        IContextInformationValidator validator= getContextInformationValidator(viewer, offset);
1858        if (validator instanceof IContextInformationPresenter)
1859            return (IContextInformationPresenter) validator;
1860        return null;
1861    }
1862
1863    /**
1864     * Returns the context information presenter that should be used to display context information.
1865     * The position is used to determine the appropriate content assist processor to invoke.
1866     *
1867     * @param contentAssistSubjectControl the content assist subject control
1868     * @param offset a document offset
1869     * @return a presenter
1870     * @since 3.0
1871     */

1872    IContextInformationPresenter getContextInformationPresenter(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1873        IContextInformationValidator validator= getContextInformationValidator(contentAssistSubjectControl, offset);
1874        if (validator instanceof IContextInformationPresenter)
1875            return (IContextInformationPresenter) validator;
1876        return null;
1877    }
1878
1879    /**
1880     * Returns the characters which when typed by the user should automatically initiate proposing
1881     * completions. The position is used to determine the appropriate content assist processor to
1882     * invoke.
1883     *
1884     * @param contentAssistSubjectControl the content assist subject control
1885     * @param offset a document offset
1886     * @return the auto activation characters
1887     * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
1888     * @since 3.0
1889     */

1890    char[] getCompletionProposalAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1891        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1892        return p != null ? p.getCompletionProposalAutoActivationCharacters() : null;
1893    }
1894
1895    /**
1896     * Returns the characters which when typed by the user should automatically initiate proposing
1897     * completions. The position is used to determine the appropriate content assist processor to
1898     * invoke.
1899     *
1900     * @param viewer the text viewer
1901     * @param offset a document offset
1902     * @return the auto activation characters
1903     * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
1904     */

1905    char[] getCompletionProposalAutoActivationCharacters(ITextViewer viewer, int offset) {
1906        IContentAssistProcessor p= getProcessor(viewer, offset);
1907        return p != null ? p.getCompletionProposalAutoActivationCharacters() : null;
1908    }
1909
1910    /**
1911     * Returns the characters which when typed by the user should automatically initiate the
1912     * presentation of context information. The position is used to determine the appropriate
1913     * content assist processor to invoke.
1914     *
1915     * @param viewer the text viewer
1916     * @param offset a document offset
1917     * @return the auto activation characters
1918     * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
1919     * @since 3.0
1920     */

1921    char[] getContextInformationAutoActivationCharacters(ITextViewer viewer, int offset) {
1922        IContentAssistProcessor p= getProcessor(viewer, offset);
1923        return p != null ? p.getContextInformationAutoActivationCharacters() : null;
1924    }
1925
1926    /**
1927     * Returns the characters which when typed by the user should automatically initiate the
1928     * presentation of context information. The position is used to determine the appropriate
1929     * content assist processor to invoke.
1930     *
1931     * @param contentAssistSubjectControl the content assist subject control
1932     * @param offset a document offset
1933     * @return the auto activation characters
1934     * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
1935     * @since 3.0
1936     */

1937    char[] getContextInformationAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
1938        IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
1939        return p != null ? p.getContextInformationAutoActivationCharacters() : null;
1940    }
1941
1942    /*
1943     * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
1944     * @since 2.0
1945     */

1946    public boolean requestWidgetToken(IWidgetTokenOwner owner) {
1947        return false;
1948    }
1949
1950    /*
1951     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner,
1952     * int)
1953     * @since 3.0
1954     */

1955    public boolean requestWidgetToken(IWidgetTokenOwner owner, int priority) {
1956        if (priority > WIDGET_PRIORITY) {
1957            hide();
1958            return true;
1959        }
1960        return false;
1961    }
1962
1963    /*
1964     * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
1965     * @since 3.0
1966     */

1967    public boolean setFocus(IWidgetTokenOwner owner) {
1968        if (fProposalPopup != null) {
1969            fProposalPopup.setFocus();
1970            return fProposalPopup.hasFocus();
1971        }
1972        return false;
1973    }
1974
1975    /**
1976     * Hides any open pop-ups.
1977     *
1978     * @since 3.0
1979     */

1980    protected void hide() {
1981        if (fProposalPopup != null)
1982            fProposalPopup.hide();
1983
1984        if (fContextInfoPopup != null)
1985            fContextInfoPopup.hide();
1986    }
1987
1988    // ------ control's size handling dialog settings ------
1989

1990    /**
1991     * Tells this information control manager to open the information control with the values
1992     * contained in the given dialog settings and to store the control's last valid size in the
1993     * given dialog settings.
1994     * <p>
1995     * Note: This API is only valid if the information control implements
1996     * {@link org.eclipse.jface.text.IInformationControlExtension3}. Not following this restriction
1997     * will later result in an {@link UnsupportedOperationException}.
1998     * </p>
1999     * <p>
2000     * The constants used to store the values are:
2001     * <ul>
2002     * <li>{@link ContentAssistant#STORE_SIZE_X}</li>
2003     * <li>{@link ContentAssistant#STORE_SIZE_Y}</li>
2004     * </ul>
2005     * </p>
2006     *
2007     * @param dialogSettings
2008     * @since 3.0
2009     */

2010    public void setRestoreCompletionProposalSize(IDialogSettings dialogSettings) {
2011        Assert.isTrue(dialogSettings != null);
2012        fDialogSettings= dialogSettings;
2013    }
2014
2015    /**
2016     * Stores the content assist pop-up's size.
2017     */

2018    protected void storeCompletionProposalPopupSize() {
2019        if (fDialogSettings == null || fProposalPopup == null)
2020            return;
2021
2022        Point size= fProposalPopup.getSize();
2023        if (size == null)
2024            return;
2025
2026        fDialogSettings.put(STORE_SIZE_X, size.x);
2027        fDialogSettings.put(STORE_SIZE_Y, size.y);
2028    }
2029
2030    /**
2031     * Restores the content assist pop-up's size.
2032     *
2033     * @return the stored size
2034     * @since 3.0
2035     */

2036    protected Point restoreCompletionProposalPopupSize() {
2037        if (fDialogSettings == null)
2038            return null;
2039
2040        Point size= new Point(-1, -1);
2041
2042        try {
2043            size.x= fDialogSettings.getInt(STORE_SIZE_X);
2044            size.y= fDialogSettings.getInt(STORE_SIZE_Y);
2045        } catch (NumberFormatException JavaDoc ex) {
2046            size.x= -1;
2047            size.y= -1;
2048        }
2049
2050        // sanity check
2051
if (size.x == -1 && size.y == -1)
2052            return null;
2053
2054        Rectangle maxBounds= null;
2055        if (fContentAssistSubjectControl != null && Helper.okToUse(fContentAssistSubjectControl.getControl()))
2056            maxBounds= fContentAssistSubjectControl.getControl().getDisplay().getBounds();
2057        else {
2058            // fallback
2059
Display display= Display.getCurrent();
2060            if (display == null)
2061                display= Display.getDefault();
2062            if (display != null && !display.isDisposed())
2063                maxBounds= display.getBounds();
2064        }
2065
2066        if (size.x > -1 && size.y > -1) {
2067            if (maxBounds != null) {
2068                size.x= Math.min(size.x, maxBounds.width);
2069                size.y= Math.min(size.y, maxBounds.height);
2070            }
2071
2072            // Enforce an absolute minimal size
2073
size.x= Math.max(size.x, 30);
2074            size.y= Math.max(size.y, 30);
2075        }
2076
2077        return size;
2078    }
2079
2080    /**
2081     * Sets the prefix completion property. If enabled, content assist delegates completion to
2082     * prefix completion.
2083     *
2084     * @param enabled <code>true</code> to enable prefix completion, <code>false</code> to
2085     * disable
2086     */

2087    public void enablePrefixCompletion(boolean enabled) {
2088        fIsPrefixCompletionEnabled= enabled;
2089    }
2090    
2091    /**
2092     * Returns the prefix completion state.
2093     *
2094     * @return <code>true</code> if prefix completion is enabled, <code>false</code> otherwise
2095     * @since 3.2
2096     */

2097    boolean isPrefixCompletionEnabled() {
2098        return fIsPrefixCompletionEnabled;
2099    }
2100
2101    /**
2102     * Returns whether the content assistant proposal popup has the focus.
2103     *
2104     * @return <code>true</code> if the proposal popup has the focus
2105     * @since 3.0
2106     */

2107    public boolean hasProposalPopupFocus() {
2108        return fProposalPopup.hasFocus();
2109    }
2110
2111    /*
2112     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#addCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
2113     * @since 3.2
2114     */

2115    public void addCompletionListener(ICompletionListener listener) {
2116        Assert.isLegal(listener != null);
2117        fCompletionListeners.add(listener);
2118    }
2119
2120    /*
2121     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#removeCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
2122     * @since 3.2
2123     */

2124    public void removeCompletionListener(ICompletionListener listener) {
2125        fCompletionListeners.remove(listener);
2126    }
2127
2128    /**
2129     * Fires a session begin event to all registered {@link ICompletionListener}s.
2130     *
2131     * @since 3.2
2132     */

2133    void fireSessionBeginEvent() {
2134        if (fContentAssistSubjectControlAdapter != null && (fProposalPopup == null || !fProposalPopup.isActive())) {
2135            IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
2136            ContentAssistEvent event= new ContentAssistEvent(this, processor);
2137            for (Iterator JavaDoc it= new ArrayList JavaDoc(fCompletionListeners).iterator(); it.hasNext();) {
2138                ICompletionListener listener= (ICompletionListener) it.next();
2139                listener.assistSessionStarted(event);
2140            }
2141        }
2142    }
2143
2144    /**
2145     * Fires a session end event to all registered {@link ICompletionListener}s.
2146     *
2147     * @since 3.2
2148     */

2149    void fireSessionEndEvent() {
2150        if (fContentAssistSubjectControlAdapter != null) {
2151            IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
2152            ContentAssistEvent event= new ContentAssistEvent(this, processor);
2153            for (Iterator JavaDoc it= new ArrayList JavaDoc(fCompletionListeners).iterator(); it.hasNext();) {
2154                ICompletionListener listener= (ICompletionListener) it.next();
2155                listener.assistSessionEnded(event);
2156            }
2157        }
2158    }
2159
2160    /*
2161     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setRepeatedInvocationMode(boolean)
2162     * @since 3.2
2163     */

2164    public void setRepeatedInvocationMode(boolean cycling) {
2165        fIsRepetitionMode= cycling;
2166    }
2167
2168    /**
2169     * Returns <code>true</code> if repeated invocation mode is enabled, <code>false</code>
2170     * otherwise.
2171     *
2172     * @return <code>true</code> if repeated invocation mode is enabled, <code>false</code>
2173     * otherwise
2174     * @since 3.2
2175     */

2176    boolean isRepeatedInvocationMode() {
2177        return fIsRepetitionMode;
2178    }
2179
2180    /*
2181     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setShowEmptyList(boolean)
2182     * @since 3.2
2183     */

2184    public void setShowEmptyList(boolean showEmpty) {
2185        fShowEmptyList= showEmpty;
2186    }
2187
2188    /**
2189     * Returns <code>true</code> if empty lists should be displayed, <code>false</code>
2190     * otherwise.
2191     *
2192     * @return <code>true</code> if empty lists should be displayed, <code>false</code>
2193     * otherwise
2194     * @since 3.2
2195     */

2196    boolean isShowEmptyList() {
2197        return fShowEmptyList;
2198    }
2199
2200    /*
2201     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusLineVisible(boolean)
2202     * @since 3.2
2203     */

2204    public void setStatusLineVisible(boolean show) {
2205        fIsStatusLineVisible= show;
2206        if (fProposalPopup != null)
2207            fProposalPopup.setStatusLineVisible(show);
2208    }
2209
2210    /**
2211     * Returns <code>true</code> if a message line should be displayed, <code>false</code>
2212     * otherwise.
2213     *
2214     * @return <code>true</code> if a message line should be displayed, <code>false</code>
2215     * otherwise
2216     * @since 3.2
2217     */

2218    boolean isStatusLineVisible() {
2219        return fIsStatusLineVisible;
2220    }
2221
2222    /*
2223     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusMessage(java.lang.String)
2224     * @since 3.2
2225     */

2226    public void setStatusMessage(String JavaDoc message) {
2227        Assert.isLegal(message != null);
2228        fMessage= message;
2229        if (fProposalPopup != null)
2230            fProposalPopup.setMessage(message);
2231    }
2232
2233    /**
2234     * Returns the affordance caption for the cycling affordance at the bottom of the pop-up.
2235     *
2236     * @return the affordance caption for the cycling affordance at the bottom of the pop-up
2237     * @since 3.2
2238     */

2239    String JavaDoc getStatusMessage() {
2240        return fMessage;
2241    }
2242
2243    /*
2244     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setEmptyMessage(java.lang.String)
2245     * @since 3.2
2246     */

2247    public void setEmptyMessage(String JavaDoc message) {
2248        Assert.isLegal(message != null);
2249        if (fProposalPopup != null)
2250            fProposalPopup.setEmptyMessage(message);
2251    }
2252
2253    /**
2254     * Fires a selection event, see {@link ICompletionListener}.
2255     *
2256     * @param proposal the selected proposal, possibly <code>null</code>
2257     * @param smartToggle true if the smart toggle is on
2258     * @since 3.2
2259     */

2260    void fireSelectionEvent(ICompletionProposal proposal, boolean smartToggle) {
2261        for (Iterator JavaDoc it= new ArrayList JavaDoc(fCompletionListeners).iterator(); it.hasNext();) {
2262            ICompletionListener listener= (ICompletionListener) it.next();
2263            listener.selectionChanged(proposal, smartToggle);
2264        }
2265    }
2266
2267    /*
2268     * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension3#setInvocationTrigger(org.eclipse.jface.bindings.keys.KeySequence)
2269     * @since 3.2
2270     */

2271    public void setRepeatedInvocationTrigger(KeySequence sequence) {
2272        fTriggerSequence= sequence;
2273    }
2274
2275    /**
2276     * Returns the repeated invocation key sequence.
2277     *
2278     * @return the repeated invocation key sequence or <code>null</code>, if none
2279     * @since 3.2
2280     */

2281    KeySequence getTriggerSequence() {
2282        return fTriggerSequence;
2283    }
2284}
2285
Popular Tags