1 12 13 package org.eclipse.jface.internal.text.link.contentassist; 14 15 16 import java.util.ArrayList ; 17 import java.util.List ; 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 68 class CompletionProposalPopup2 implements IContentAssistListener2 { 69 70 71 private ITextViewer fViewer; 72 73 private ContentAssistant2 fContentAssistant; 74 75 private AdditionalInfoController2 fAdditionalInfoController; 76 77 private PopupCloser2 fPopupCloser= new PopupCloser2(); 78 79 private Shell fProposalShell; 80 81 private Table fProposalTable; 82 83 private boolean fInserting= false; 84 85 private KeyListener fKeyListener; 86 87 private List fDocumentEvents= new ArrayList (); 88 89 private IDocumentListener fDocumentListener; 90 91 private long fInvocationCounter= 0; 92 93 private ICompletionProposal[] fFilteredProposals; 94 95 private ICompletionProposal[] fComputedProposals; 96 97 private int fInvocationOffset; 98 99 private int fFilterOffset; 100 101 private String fLineDelimiter; 102 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 141 public CompletionProposalPopup2(ContentAssistant2 contentAssistant, ITextViewer viewer, AdditionalInfoController2 infoController) { 142 fContentAssistant= contentAssistant; 143 fViewer= viewer; 144 fAdditionalInfoController= infoController; 145 } 146 147 154 public String 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 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 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 191 fInvocationOffset= fViewer.getSelectedRange().x; 192 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 222 return getErrorMessage(); 223 } 224 225 232 private ICompletionProposal[] computeProposals(int offset) { 233 return fContentAssistant.computeCompletionProposals(fViewer, offset); 234 } 235 236 241 private String getErrorMessage() { 242 return fContentAssistant.getErrorMessage(); 243 } 244 245 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 fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL); 256 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 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 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(); } 317 }); 318 319 fProposalTable.setHeaderVisible(false); 320 fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant2.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset()); 321 } 322 323 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 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 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 432 public boolean hasFocus() { 433 if (Helper2.okToUse(fProposalShell)) 434 return (fProposalShell.isFocusControl() || fProposalTable.isFocusControl()); 435 436 return false; 437 } 438 439 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 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 498 public boolean isActive() { 499 return fProposalShell != null && !fProposalShell.isDisposed(); 500 } 501 502 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 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 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 return result; 588 } 589 590 private boolean validateProposal(IDocument document, ICompletionProposal p, int offset, DocumentEvent event) { 591 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 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 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 if (!Helper2.okToUse(fProposalShell)) 655 return; 656 657 658 if (fAdditionalInfoController != null) { 659 fAdditionalInfoController.install(fProposalTable); 660 fAdditionalInfoController.handleTableSelectionChanged(); 661 } 662 } 663 } 664 665 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 switch (key) { 731 case 0x1B: e.doit= false; 733 hide(); 734 break; 735 736 case '\n': case '\r': if ((e.stateMask & SWT.CTRL) == 0) { 739 e.doit= false; 740 selectProposalWithMask(e.stateMask); 741 } 742 break; 743 744 case '\t': 747 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 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 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 822 public void processEvent(VerifyEvent e) { 823 } 824 825 829 private void filterProposals() { 830 ++ fInvocationCounter; 831 Control control= fViewer.getTextWidget(); 832 control.getDisplay().asyncExec(new Runnable () { 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 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 filtered= new ArrayList (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 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 919 public void setFocus() { 920 if (Helper2.okToUse(fProposalShell)) 921 fProposalShell.setFocus(); 922 } 923 } 924 | Popular Tags |