KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > internal > text > link > contentassist > CompletionProposalPopup2


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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  * Sean Montgomery, sean_montgomery@comcast.net - https://bugs.eclipse.org/bugs/show_bug.cgi?id=116454
11  *******************************************************************************/

12
13 package org.eclipse.jface.internal.text.link.contentassist;
14
15
16 import java.util.ArrayList JavaDoc;
17 import java.util.List JavaDoc;
18
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.custom.StyledText;
21 import org.eclipse.swt.events.ControlEvent;
22 import org.eclipse.swt.events.ControlListener;
23 import org.eclipse.swt.events.DisposeEvent;
24 import org.eclipse.swt.events.DisposeListener;
25 import org.eclipse.swt.events.KeyEvent;
26 import org.eclipse.swt.events.KeyListener;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.events.SelectionListener;
29 import org.eclipse.swt.events.VerifyEvent;
30 import org.eclipse.swt.graphics.Color;
31 import org.eclipse.swt.graphics.Point;
32 import org.eclipse.swt.layout.GridData;
33 import org.eclipse.swt.layout.GridLayout;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.swt.widgets.Shell;
36 import org.eclipse.swt.widgets.Table;
37 import org.eclipse.swt.widgets.TableItem;
38
39 import org.eclipse.jface.resource.JFaceResources;
40
41 import org.eclipse.jface.text.BadLocationException;
42 import org.eclipse.jface.text.DocumentEvent;
43 import org.eclipse.jface.text.IDocument;
44 import org.eclipse.jface.text.IDocumentListener;
45 import org.eclipse.jface.text.IEditingSupport;
46 import org.eclipse.jface.text.IEditingSupportRegistry;
47 import org.eclipse.jface.text.IRegion;
48 import org.eclipse.jface.text.IRewriteTarget;
49 import org.eclipse.jface.text.ITextViewer;
50 import org.eclipse.jface.text.ITextViewerExtension;
51 import org.eclipse.jface.text.TextUtilities;
52 import org.eclipse.jface.text.contentassist.ICompletionProposal;
53 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
54 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
55 import org.eclipse.jface.text.contentassist.IContextInformation;
56
57
58
59 /**
60  * This class is used to present proposals to the user. If additional
61  * information exists for a proposal, then selecting that proposal
62  * will result in the information being displayed in a secondary
63  * window.
64  *
65  * @see org.eclipse.jface.text.contentassist.ICompletionProposal
66  * @see org.eclipse.jface.internal.text.link.contentassist.AdditionalInfoController2
67  */

