1 12 package org.eclipse.jface.text.contentassist; 13 14 import java.util.ArrayList ; 15 import java.util.List ; 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.custom.BusyIndicator; 19 import org.eclipse.swt.events.ControlEvent; 20 import org.eclipse.swt.events.ControlListener; 21 import org.eclipse.swt.events.DisposeEvent; 22 import org.eclipse.swt.events.DisposeListener; 23 import org.eclipse.swt.events.FocusEvent; 24 import org.eclipse.swt.events.FocusListener; 25 import org.eclipse.swt.events.KeyAdapter; 26 import org.eclipse.swt.events.KeyEvent; 27 import org.eclipse.swt.events.KeyListener; 28 import org.eclipse.swt.events.MouseAdapter; 29 import org.eclipse.swt.events.MouseEvent; 30 import org.eclipse.swt.events.SelectionEvent; 31 import org.eclipse.swt.events.SelectionListener; 32 import org.eclipse.swt.events.VerifyEvent; 33 import org.eclipse.swt.graphics.Color; 34 import org.eclipse.swt.graphics.Font; 35 import org.eclipse.swt.graphics.FontData; 36 import org.eclipse.swt.graphics.Image; 37 import org.eclipse.swt.graphics.Point; 38 import org.eclipse.swt.graphics.Rectangle; 39 import org.eclipse.swt.layout.GridData; 40 import org.eclipse.swt.layout.GridLayout; 41 import org.eclipse.swt.widgets.Control; 42 import org.eclipse.swt.widgets.Display; 43 import org.eclipse.swt.widgets.Event; 44 import org.eclipse.swt.widgets.Label; 45 import org.eclipse.swt.widgets.Listener; 46 import org.eclipse.swt.widgets.Shell; 47 import org.eclipse.swt.widgets.Table; 48 import org.eclipse.swt.widgets.TableItem; 49 50 import org.eclipse.core.runtime.Assert; 51 52 import org.eclipse.jface.text.AbstractInformationControlManager; 53 import org.eclipse.jface.text.BadLocationException; 54 import org.eclipse.jface.text.DocumentEvent; 55 import org.eclipse.jface.text.IDocument; 56 import org.eclipse.jface.text.IDocumentListener; 57 import org.eclipse.jface.text.IEditingSupport; 58 import org.eclipse.jface.text.IEditingSupportRegistry; 59 import org.eclipse.jface.text.IRegion; 60 import org.eclipse.jface.text.IRewriteTarget; 61 import org.eclipse.jface.text.ITextViewer; 62 import org.eclipse.jface.text.ITextViewerExtension; 63 import org.eclipse.jface.text.TextUtilities; 64 import org.eclipse.jface.text.AbstractInformationControlManager.Anchor; 65 66 import org.eclipse.jface.bindings.keys.KeySequence; 67 import org.eclipse.jface.bindings.keys.SWTKeySupport; 68 import org.eclipse.jface.contentassist.IContentAssistSubjectControl; 69 import org.eclipse.jface.resource.JFaceResources; 70 import org.eclipse.jface.util.Geometry; 71 72 73 82 class CompletionProposalPopup implements IContentAssistListener { 83 89 private static final boolean USE_VIRTUAL= !"motif".equals(SWT.getPlatform()); 91 96 private static final class EmptyProposal implements ICompletionProposal, ICompletionProposalExtension { 97 98 String fDisplayString; 99 int fOffset; 100 103 public void apply(IDocument document) { 104 } 105 106 109 public Point getSelection(IDocument document) { 110 return new Point(fOffset, 0); 111 } 112 113 116 public IContextInformation getContextInformation() { 117 return null; 118 } 119 120 123 public Image getImage() { 124 return null; 125 } 126 127 130 public String getDisplayString() { 131 return fDisplayString; 132 } 133 134 137 public String getAdditionalProposalInfo() { 138 return null; 139 } 140 141 144 public void apply(IDocument document, char trigger, int offset) { 145 } 146 147 150 public boolean isValidFor(IDocument document, int offset) { 151 return false; 152 } 153 154 157 public char[] getTriggerCharacters() { 158 return null; 159 } 160 161 164 public int getContextInformationPosition() { 165 return -1; 166 } 167 } 168 169 private final class ProposalSelectionListener implements KeyListener { 170 public void keyPressed(KeyEvent e) { 171 if (!Helper.okToUse(fProposalShell)) 172 return; 173 174 if (e.character == 0 && e.keyCode == SWT.MOD1) { 175 int index= fProposalTable.getSelectionIndex(); 177 if (index >= 0) 178 selectProposal(index, true); 179 } 180 } 181 182 public void keyReleased(KeyEvent e) { 183 if (!Helper.okToUse(fProposalShell)) 184 return; 185 186 if (e.character == 0 && e.keyCode == SWT.MOD1) { 187 int index= fProposalTable.getSelectionIndex(); 189 if (index >= 0) 190 selectProposal(index, false); 191 } 192 } 193 } 194 195 private final class CommandKeyListener extends KeyAdapter { 196 private KeySequence fCommandSequence; 197 198 private CommandKeyListener(KeySequence keySequence) { 199 fCommandSequence= keySequence; 200 } 201 202 public void keyPressed(KeyEvent e) { 203 if (!Helper.okToUse(fProposalShell)) 204 return; 205 206 int accelerator= SWTKeySupport.convertEventToUnmodifiedAccelerator(e); 207 KeySequence sequence= KeySequence.getInstance(SWTKeySupport.convertAcceleratorToKeyStroke(accelerator)); 208 if (sequence.equals(fCommandSequence)) 209 if (fContentAssistant.isPrefixCompletionEnabled()) 210 incrementalComplete(); 211 else 212 showProposals(false); 213 214 } 215 } 216 217 218 private ITextViewer fViewer; 219 220 private ContentAssistant fContentAssistant; 221 222 private AdditionalInfoController fAdditionalInfoController; 223 224 private PopupCloser fPopupCloser= new PopupCloser(); 225 226 private Shell fProposalShell; 227 228 private Table fProposalTable; 229 230 private boolean fInserting= false; 231 232 private ProposalSelectionListener fKeyListener; 233 234 private List fDocumentEvents= new ArrayList (); 235 236 private IDocumentListener fDocumentListener; 237 238 private ICompletionProposal[] fFilteredProposals; 239 240 private ICompletionProposal[] fComputedProposals; 241 242 private int fInvocationOffset; 243 244 private int fFilterOffset; 245 249 private ICompletionProposal fLastProposal; 250 256 private IContentAssistSubjectControl fContentAssistSubjectControl; 257 263 private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter; 264 268 private Point fSize; 269 274 private IEditingSupport fFocusHelper; 275 281 private boolean fIsFilteredSubset; 282 287 private final Runnable fFilterRunnable= new Runnable () { 288 public void run() { 289 if (!fIsFilterPending) 290 return; 291 292 fIsFilterPending= false; 293 294 if (!Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) 295 return; 296 297 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x; 298 ICompletionProposal[] proposals= null; 299 try { 300 if (offset > -1) { 301 DocumentEvent event= TextUtilities.mergeProcessedDocumentEvents(fDocumentEvents); 302 proposals= computeFilteredProposals(offset, event); 303 } 304 } catch (BadLocationException x) { 305 } finally { 306 fDocumentEvents.clear(); 307 } 308 fFilterOffset= offset; 309 310 if (proposals != null && proposals.length > 0) 311 setProposals(proposals, fIsFilteredSubset); 312 else 313 hide(); 314 } 315 }; 316 322 private boolean fIsFilterPending= false; 323 329 private Label fMessageText; 330 335 private Font fMessageTextFont; 336 341 private int fLastCompletionOffset; 342 347 private final EmptyProposal fEmptyProposal= new EmptyProposal(); 348 353 private String fEmptyMessage= null; 354 355 363 public CompletionProposalPopup(ContentAssistant contentAssistant, ITextViewer viewer, AdditionalInfoController infoController) { 364 fContentAssistant= contentAssistant; 365 fViewer= viewer; 366 fAdditionalInfoController= infoController; 367 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer); 368 } 369 370 378 public CompletionProposalPopup(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl, AdditionalInfoController infoController) { 379 fContentAssistant= contentAssistant; 380 fContentAssistSubjectControl= contentAssistSubjectControl; 381 fAdditionalInfoController= infoController; 382 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl); 383 } 384 385 392 public String showProposals(final boolean autoActivated) { 393 394 if (fKeyListener == null) 395 fKeyListener= new ProposalSelectionListener(); 396 397 final Control control= fContentAssistSubjectControlAdapter.getControl(); 398 399 if (!Helper.okToUse(fProposalShell) && control != null && !control.isDisposed()) { 400 fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener); 403 404 BusyIndicator.showWhile(control.getDisplay(), new Runnable () { 405 public void run() { 406 407 fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x; 408 fFilterOffset= fInvocationOffset; 409 fLastCompletionOffset= fFilterOffset; 410 fComputedProposals= computeProposals(fInvocationOffset); 411 412 int count= (fComputedProposals == null ? 0 : fComputedProposals.length); 413 if (count == 0 && hideWhenNoProposals(autoActivated)) 414 return; 415 416 if (count == 1 && !autoActivated && canAutoInsert(fComputedProposals[0])) { 417 insertProposal(fComputedProposals[0], (char) 0, 0, fInvocationOffset); 418 hide(); 419 } else { 420 createProposalSelector(); 421 setProposals(fComputedProposals, false); 422 displayProposals(); 423 } 424 } 425 }); 426 } else { 427 fLastCompletionOffset= fFilterOffset; 428 handleRepeatedInvocation(); 429 } 430 431 return getErrorMessage(); 432 } 433 434 442 private boolean hideWhenNoProposals(boolean autoActivated) { 443 if (autoActivated || !fContentAssistant.isShowEmptyList()) { 444 if (!autoActivated) { 445 Control control= fContentAssistSubjectControlAdapter.getControl(); 446 if (control != null && !control.isDisposed()) 447 control.getDisplay().beep(); 448 } 449 hide(); 450 return true; 451 } 452 return false; 453 } 454 455 461 private void handleRepeatedInvocation() { 462 if (fContentAssistant.isRepeatedInvocationMode()) { 463 fComputedProposals= computeProposals(fFilterOffset); 464 setProposals(fComputedProposals, false); 465 } 466 } 467 468 475 private ICompletionProposal[] computeProposals(int offset) { 476 if (fContentAssistSubjectControl != null) 477 return fContentAssistant.computeCompletionProposals(fContentAssistSubjectControl, offset); 478 return fContentAssistant.computeCompletionProposals(fViewer, offset); 479 } 480 481 486 private String getErrorMessage() { 487 return fContentAssistant.getErrorMessage(); 488 } 489 490 493 private void createProposalSelector() { 494 if (Helper.okToUse(fProposalShell)) 495 return; 496 497 Control control= fContentAssistSubjectControlAdapter.getControl(); 498 fProposalShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE ); 499 fProposalShell.setFont(JFaceResources.getDefaultFont()); 500 if (USE_VIRTUAL) { 501 fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.VIRTUAL); 502 503 Listener listener= new Listener() { 504 public void handleEvent(Event event) { 505 handleSetData(event); 506 } 507 }; 508 fProposalTable.addListener(SWT.SetData, listener); 509 } else { 510 fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL); 511 } 512 513 fProposalTable.setLocation(0, 0); 514 if (fAdditionalInfoController != null) 515 fAdditionalInfoController.setSizeConstraints(50, 10, true, true); 516 517 GridLayout layout= new GridLayout(); 518 layout.marginWidth= 0; 519 layout.marginHeight= 0; 520 layout.verticalSpacing= 1; 521 fProposalShell.setLayout(layout); 522 523 if (fContentAssistant.isStatusLineVisible()) { 524 createMessageText(); 525 } 526 527 GridData data= new GridData(GridData.FILL_BOTH); 528 529 Point size= fContentAssistant.restoreCompletionProposalPopupSize(); 530 if (size != null) { 531 fProposalTable.setLayoutData(data); 532 fProposalShell.setSize(size); 533 } else { 534 int height= fProposalTable.getItemHeight() * 10; 535 final double aspectRatio= (1 + Math.sqrt(5)) / 2; 537 int width= (int) (height * aspectRatio); 538 Rectangle trim= fProposalTable.computeTrim(0, 0, width, height); 539 data.heightHint= trim.height; 540 data.widthHint= trim.width; 541 fProposalTable.setLayoutData(data); 542 fProposalShell.pack(); 543 } 544 fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset()); 545 546 fProposalShell.addControlListener(new ControlListener() { 547 548 public void controlMoved(ControlEvent e) {} 549 550 public void controlResized(ControlEvent e) { 551 if (fAdditionalInfoController != null) { 552 fAdditionalInfoController.setSizeConstraints(50, 10, true, false); 554 } 555 556 fSize= fProposalShell.getSize(); 557 } 558 }); 559 560 if (!"carbon".equals(SWT.getPlatform())) fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_GRAY)); 562 563 Color c= getBackgroundColor(control); 564 fProposalTable.setBackground(c); 565 566 c= getForegroundColor(control); 567 fProposalTable.setForeground(c); 568 569 fProposalTable.addSelectionListener(new SelectionListener() { 570 571 public void widgetSelected(SelectionEvent e) {} 572 573 public void widgetDefaultSelected(SelectionEvent e) { 574 insertSelectedProposalWithMask(e.stateMask); 575 } 576 }); 577 578 fPopupCloser.install(fContentAssistant, fProposalTable); 579 580 fProposalShell.addDisposeListener(new DisposeListener() { 581 public void widgetDisposed(DisposeEvent e) { 582 unregister(); } 584 }); 585 586 fProposalTable.setHeaderVisible(false); 587 588 addCommandSupport(fProposalTable); 589 } 590 591 598 int getMinimalHeight() { 599 int height= 0; 600 if (Helper.okToUse(fProposalTable)) { 601 int items= fProposalTable.getItemHeight() * 10; 602 Rectangle trim= fProposalTable.computeTrim(0, 0, SWT.DEFAULT, items); 603 height= trim.height; 604 } 605 if (Helper.okToUse(fMessageText)) 606 height+= fMessageText.getSize().y + 1; 607 return height; 608 } 609 610 616 private void addCommandSupport(final Control control) { 617 final KeySequence commandSequence= fContentAssistant.getTriggerSequence(); 618 if (commandSequence != null && !commandSequence.isEmpty() && fContentAssistant.isRepeatedInvocationMode()) { 619 control.addFocusListener(new FocusListener() { 620 private CommandKeyListener fCommandKeyListener; 621 public void focusGained(FocusEvent e) { 622 if (Helper.okToUse(control)) { 623 if (fCommandKeyListener == null) { 624 fCommandKeyListener= new CommandKeyListener(commandSequence); 625 fProposalTable.addKeyListener(fCommandKeyListener); 626 } 627 } 628 } 629 public void focusLost(FocusEvent e) { 630 if (fCommandKeyListener != null) { 631 control.removeKeyListener(fCommandKeyListener); 632 fCommandKeyListener= null; 633 } 634 } 635 }); 636 } 637 } 638 639 646 private Color getBackgroundColor(Control control) { 647 Color c= fContentAssistant.getProposalSelectorBackground(); 648 if (c == null) 649 c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND); 650 return c; 651 } 652 653 660 private Color getForegroundColor(Control control) { 661 Color c= fContentAssistant.getProposalSelectorForeground(); 662 if (c == null) 663 c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND); 664 return c; 665 } 666 667 672 private void createMessageText() { 673 if (fMessageText == null) { 674 fMessageText= new Label(fProposalShell, SWT.RIGHT); 675 GridData textData= new GridData(SWT.FILL, SWT.BOTTOM, true, false); 676 fMessageText.setLayoutData(textData); 677 fMessageText.setText(fContentAssistant.getStatusMessage() + " "); if (fMessageTextFont == null) { 679 Font font= fMessageText.getFont(); 680 Display display= fProposalShell.getDisplay(); 681 FontData[] fontDatas= font.getFontData(); 682 for (int i= 0; i < fontDatas.length; i++) 683 fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10); 684 fMessageTextFont= new Font(display, fontDatas); 685 } 686 fMessageText.setFont(fMessageTextFont); 687 fMessageText.setBackground(getBackgroundColor(fProposalShell)); 688 fMessageText.setForeground(getForegroundColor(fProposalShell)); 689 690 if (fContentAssistant.isRepeatedInvocationMode()) { 691 fMessageText.setCursor(fProposalShell.getDisplay().getSystemCursor(SWT.CURSOR_HAND)); 692 fMessageText.addMouseListener(new MouseAdapter() { 693 public void mouseUp(MouseEvent e) { 694 fLastCompletionOffset= fFilterOffset; 695 fProposalTable.setFocus(); 696 handleRepeatedInvocation(); 697 } 698 699 public void mouseDown(MouseEvent e) { 700 } 701 }); 702 } 703 } 704 } 705 706 709 private void handleSetData(Event event) { 710 TableItem item= (TableItem) event.item; 711 int index= fProposalTable.indexOf(item); 712 713 if (0 <= index && index < fFilteredProposals.length) { 714 ICompletionProposal current= fFilteredProposals[index]; 715 716 item.setText(current.getDisplayString()); 717 item.setImage(current.getImage()); 718 item.setData(current); 719 } else { 720 } 722 } 723 724 730 private ICompletionProposal getSelectedProposal() { 731 734 if (fIsFilterPending) 735 fFilterRunnable.run(); 736 737 if (!Helper.okToUse(fProposalTable)) 739 return null; 740 741 int i= fProposalTable.getSelectionIndex(); 742 if (fFilteredProposals == null || i < 0 || i >= fFilteredProposals.length) 743 return null; 744 return fFilteredProposals[i]; 745 } 746 747 753 private void insertSelectedProposalWithMask(int stateMask) { 754 ICompletionProposal p= getSelectedProposal(); 755 hide(); 756 if (p != null) 757 insertProposal(p, (char) 0, stateMask, fContentAssistSubjectControlAdapter.getSelectedRange().x); 758 } 759 760 770 private void insertProposal(ICompletionProposal p, char trigger, int stateMask, final int offset) { 771 772 fInserting= true; 773 IRewriteTarget target= null; 774 IEditingSupport helper= new IEditingSupport() { 775 776 public boolean isOriginator(DocumentEvent event, IRegion focus) { 777 return focus.getOffset() <= offset && focus.getOffset() + focus.getLength() >= offset; 778 } 779 780 public boolean ownsFocusShell() { 781 return false; 782 } 783 784 }; 785 786 try { 787 788 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); 789 790 if (fViewer instanceof ITextViewerExtension) { 791 ITextViewerExtension extension= (ITextViewerExtension) fViewer; 792 target= extension.getRewriteTarget(); 793 } 794 795 if (target != null) 796 target.beginCompoundChange(); 797 798 if (fViewer instanceof IEditingSupportRegistry) { 799 IEditingSupportRegistry registry= (IEditingSupportRegistry) fViewer; 800 registry.register(helper); 801 } 802 803 804 if (p instanceof ICompletionProposalExtension2 && fViewer != null) { 805 ICompletionProposalExtension2 e= (ICompletionProposalExtension2) p; 806 e.apply(fViewer, trigger, stateMask, offset); 807 } else if (p instanceof ICompletionProposalExtension) { 808 ICompletionProposalExtension e= (ICompletionProposalExtension) p; 809 e.apply(document, trigger, offset); 810 } else { 811 p.apply(document); 812 } 813 814 Point selection= p.getSelection(document); 815 if (selection != null) { 816 fContentAssistSubjectControlAdapter.setSelectedRange(selection.x, selection.y); 817 fContentAssistSubjectControlAdapter.revealRange(selection.x, selection.y); 818 } 819 820 IContextInformation info= p.getContextInformation(); 821 if (info != null) { 822 823 int contextInformationOffset; 824 if (p instanceof ICompletionProposalExtension) { 825 ICompletionProposalExtension e= (ICompletionProposalExtension) p; 826 contextInformationOffset= e.getContextInformationPosition(); 827 } else { 828 if (selection == null) 829 selection= fContentAssistSubjectControlAdapter.getSelectedRange(); 830 contextInformationOffset= selection.x + selection.y; 831 } 832 833 fContentAssistant.showContextInformation(info, contextInformationOffset); 834 } else 835 fContentAssistant.showContextInformation(null, -1); 836 837 838 } finally { 839 if (target != null) 840 target.endCompoundChange(); 841 842 if (fViewer instanceof IEditingSupportRegistry) { 843 IEditingSupportRegistry registry= (IEditingSupportRegistry) fViewer; 844 registry.unregister(helper); 845 } 846 fInserting= false; 847 } 848 } 849 850 855 public boolean hasFocus() { 856 if (Helper.okToUse(fProposalShell)) 857 return (fProposalShell.isFocusControl() || fProposalTable.isFocusControl()); 858 859 return false; 860 } 861 862 865 public void hide() { 866 867 unregister(); 868 869 if (fViewer instanceof IEditingSupportRegistry) { 870 IEditingSupportRegistry registry= (IEditingSupportRegistry) fViewer; 871 registry.unregister(fFocusHelper); 872 } 873 874 if (Helper.okToUse(fProposalShell)) { 875 876 fContentAssistant.removeContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR); 877 878 fPopupCloser.uninstall(); 879 fProposalShell.setVisible(false); 880 fProposalShell.dispose(); 881 fProposalShell= null; 882 } 883 884 if (fMessageTextFont != null) { 885 fMessageTextFont.dispose(); 886 fMessageTextFont= null; 887 } 888 889 if (fMessageText != null) { 890 fMessageText= null; 891 } 892 893 fEmptyMessage= null; 894 895 fLastCompletionOffset= -1; 896 897 fContentAssistant.fireSessionEndEvent(); 898 } 899 900 905 private void unregister() { 906 if (fDocumentListener != null) { 907 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); 908 if (document != null) 909 document.removeDocumentListener(fDocumentListener); 910 fDocumentListener= null; 911 } 912 fDocumentEvents.clear(); 913 914 if (fKeyListener != null && fContentAssistSubjectControlAdapter.getControl() != null && !fContentAssistSubjectControlAdapter.getControl().isDisposed()) { 915 fContentAssistSubjectControlAdapter.removeKeyListener(fKeyListener); 916 fKeyListener= null; 917 } 918 919 if (fLastProposal != null) { 920 if (fLastProposal instanceof ICompletionProposalExtension2 && fViewer != null) { 921 ICompletionProposalExtension2 extension= (ICompletionProposalExtension2) fLastProposal; 922 extension.unselected(fViewer); 923 } 924 fLastProposal= null; 925 } 926 927 fFilteredProposals= null; 928 fComputedProposals= null; 929 930 fContentAssistant.possibleCompletionsClosed(); 931 } 932 933 938 public boolean isActive() { 939 return fProposalShell != null && !fProposalShell.isDisposed(); 940 } 941 942 949 private void setProposals(ICompletionProposal[] proposals, boolean isFilteredSubset) { 950 ICompletionProposal[] oldProposals= fFilteredProposals; 951 ICompletionProposal oldProposal= getSelectedProposal(); if (oldProposals != fFilteredProposals) return; 954 955 if (Helper.okToUse(fProposalTable)) { 956 if (oldProposal instanceof ICompletionProposalExtension2 && fViewer != null) 957 ((ICompletionProposalExtension2) oldProposal).unselected(fViewer); 958 959 if (proposals == null || proposals.length == 0) { 960 fEmptyProposal.fOffset= fFilterOffset; 961 fEmptyProposal.fDisplayString= fEmptyMessage != null ? fEmptyMessage : JFaceTextMessages.getString("CompletionProposalPopup.no_proposals"); proposals= new ICompletionProposal[] { fEmptyProposal }; 963 } 964 965 fFilteredProposals= proposals; 966 final int newLen= proposals.length; 967 if (USE_VIRTUAL) { 968 fProposalTable.setItemCount(newLen); 969 fProposalTable.clearAll(); 970 } else { 971 fProposalTable.setRedraw(false); 972 fProposalTable.setItemCount(newLen); 973 TableItem[] items= fProposalTable.getItems(); 974 for (int i= 0; i < items.length; i++) { 975 TableItem item= items[i]; 976 ICompletionProposal proposal= proposals[i]; 977 item.setText(proposal.getDisplayString()); 978 item.setImage(proposal.getImage()); 979 item.setData(proposal); 980 } 981 fProposalTable.setRedraw(true); 982 } 983 984 Point currentLocation= fProposalShell.getLocation(); 985 Point newLocation= getLocation(); 986 if ((newLocation.x < currentLocation.x && newLocation.y == currentLocation.y) || newLocation.y < currentLocation.y) 987 fProposalShell.setLocation(newLocation); 988 989 selectProposal(0, false); 990 } 991 } 992 993 998 private Point getLocation() { 999 int caret= fContentAssistSubjectControlAdapter.getCaretOffset(); 1000 Rectangle location= fContentAssistant.getLayoutManager().computeBoundsBelowAbove(fProposalShell, fSize == null ? fProposalShell.getSize() : fSize, caret, this); 1001 return Geometry.getLocation(location); 1002 } 1003 1004 1010 Point getSize() { 1011 return fSize; 1012 } 1013 1014 1018 private void displayProposals() { 1019 1020 if (!Helper.okToUse(fProposalShell) || !Helper.okToUse(fProposalTable)) 1021 return; 1022 1023 if (fContentAssistant.addContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR)) { 1024 1025 ensureDocumentListenerInstalled(); 1026 1027 if (fFocusHelper == null) { 1028 fFocusHelper= new IEditingSupport() { 1029 1030 public boolean isOriginator(DocumentEvent event, IRegion focus) { 1031 return false; } 1033 1034 public boolean ownsFocusShell() { 1035 return true; 1036 } 1037 1038 }; 1039 } 1040 if (fViewer instanceof IEditingSupportRegistry) { 1041 IEditingSupportRegistry registry= (IEditingSupportRegistry) fViewer; 1042 registry.register(fFocusHelper); 1043 } 1044 1045 1046 1053 fProposalShell.setVisible(true); if (!fContentAssistSubjectControlAdapter.supportsVerifyKeyListener() && Helper.okToUse(fProposalShell)) 1056 fProposalShell.setFocus(); 1058 if (fAdditionalInfoController != null && Helper.okToUse(fProposalTable)) { 1059 fAdditionalInfoController.install(fProposalTable); 1060 fAdditionalInfoController.handleTableSelectionChanged(); 1061 } 1062 } else 1063 hide(); 1064 } 1065 1066 1071 private void ensureDocumentListenerInstalled() { 1072 if (fDocumentListener == null) { 1073 fDocumentListener= new IDocumentListener() { 1074 public void documentAboutToBeChanged(DocumentEvent event) { 1075 if (!fInserting) 1076 fDocumentEvents.add(event); 1077 } 1078 1079 public void documentChanged(DocumentEvent event) { 1080 if (!fInserting) 1081 filterProposals(); 1082 } 1083 }; 1084 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); 1085 if (document != null) 1086 document.addDocumentListener(fDocumentListener); 1087 } 1088 } 1089 1090 1093 public boolean verifyKey(VerifyEvent e) { 1094 if (!Helper.okToUse(fProposalShell)) 1095 return true; 1096 1097 char key= e.character; 1098 if (key == 0) { 1099 int newSelection= fProposalTable.getSelectionIndex(); 1100 int visibleRows= (fProposalTable.getSize().y / fProposalTable.getItemHeight()) - 1; 1101 boolean smartToggle= false; 1102 switch (e.keyCode) { 1103 1104 case SWT.ARROW_LEFT : 1105 case SWT.ARROW_RIGHT : 1106 filterProposals(); 1107 return true; 1108 1109 case SWT.ARROW_UP : 1110 newSelection -= 1; 1111 if (newSelection < 0) 1112 newSelection= fProposalTable.getItemCount() - 1; 1113 break; 1114 1115 case SWT.ARROW_DOWN : 1116 newSelection += 1; 1117 if (newSelection > fProposalTable.getItemCount() - 1) 1118 newSelection= 0; 1119 break; 1120 1121 case SWT.PAGE_DOWN : 1122 newSelection += visibleRows; 1123 if (newSelection >= fProposalTable.getItemCount()) 1124 newSelection= fProposalTable.getItemCount() - 1; 1125 break; 1126 1127 case SWT.PAGE_UP : 1128 newSelection -= visibleRows; 1129 if (newSelection < 0) 1130 newSelection= 0; 1131 break; 1132 1133 case SWT.HOME : 1134 newSelection= 0; 1135 break; 1136 1137 case SWT.END : 1138 newSelection= fProposalTable.getItemCount() - 1; 1139 break; 1140 1141 default : 1142 if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4) 1143 hide(); 1144 return true; 1145 } 1146 1147 selectProposal(newSelection, smartToggle); 1148 1149 e.doit= false; 1150 return false; 1151 1152 } 1153 1154 switch (key) { 1156 case 0x1B: e.doit= false; 1158 hide(); 1159 break; 1160 1161 case '\n': case '\r': e.doit= false; 1164 insertSelectedProposalWithMask(e.stateMask); 1165 break; 1166 1167 case '\t': 1168 e.doit= false; 1169 fProposalShell.setFocus(); 1170 return false; 1171 1172 default: 1173 ICompletionProposal p= getSelectedProposal(); 1174 if (p instanceof ICompletionProposalExtension) { 1175 ICompletionProposalExtension t= (ICompletionProposalExtension) p; 1176 char[] triggers= t.getTriggerCharacters(); 1177 if (contains(triggers, key)) { 1178 e.doit= false; 1179 hide(); 1180 insertProposal(p, key, e.stateMask, fContentAssistSubjectControlAdapter.getSelectedRange().x); 1181 } 1182 } 1183 } 1184 1185 return true; 1186 } 1187 1188 1196 private void selectProposal(int index, boolean smartToggle) { 1197 1198 ICompletionProposal oldProposal= getSelectedProposal(); 1199 if (oldProposal instanceof ICompletionProposalExtension2 && fViewer != null) 1200 ((ICompletionProposalExtension2) oldProposal).unselected(fViewer); 1201 1202 if (fFilteredProposals == null) { 1203 fireSelectionEvent(null, smartToggle); 1204 return; 1205 } 1206 1207 ICompletionProposal proposal= fFilteredProposals[index]; 1208 if (proposal instanceof ICompletionProposalExtension2 && fViewer != null) 1209 ((ICompletionProposalExtension2) proposal).selected(fViewer, smartToggle); 1210 1211 fireSelectionEvent(proposal, smartToggle); 1212 1213 fLastProposal= proposal; 1214 1215 fProposalTable.setSelection(index); 1216 fProposalTable.showSelection(); 1217 if (fAdditionalInfoController != null) 1218 fAdditionalInfoController.handleTableSelectionChanged(); 1219 } 1220 1221 1228 private void fireSelectionEvent(ICompletionProposal proposal, boolean smartToggle) { 1229 fContentAssistant.fireSelectionEvent(proposal, smartToggle); 1230 } 1231 1232 1241 private boolean contains(char[] characters, char c) { 1242 1243 if (characters == null) 1244 return false; 1245 1246 for (int i= 0; i < characters.length; i++) { 1247 if (c == characters[i]) 1248 return true; 1249 } 1250 1251 return false; 1252 } 1253 1254 1257 public void processEvent(VerifyEvent e) { 1258 } 1259 1260 1264 private void filterProposals() { 1265 if (!fIsFilterPending) { 1266 fIsFilterPending= true; 1267 Control control= fContentAssistSubjectControlAdapter.getControl(); 1268 control.getDisplay().asyncExec(fFilterRunnable); 1269 } 1270 } 1271 1272 1281 private ICompletionProposal[] computeFilteredProposals(int offset, DocumentEvent event) { 1282 1283 if (offset == fInvocationOffset && event == null) { 1284 fIsFilteredSubset= false; 1285 return fComputedProposals; 1286 } 1287 1288 if (offset < fInvocationOffset) { 1289 fIsFilteredSubset= false; 1290 fInvocationOffset= offset; 1291 fComputedProposals= computeProposals(fInvocationOffset); 1292 return fComputedProposals; 1293 } 1294 1295 ICompletionProposal[] proposals; 1296 if (offset < fFilterOffset) { 1297 proposals= fComputedProposals; 1298 fIsFilteredSubset= false; 1299 } else { 1300 proposals= fFilteredProposals; 1301 fIsFilteredSubset= true; 1302 } 1303 1304 if (proposals == null) { 1305 fIsFilteredSubset= false; 1306 return null; 1307 } 1308 1309 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); 1310 int length= proposals.length; 1311 List filtered= new ArrayList (length); 1312 for (int i= 0; i < length; i++) { 1313 1314 if (proposals[i] instanceof ICompletionProposalExtension2) { 1315 1316 ICompletionProposalExtension2 p= (ICompletionProposalExtension2) proposals[i]; 1317 if (p.validate(document, offset, event)) 1318 filtered.add(p); 1319 1320 } else if (proposals[i] instanceof ICompletionProposalExtension) { 1321 1322 ICompletionProposalExtension p= (ICompletionProposalExtension) proposals[i]; 1323 if (p.isValidFor(document, offset)) 1324 filtered.add(p); 1325 1326 } else { 1327 fIsFilteredSubset= false; 1329 fInvocationOffset= offset; 1330 fComputedProposals= computeProposals(fInvocationOffset); 1331 return fComputedProposals; 1332 } 1333 } 1334 1335 return (ICompletionProposal[]) filtered.toArray(new ICompletionProposal[filtered.size()]); 1336 } 1337 1338 1343 public void setFocus() { 1344 if (Helper.okToUse(fProposalShell)) { 1345 fProposalShell.setFocus(); 1346 } 1347 } 1348 1349 1358 private boolean canAutoInsert(ICompletionProposal proposal) { 1359 if (fContentAssistant.isAutoInserting()) { 1360 if (proposal instanceof ICompletionProposalExtension4) { 1361 ICompletionProposalExtension4 ext= (ICompletionProposalExtension4) proposal; 1362 return ext.isAutoInsertable(); 1363 } 1364 return true; } 1366 return false; 1367 } 1368 1369 1376 public String incrementalComplete() { 1377 if (Helper.okToUse(fProposalShell) && fFilteredProposals != null) { 1378 if (fLastCompletionOffset == fFilterOffset) { 1379 handleRepeatedInvocation(); 1380 } else { 1381 fLastCompletionOffset= fFilterOffset; 1382 completeCommonPrefix(); 1383 } 1384 } else { 1385 final Control control= fContentAssistSubjectControlAdapter.getControl(); 1386 1387 if (fKeyListener == null) 1388 fKeyListener= new ProposalSelectionListener(); 1389 1390 if (!Helper.okToUse(fProposalShell) && !control.isDisposed()) 1391 fContentAssistSubjectControlAdapter.addKeyListener(fKeyListener); 1392 1393 BusyIndicator.showWhile(control.getDisplay(), new Runnable () { 1394 public void run() { 1395 1396 fInvocationOffset= fContentAssistSubjectControlAdapter.getSelectedRange().x; 1397 fFilterOffset= fInvocationOffset; 1398 fLastCompletionOffset= fFilterOffset; 1399 fFilteredProposals= computeProposals(fInvocationOffset); 1400 1401 int count= (fFilteredProposals == null ? 0 : fFilteredProposals.length); 1402 if (count == 0 && hideWhenNoProposals(false)) 1403 return; 1404 1405 if (count == 1 && canAutoInsert(fFilteredProposals[0])) { 1406 insertProposal(fFilteredProposals[0], (char) 0, 0, fInvocationOffset); 1407 hide(); 1408 } else { 1409 ensureDocumentListenerInstalled(); 1410 if (count > 0 && completeCommonPrefix()) 1411 hide(); 1412 else { 1413 fComputedProposals= fFilteredProposals; 1414 createProposalSelector(); 1415 setProposals(fComputedProposals, false); 1416 displayProposals(); 1417 } 1418 } 1419 } 1420 }); 1421 } 1422 return getErrorMessage(); 1423 } 1424 1425 1435 private boolean completeCommonPrefix() { 1436 1437 if (fFilteredProposals.length == 1) { 1439 if (canAutoInsert(fFilteredProposals[0])) { 1440 insertProposal(fFilteredProposals[0], (char) 0, 0, fFilterOffset); 1441 hide(); 1442 return true; 1443 } 1444 return false; 1445 } 1446 1447 IDocument document= fContentAssistSubjectControlAdapter.getDocument(); 1449 1450 StringBuffer rightCasePostfix= null; 1452 List rightCase= new ArrayList (); 1453 1454 boolean checkWrongCase= true; 1458 CharSequence wrongCasePrefix= null; 1461 int wrongCasePrefixStart= 0; 1462 StringBuffer wrongCasePostfix= null; 1464 List wrongCase= new ArrayList (); 1465 1466 for (int i= 0; i < fFilteredProposals.length; i++) { 1467 ICompletionProposal proposal= fFilteredProposals[i]; 1468 CharSequence insertion= getPrefixCompletion(proposal); 1469 int start= getPrefixCompletionOffset(proposal); 1470 try { 1471 int prefixLength= fFilterOffset - start; 1472 int relativeCompletionOffset= Math.min(insertion.length(), prefixLength); 1473 String prefix= document.get(start, prefixLength); 1474 if (insertion.toString().startsWith(prefix)) { 1475 checkWrongCase= false; 1476 rightCase.add(proposal); 1477 CharSequence newPostfix= insertion.subSequence(relativeCompletionOffset, insertion.length()); 1478 if (rightCasePostfix == null) 1479 rightCasePostfix= new StringBuffer (newPostfix.toString()); 1480 else 1481 truncatePostfix(rightCasePostfix, newPostfix); 1482 } else if (checkWrongCase) { 1483 CharSequence newPrefix= insertion.subSequence(0, relativeCompletionOffset); 1484 if (isPrefixCompatible(wrongCasePrefix, wrongCasePrefixStart, newPrefix, start, document)) { 1485 wrongCasePrefix= newPrefix; 1486 wrongCasePrefixStart= start; 1487 CharSequence newPostfix= insertion.subSequence(relativeCompletionOffset, insertion.length()); 1488 if (wrongCasePostfix == null) 1489 wrongCasePostfix= new StringBuffer (newPostfix.toString()); 1490 else 1491 truncatePostfix(wrongCasePostfix, newPostfix); 1492 wrongCase.add(proposal); 1493 } else { 1494 checkWrongCase= false; 1495 } 1496 } 1497 } catch (BadLocationException e2) { 1498 return false; 1500 } 1501 1502 if (rightCasePostfix != null && rightCasePostfix.length() == 0 && rightCase.size() > 1) 1503 return false; 1504 } 1505 1506 1508 if (rightCase.size() == 1) { 1509 ICompletionProposal proposal= (ICompletionProposal) rightCase.get(0); 1510 if (canAutoInsert(proposal) && rightCasePostfix.length() > 0) { 1511 insertProposal(proposal, (char) 0, 0, fInvocationOffset); 1512 hide(); 1513 return true; 1514 } 1515 return false; 1516 } else if (checkWrongCase && wrongCase.size() == 1) { 1517 ICompletionProposal proposal= (ICompletionProposal) wrongCase.get(0); 1518 if (canAutoInsert(proposal)) { 1519 insertProposal(proposal, (char) 0, 0, fInvocationOffset); 1520 hide(); 1521 return true; 1522 } 1523 return false; 1524 } 1525 1526 1528 CharSequence prefix; 1529 if (checkWrongCase) 1530 prefix= wrongCasePrefix; 1531 else 1532 prefix= ""; 1534 CharSequence postfix; 1535 if (checkWrongCase) 1536 postfix= wrongCasePostfix; 1537 else 1538 postfix= rightCasePostfix; 1539 1540 if (prefix == null || postfix == null) 1541 return false; 1542 1543 try { 1544 int to= Math.min(document.getLength(), fFilterOffset + postfix.length()); 1546 StringBuffer inDocument= new StringBuffer (document.get(fFilterOffset, to - fFilterOffset)); 1547 truncatePostfix(inDocument, postfix); 1548 1549 document.replace(fFilterOffset - prefix.length(), prefix.length() + inDocument.length(), prefix.toString() + postfix.toString()); 1551 1552 fContentAssistSubjectControlAdapter.setSelectedRange(fFilterOffset + postfix.length(), 0); 1553 fContentAssistSubjectControlAdapter.revealRange(fFilterOffset + postfix.length(), 0); 1554 fFilterOffset+= postfix.length(); 1555 fLastCompletionOffset= fFilterOffset; 1556 1557 return false; 1558 } catch (BadLocationException e) { 1559 return false; 1561 } 1562 } 1563 1564 1567 private boolean isPrefixCompatible(CharSequence oneSequence, int oneOffset, CharSequence twoSequence, int twoOffset, IDocument document) throws BadLocationException { 1568 if (oneSequence == null || twoSequence == null) 1569 return true; 1570 1571 int min= Math.min(oneOffset, twoOffset); 1572 int oneEnd= oneOffset + oneSequence.length(); 1573 int twoEnd= twoOffset + twoSequence.length(); 1574 1575 String one= document.get(oneOffset, min - oneOffset) + oneSequence + document.get(oneEnd, Math.min(fFilterOffset, fFilterOffset - oneEnd)); 1576 String two= document.get(twoOffset, min - twoOffset) + twoSequence + document.get(twoEnd, Math.min(fFilterOffset, fFilterOffset - twoEnd)); 1577 1578 return one.equals(two); 1579 } 1580 1581 1588 private void truncatePostfix(StringBuffer buffer, CharSequence sequence) { 1589 int min= Math.min(buffer.length(), sequence.length()); 1591 for (int c= 0; c < min; c++) { 1592 if (sequence.charAt(c) != buffer.charAt(c)) { 1593 buffer.delete(c, buffer.length()); 1594 return; 1595 } 1596 } 1597 1598 buffer.delete(min, buffer.length()); 1600 } 1601 1602 1612 private int getPrefixCompletionOffset(ICompletionProposal proposal) { 1613 if (proposal instanceof ICompletionProposalExtension3) 1614 return ((ICompletionProposalExtension3) proposal).getPrefixCompletionStart(fContentAssistSubjectControlAdapter.getDocument(), fFilterOffset); 1615 return fInvocationOffset; 1616 } 1617 1618 1628 private CharSequence getPrefixCompletion(ICompletionProposal proposal) { 1629 CharSequence insertion= null; 1630 if (proposal instanceof ICompletionProposalExtension3) 1631 insertion= ((ICompletionProposalExtension3) proposal).getPrefixCompletionText(fContentAssistSubjectControlAdapter.getDocument(), fFilterOffset); 1632 1633 if (insertion == null) 1634 insertion= proposal.getDisplayString(); 1635 1636 return insertion; 1637 } 1638 1639 1646 void setMessage(String message) { 1647 Assert.isNotNull(message); 1648 if (isActive() && fMessageText != null) 1649 fMessageText.setText(message + " "); } 1651 1652 1659 void setEmptyMessage(String message) { 1660 Assert.isNotNull(message); 1661 fEmptyMessage= message; 1662 } 1663 1664 1670 public void setStatusLineVisible(boolean show) { 1671 if (!isActive() || show == (fMessageText != null)) 1672 return; 1674 if (show) { 1675 createMessageText(); 1676 } else { 1677 fMessageText.dispose(); 1678 fMessageText= null; 1679 } 1680 fProposalShell.layout(); 1681 } 1682 1683 1689 void switchedPositionToAbove(boolean above) { 1690 if (fAdditionalInfoController != null) { 1691 fAdditionalInfoController.setFallbackAnchors(new Anchor[] { 1692 AbstractInformationControlManager.ANCHOR_RIGHT, 1693 AbstractInformationControlManager.ANCHOR_LEFT, 1694 above ? AbstractInformationControlManager.ANCHOR_TOP : AbstractInformationControlManager.ANCHOR_BOTTOM 1695 }); 1696 } 1697 } 1698} 1699 | Popular Tags |