68 class CompletionProposalPopup2 implements IContentAssistListener2 {
69
70     /** The associated text viewer */
71     private ITextViewer fViewer;
72     /** The associated content assistant */
73     private ContentAssistant2 fContentAssistant;
74     /** The used additional info controller */
75     private AdditionalInfoController2 fAdditionalInfoController;
76     /** The closing strategy for this completion proposal popup */
77     private PopupCloser2 fPopupCloser= new PopupCloser2();
78     /** The popup shell */
79     private Shell fProposalShell;
80     /** The proposal table */
81     private Table fProposalTable;
82     /** Indicates whether a completion proposal is being inserted */
83     private boolean fInserting= false;
84     /** The key listener to control navigation */
85     private KeyListener fKeyListener;
86     /** List of document events used for filtering proposals */
87     private List JavaDoc fDocumentEvents= new ArrayList JavaDoc();
88     /** Listener filling the document event queue */
89     private IDocumentListener fDocumentListener;
90     /** Reentrance count for <code>filterProposals</code> */
91     private long fInvocationCounter= 0;
92     /** The filter list of proposals */
93     private ICompletionProposal[] fFilteredProposals;
94     /** The computed list of proposals */
95     private ICompletionProposal[] fComputedProposals;
96     /** The offset for which the proposals have been computed */
97     private int fInvocationOffset;
98     /** The offset for which the computed proposaks have been filtered */
99     private int fFilterOffset;
100     /** The default line delimiter of the viewer's widget */
101     private String JavaDoc fLineDelimiter;
102     /** The most recently selected proposal. */
103     private ICompletionProposal fLastProposal;
104     private final IEditingSupport fFocusEditingSupport= new IEditingSupport() {
105
106         public boolean isOriginator(DocumentEvent event, IRegion focus) {
107             return false;
108         }
109
110         public boolean ownsFocusShell() {
111             return Helper2.okToUse(fProposalShell) && fProposalShell.isFocusControl()
112                     || Helper2.okToUse(fProposalTable) && fProposalTable.isFocusControl();
113         }
114
115     };
116     private final IEditingSupport fModificationEditingSupport= new IEditingSupport() {
117
118         public boolean isOriginator(DocumentEvent event, IRegion focus) {
119             if (fViewer != null) {
120                 Point selection= fViewer.getSelectedRange();
121                 return selection.x <= focus.getOffset() + focus.getLength() && selection.x + selection.y >= focus.getOffset();
122             }
123             return false;
124         }
125
126         public boolean ownsFocusShell() {
127             return false;
128         }
129
130     };
131
132
133     /**
134      * Creates a new completion proposal popup for the given elements.
135      *
136      * @param contentAssistant the content assistant feeding this popup
137      * @param viewer the viewer on top of which this popup appears
138      * @param infoController the info control collaborating with this popup
139      * @since 2.0
140      */

141     public CompletionProposalPopup2(ContentAssistant2 contentAssistant, ITextViewer viewer, AdditionalInfoController2 infoController) {
142         fContentAssistant= contentAssistant;
143         fViewer= viewer;
144         fAdditionalInfoController= infoController;
145     }
146
147     /**
148      * Computes and presents completion proposals. The flag indicates whether this call has
149      * be made out of an auto activation context.
150      *
151      * @param autoActivated <code>true</code> if auto activation context
152      * @return an error message or <code>null</code> in case of no error
153      */

154     public String JavaDoc showProposals(final boolean autoActivated) {
155
156         if (fKeyListener == null) {
157             fKeyListener= new KeyListener() {
158                 public void keyPressed(KeyEvent e) {
159                     if (!Helper2.okToUse(fProposalShell))
160                         return;
161
162                     if (e.character == 0 && e.keyCode == SWT.MOD1) {
163                         // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
164
int index= fProposalTable.getSelectionIndex();
165                         if (index >= 0)
166                             selectProposal(index, true);
167                     }
168                 }
169
170                 public void keyReleased(KeyEvent e) {
171                     if (!Helper2.okToUse(fProposalShell))
172                         return;
173
174                     if (e.character == 0 && e.keyCode == SWT.MOD1) {
175                         // http://dev.eclipse.org/bugs/show_bug.cgi?id=34754
176
int index= fProposalTable.getSelectionIndex();
177                         if (index >= 0)
178                             selectProposal(index, false);
179                     }
180                 }
181             };
182         }
183
184         final StyledText styledText= fViewer.getTextWidget();
185         if (styledText != null && !styledText.isDisposed())
186             styledText.addKeyListener(fKeyListener);
187
188 // BusyIndicator.showWhile(styledText.getDisplay(), new Runnable() {
189
// public void run() {
190

191                 fInvocationOffset= fViewer.getSelectedRange().x;
192                 // lazily compute proposals
193
// if (fComputedProposals == null) fComputedProposals= computeProposals(fContentAssistant.getCompletionPosition());
194
fComputedProposals= computeProposals(fInvocationOffset);
195
196                 int count= (fComputedProposals == null ? 0 : fComputedProposals.length);
197                 if (count == 0) {
198
199                     if (!autoActivated)
200                         styledText.getDisplay().beep();
201
202                 } else {
203
204                     if (count == 1 && !autoActivated && fContentAssistant.isAutoInserting())
205
206                         insertProposal(fComputedProposals[0], (char) 0, 0, fInvocationOffset);
207
208                     else {
209
210                         if (fLineDelimiter == null)
211                             fLineDelimiter= styledText.getLineDelimiter();
212
213                         createProposalSelector();
214                         setProposals(fComputedProposals);
215                         resizeProposalSelector(true);
216                         displayProposals();
217                     }
218                 }
219 // }
220
// });
221

222         return getErrorMessage();
223     }
224
225     /**
226      * Returns the completion proposal available at the given offset of the
227      * viewer's document. Delegates the work to the content assistant.
228      *
229      * @param offset the offset
230      * @return the completion proposals available at this offset
231      */

232     private ICompletionProposal[] computeProposals(int offset) {
233         return fContentAssistant.computeCompletionProposals(fViewer, offset);
234     }
235
236     /**
237      * Returns the error message.
238      *
239      * @return the error message
240      */

241     private String JavaDoc getErrorMessage() {
242         return fContentAssistant.getErrorMessage();
243     }
244
245     /**
246      * Creates the proposal selector.
247      */

248     private void createProposalSelector() {
249         if (Helper2.okToUse(fProposalShell))
250             return;
251
252         Control control= fViewer.getTextWidget();
253         fProposalShell= new Shell(control.getShell(), SWT.ON_TOP);
254 // fProposalShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE );
255
fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL);
256 // fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL);
257

258         fProposalTable.setLocation(0, 0);
259         if (fAdditionalInfoController != null)
260             fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
261
262         GridLayout layout= new GridLayout();
263         layout.marginWidth= 0;
264         layout.marginHeight= 0;
265         fProposalShell.setLayout(layout);
266
267         GridData data= new GridData(GridData.FILL_BOTH);
268         fProposalTable.setLayoutData(data);
269
270         fProposalShell.pack();
271
272         // set location
273
Point currentLocation= fProposalShell.getLocation();
274         Point newLocation= getLocation();
275         if ((newLocation.x < currentLocation.x && newLocation.y == currentLocation.y) || newLocation.y < currentLocation.y)
276             fProposalShell.setLocation(newLocation);
277
278         if (fAdditionalInfoController != null) {
279             fProposalShell.addControlListener(new ControlListener() {
280
281                 public void controlMoved(ControlEvent e) {}
282
283                 public void controlResized(ControlEvent e) {
284                     // resets the cached resize constraints
285
fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
286                 }
287             });
288         }
289
290         fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
291
292         Color c= fContentAssistant.getProposalSelectorBackground();
293         if (c == null)
294             c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
295         fProposalTable.setBackground(c);
296
297         c= fContentAssistant.getProposalSelectorForeground();
298         if (c == null)
299             c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
300         fProposalTable.setForeground(c);
301
302         fProposalTable.addSelectionListener(new SelectionListener() {
303
304             public void widgetSelected(SelectionEvent e) {}
305
306             public void widgetDefaultSelected(SelectionEvent e) {
307                 selectProposalWithMask(e.stateMask);
308             }
309         });
310
311         fPopupCloser.install(fContentAssistant, fProposalTable);
312
313         fProposalShell.addDisposeListener(new DisposeListener() {
314             public void widgetDisposed(DisposeEvent e) {
315                 unregister(); // but don't dispose the shell, since we're being called from its disposal event!
316
}
317         });
318
319         fProposalTable.setHeaderVisible(false);
320         fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant2.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset());
321     }
322
323     /**
324      * Returns the proposal selected in the proposal selector.
325      *
326      * @return the selected proposal
327      * @since 2.0
328      */

329     private ICompletionProposal getSelectedProposal() {
330         int i= fProposalTable.getSelectionIndex();
331         if (i < 0 || i >= fFilteredProposals.length)
332             return null;
333         return fFilteredProposals[i];
334     }
335
336     /**
337      * Takes the selected proposal and applies it.
338      *
339      * @param stateMask the state mask
340      * @since 2.1
341      */

342     private void selectProposalWithMask(int stateMask) {
343         ICompletionProposal p= getSelectedProposal();
344         hide();
345         if (p != null)
346             insertProposal(p, (char) 0, stateMask, fViewer.getSelectedRange().x);
347     }
348
349     /**
350      * Applies the given proposal at the given offset. The given character is the
351      * one that triggered the insertion of this proposal.
352      *
353      * @param p the completion proposal
354      * @param trigger the trigger character
355      * @param stateMask the state mask of the keyboard event triggering the insertion
356      * @param offset the offset
357      * @since 2.1
358      */

359     private void insertProposal(ICompletionProposal p, char trigger, int stateMask, final int offset) {
360
361         fInserting= true;
362         IRewriteTarget target= null;
363         IEditingSupportRegistry registry= null;
364
365         try {
366
367             IDocument document= fViewer.getDocument();
368
369             if (fViewer instanceof ITextViewerExtension) {
370                 ITextViewerExtension extension= (ITextViewerExtension) fViewer;
371                 target= extension.getRewriteTarget();
372             }
373
374             if (target != null)
375                 target.beginCompoundChange();
376
377             if (fViewer instanceof IEditingSupportRegistry) {
378                 registry= (IEditingSupportRegistry) fViewer;
379                 registry.register(fModificationEditingSupport);
380             }
381
382             if (p instanceof ICompletionProposalExtension2) {
383                 ICompletionProposalExtension2 e= (ICompletionProposalExtension2) p;
384                 e.apply(fViewer, trigger, stateMask, offset);
385             } else if (p instanceof ICompletionProposalExtension) {
386                 ICompletionProposalExtension e= (ICompletionProposalExtension) p;
387                 e.apply(document, trigger, offset);
388             } else {
389                 p.apply(document);
390             }
391
392             Point selection= p.getSelection(document);
393             if (selection != null) {
394                 fViewer.setSelectedRange(selection.x, selection.y);
395                 fViewer.revealRange(selection.x, selection.y);
396             }
397
398             IContextInformation info= p.getContextInformation();
399             if (info != null) {
400
401                 int position;
402                 if (p instanceof ICompletionProposalExtension) {
403                     ICompletionProposalExtension e= (ICompletionProposalExtension) p;
404                     position= e.getContextInformationPosition();
405                 } else {
406                     if (selection == null)
407                         selection= fViewer.getSelectedRange();
408                     position= selection.x + selection.y;
409                 }
410
411                 fContentAssistant.showContextInformation(info, position);
412             }
413
414             fContentAssistant.fireProposalChosen(p);
415
416         } finally {
417             if (target != null)
418                 target.endCompoundChange();
419
420             if (registry != null)
421                 registry.unregister(fModificationEditingSupport);
422
423             fInserting= false;
424         }
425     }
426
427     /**
428      * Returns whether this popup has the focus.
429      *
430      * @return <code>true</code> if the popup has the focus
431      */

432     public boolean hasFocus() {
433         if (Helper2.okToUse(fProposalShell))
434             return (fProposalShell.isFocusControl() || fProposalTable.isFocusControl());
435
436         return false;
437     }
438
439     /**
440      * Hides this popup.
441      */

442     public void hide() {
443
444         unregister();
445
446         if (fViewer instanceof IEditingSupportRegistry) {
447             IEditingSupportRegistry registry= (IEditingSupportRegistry) fViewer;
448             registry.unregister(fFocusEditingSupport);
449         }
450
451         if (Helper2.okToUse(fProposalShell)) {
452             fContentAssistant.removeContentAssistListener(this, ContentAssistant2.PROPOSAL_SELECTOR);
453
454             fPopupCloser.uninstall();
455             // see bug 47511: setVisible may run the event loop on GTK
456
// and trigger a rentrant call - have to make sure we don't
457
// dispose another shell that was already brought up in a
458
// reentrant call when calling setVisible()
459
Shell tempShell= fProposalShell;
460             fProposalShell= null;
461             tempShell.setVisible(false);
462             tempShell.dispose();
463         }
464     }
465
466     private void unregister() {
467         if (fDocumentListener != null) {
468             IDocument document= fViewer.getDocument();
469             if (document != null)
470                 document.removeDocumentListener(fDocumentListener);
471             fDocumentListener= null;
472         }
473         fDocumentEvents.clear();
474
475         StyledText styledText= fViewer.getTextWidget();
476         if (fKeyListener != null && styledText != null && !styledText.isDisposed())
477             styledText.removeKeyListener(fKeyListener);
478
479         if (fLastProposal != null) {
480             if (fLastProposal instanceof ICompletionProposalExtension2) {
481                 ICompletionProposalExtension2 extension= (ICompletionProposalExtension2) fLastProposal;
482                 extension.unselected(fViewer);
483             }
484
485             fLastProposal= null;
486         }
487
488         fFilteredProposals= null;
489
490         fContentAssistant.possibleCompletionsClosed();
491     }
492
493     /**
494      *Returns whether this popup is active. It is active if the propsal selector is visible.
495      *
496      * @return <code>true</code> if this popup is active
497      */

498     public boolean isActive() {
499         return fProposalShell != null && !fProposalShell.isDisposed();
500     }
501
502     /**
503      * Initializes the proposal selector with these given proposals.
504      *
505      * @param proposals the proposals
506      */

507     private void setProposals(ICompletionProposal[] proposals) {
508         if (Helper2.okToUse(fProposalTable)) {
509
510             ICompletionProposal oldProposal= getSelectedProposal();
511             if (oldProposal instanceof ICompletionProposalExtension2)
512                 ((ICompletionProposalExtension2) oldProposal).unselected(fViewer);
513
514             fFilteredProposals= proposals;
515
516             fProposalTable.setRedraw(false);
517             fProposalTable.removeAll();
518
519             Point selection= fViewer.getSelectedRange();
520             int endOffset;
521             endOffset= selection.x + selection.y;
522             IDocument document= fViewer.getDocument();
523             boolean validate= false;
524             if (selection.y != 0 && document != null) validate= true;
525             int selectionIndex= 0;
526
527             TableItem item;
528             ICompletionProposal p;
529             for (int i= 0; i < proposals.length; i++) {
530                 p= proposals[i];
531                 item= new TableItem(fProposalTable, SWT.NULL);
532                 if (p.getImage() != null)
533                     item.setImage(p.getImage());
534                 item.setText(p.getDisplayString());
535                 item.setData(p);
536
537                 if (validate && validateProposal(document, p, endOffset, null)) {
538                     selectionIndex= i;
539                     validate= false;
540                 }
541             }
542
543             resizeProposalSelector(false);
544
545             selectProposal(selectionIndex, false);
546             fProposalTable.setRedraw(true);
547         }
548     }
549
550     private void resizeProposalSelector(boolean adjustWidth) {
551         // in order to fill in the table items so size computation works correctly
552
// will cause flicker, though
553
fProposalTable.setRedraw(true);
554
555         int width= adjustWidth ? SWT.DEFAULT : ((GridData)fProposalTable.getLayoutData()).widthHint;
556         Point size= fProposalTable.computeSize(width, SWT.DEFAULT, true);
557
558         GridData data= new GridData(GridData.FILL_BOTH);
559         data.widthHint= adjustWidth ? Math.min(size.x, 300) : width;
560         data.heightHint= Math.min(getTableHeightHint(fProposalTable, fProposalTable.getItemCount()), getTableHeightHint(fProposalTable, 10));
561         fProposalTable.setLayoutData(data);
562
563         fProposalShell.layout(true);
564         fProposalShell.pack();
565
566         if (adjustWidth) {
567             fProposalShell.setLocation(getLocation());
568         }
569     }
570
571     /**
572      * Computes the table hight hint for <code>table</code>.
573      *
574      * @param table the table to compute the height for
575      * @param rows the number of rows to compute the height for
576      * @return the height hint for <code>table</code>
577      */

578     private int getTableHeightHint(Table table, int rows) {
579         if (table.getFont().equals(JFaceResources.getDefaultFont()))
580             table.setFont(JFaceResources.getDialogFont());
581         int result= table.getItemHeight() * rows;
582         if (table.getLinesVisible())
583             result+= table.getGridLineWidth() * (rows - 1);
584
585         // TODO adjust to correct size. +4 works on windows, but not others
586
// return result + 4;
587
return result;
588     }
589
590     private boolean validateProposal(IDocument document, ICompletionProposal p, int offset, DocumentEvent event) {
591         // detect selected
592
if (p instanceof ICompletionProposalExtension2) {
593             ICompletionProposalExtension2 e= (ICompletionProposalExtension2) p;
594             if (e.validate(document, offset, event))
595                 return true;
596         } else if (p instanceof ICompletionProposalExtension) {
597             ICompletionProposalExtension e= (ICompletionProposalExtension) p;
598             if (e.isValidFor(document, offset))
599                 return true;
600         }
601         return false;
602     }
603
604     /**
605      * Returns the graphical location at which this popup should be made visible.
606      *
607      * @return the location of this popup
608      */

609     private Point getLocation() {
610         StyledText text= fViewer.getTextWidget();
611         Point selection= text.getSelection();
612         Point p= text.getLocationAtOffset(selection.x);
613         p.x -= fProposalShell.getBorderWidth();
614         if (p.x < 0) p.x= 0;
615         if (p.y < 0) p.y= 0;
616         p= new Point(p.x, p.y + text.getLineHeight(selection.x));
617         p= text.toDisplay(p);
618         return p;
619     }
620
621     /**
622      *Displays this popup and install the additional info controller, so that additional info
623      * is displayed when a proposal is selected and additional info is available.
624      */

625     private void displayProposals() {
626         if (fContentAssistant.addContentAssistListener(this, ContentAssistant2.PROPOSAL_SELECTOR)) {
627
628             if (fDocumentListener == null)
629                 fDocumentListener= new IDocumentListener() {
630                     public void documentAboutToBeChanged(DocumentEvent event) {
631                         if (!fInserting)
632                             fDocumentEvents.add(event);
633                     }
634
635                     public void documentChanged(DocumentEvent event) {
636                         if (!fInserting)
637                             filterProposals();
638                     }
639                 };
640             IDocument document= fViewer.getDocument();
641             if (document != null)
642                 document.addDocumentListener(fDocumentListener);
643
644
645             if (fViewer instanceof IEditingSupportRegistry) {
646                 IEditingSupportRegistry registry= (IEditingSupportRegistry) fViewer;
647                 registry.register(fFocusEditingSupport);
648             }
649
650             fProposalShell.setVisible(true);
651             // see bug 47511: setVisible may run the event loop on GTK
652
// and trigger a rentrant call - have to check whether we are still
653
// visible
654
if (!Helper2.okToUse(fProposalShell))
655                 return;
656
657
658             if (fAdditionalInfoController != null) {
659                 fAdditionalInfoController.install(fProposalTable);
660                 fAdditionalInfoController.handleTableSelectionChanged();
661             }
662         }
663     }
664
665         /*
666          * @see IContentAssistListener#verifyKey(VerifyEvent)
667          */

668         public boolean verifyKey(VerifyEvent e) {
669             if (!Helper2.okToUse(fProposalShell))
670                 return true;
671
672             char key= e.character;
673             if (key == 0) {
674                 int newSelection= fProposalTable.getSelectionIndex();
675                 int visibleRows= (fProposalTable.getSize().y / fProposalTable.getItemHeight()) - 1;
676                 boolean smartToggle= false;
677                 switch (e.keyCode) {
678
679                     case SWT.ARROW_LEFT :
680                     case SWT.ARROW_RIGHT :
681                         filterProposals();
682                         return true;
683
684                     case SWT.ARROW_UP :
685                         newSelection -= 1;
686                         if (newSelection < 0)
687                             newSelection= fProposalTable.getItemCount() - 1;
688                         break;
689
690                     case SWT.ARROW_DOWN :
691                         newSelection += 1;
692                         if (newSelection > fProposalTable.getItemCount() - 1)
693                             newSelection= 0;
694                         break;
695
696                     case SWT.PAGE_DOWN :
697                         newSelection += visibleRows;
698                         if (newSelection >= fProposalTable.getItemCount())
699                             newSelection= fProposalTable.getItemCount() - 1;
700                         break;
701
702                     case SWT.PAGE_UP :
703                         newSelection -= visibleRows;
704                         if (newSelection < 0)
705                             newSelection= 0;
706                         break;
707
708                     case SWT.HOME :
709                         newSelection= 0;
710                         break;
711
712                     case SWT.END :
713                         newSelection= fProposalTable.getItemCount() - 1;
714                         break;
715
716                     default :
717                         if (e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4)
718                             hide();
719                         return true;
720                 }
721
722                 selectProposal(newSelection, smartToggle);
723
724                 e.doit= false;
725                 return false;
726
727             }
728
729             // key != 0
730
switch (key) {
731                 case 0x1B: // Esc
732
e.doit= false;
733                     hide();
734                     break;
735
736                 case '\n': // Ctrl-Enter on w2k
737
case '\r': // Enter
738
if ((e.stateMask & SWT.CTRL) == 0) {
739                         e.doit= false;
740                         selectProposalWithMask(e.stateMask);
741                     }
742                     break;
743
744                     // in linked mode: hide popup
745
// plus: don't invalidate the event in order to give LinkedUI a chance to handle it
746
case '\t':
747 // hide();
748
break;
749
750                 default:
751                     ICompletionProposal p= getSelectedProposal();
752                 if (p instanceof ICompletionProposalExtension) {
753                     ICompletionProposalExtension t= (ICompletionProposalExtension) p;
754                     char[] triggers= t.getTriggerCharacters();
755                     if (contains(triggers, key)) {
756                         hide();
757                         if (key == ';') {
758                             e.doit= true;
759                             insertProposal(p, (char) 0, e.stateMask, fViewer.getSelectedRange().x);
760                         } else {
761                             e.doit= false;
762                             insertProposal(p, key, e.stateMask, fViewer.getSelectedRange().x);
763                         }
764                     }
765                 }
766             }
767
768             return true;
769         }
770
771     /**
772      * Selects the entry with the given index in the proposal selector and feeds
773      * the selection to the additional info controller.
774      *
775      * @param index the index in the list
776      * @param smartToggle <code>true</code> if the smart toogle key has been pressed
777      * @since 2.1
778      */

779     private void selectProposal(int index, boolean smartToggle) {
780
781         ICompletionProposal oldProposal= getSelectedProposal();
782         if (oldProposal instanceof ICompletionProposalExtension2)
783             ((ICompletionProposalExtension2) oldProposal).unselected(fViewer);
784
785         ICompletionProposal proposal= fFilteredProposals[index];
786         if (proposal instanceof ICompletionProposalExtension2)
787             ((ICompletionProposalExtension2) proposal).selected(fViewer, smartToggle);
788
789         fLastProposal= proposal;
790
791         fProposalTable.setSelection(index);
792         fProposalTable.showSelection();
793         if (fAdditionalInfoController != null)
794             fAdditionalInfoController.handleTableSelectionChanged();
795     }
796
797     /**
798      * Returns whether the given character is contained in the given array of
799      * characters.
800      *
801      * @param characters the list of characters
802      * @param c the character to look for in the list
803      * @return <code>true</code> if character belongs to the list
804      * @since 2.0
805      */

806     private boolean contains(char[] characters, char c) {
807
808         if (characters == null)
809             return false;
810
811         for (int i= 0; i < characters.length; i++) {
812             if (c == characters[i])
813                 return true;
814         }
815
816         return false;
817     }
818
819     /*
820      * @see IEventConsumer#processEvent(VerifyEvent)
821      */

822     public void processEvent(VerifyEvent e) {
823     }
824
825     /**
826      * Filters the displayed proposal based on the given cursor position and the
827      * offset of the original invocation of the content assistant.
828      */

829     private void filterProposals() {
830         ++ fInvocationCounter;
831         Control control= fViewer.getTextWidget();
832         control.getDisplay().asyncExec(new Runnable JavaDoc() {
833             long fCounter= fInvocationCounter;
834             public void run() {
835
836                 if (fCounter != fInvocationCounter) return;
837
838                 int offset= fViewer.getSelectedRange().x;
839                 ICompletionProposal[] proposals= null;
840                 try {
841                     if (offset > -1) {
842                         DocumentEvent event= TextUtilities.mergeProcessedDocumentEvents(fDocumentEvents);
843                         proposals= computeFilteredProposals(offset, event);
844                     }
845                 } catch (BadLocationException x) {
846                 } finally {
847                     fDocumentEvents.clear();
848                 }
849                 fFilterOffset= offset;
850
851                 if (proposals != null && proposals.length > 0)
852                     setProposals(proposals);
853                 else
854                     hide();
855             }
856         });
857     }
858
859     /**
860      * Computes the subset of already computed propsals that are still valid for
861      * the given offset.
862      *
863      * @param offset the offset
864      * @param event the merged document event
865      * @return the set of filtered proposals
866      * @since 2.0
867      */

868     private ICompletionProposal[] computeFilteredProposals(int offset, DocumentEvent event) {
869
870         if (offset == fInvocationOffset && event == null)
871             return fComputedProposals;
872
873         if (offset < fInvocationOffset) {
874             return null;
875         }
876
877         ICompletionProposal[] proposals= fComputedProposals;
878         if (offset > fFilterOffset)
879             proposals= fFilteredProposals;
880
881         if (proposals == null)
882             return null;
883
884         IDocument document= fViewer.getDocument();
885         int length= proposals.length;
886         List JavaDoc filtered= new ArrayList JavaDoc(length);
887         for (int i= 0; i < length; i++) {
888
889             if (proposals[i] instanceof ICompletionProposalExtension2) {
890
891                 ICompletionProposalExtension2 p= (ICompletionProposalExtension2) proposals[i];
892                 if (p.validate(document, offset, event))
893                     filtered.add(p);
894
895             } else if (proposals[i] instanceof ICompletionProposalExtension) {
896
897                 ICompletionProposalExtension p= (ICompletionProposalExtension) proposals[i];
898                 if (p.isValidFor(document, offset))
899                     filtered.add(p);
900
901             } else {
902                 // restore original behavior
903
fInvocationOffset= offset;
904                 fComputedProposals= computeProposals(fInvocationOffset);
905                 return fComputedProposals;
906             }
907         }
908
909         ICompletionProposal[] p= new ICompletionProposal[filtered.size()];
910         filtered.toArray(p);
911         return p;
912     }
913
914     /**
915      * Requests the proposal shell to take focus.
916      *
917      * @since 3.0
918      */

919     public void setFocus() {
920         if (Helper2.okToUse(fProposalShell))
921             fProposalShell.setFocus();
922     }
923 }
924
Popular Tags