1 11 package org.eclipse.jface.text.contentassist; 12 13 import java.util.Iterator ; 14 import java.util.Stack ; 15 16 import org.eclipse.swt.SWT; 17 import org.eclipse.swt.custom.BusyIndicator; 18 import org.eclipse.swt.custom.StyledText; 19 import org.eclipse.swt.events.KeyEvent; 20 import org.eclipse.swt.events.SelectionAdapter; 21 import org.eclipse.swt.events.SelectionEvent; 22 import org.eclipse.swt.events.SelectionListener; 23 import org.eclipse.swt.events.VerifyEvent; 24 import org.eclipse.swt.graphics.Color; 25 import org.eclipse.swt.graphics.Point; 26 import org.eclipse.swt.graphics.Rectangle; 27 import org.eclipse.swt.layout.GridData; 28 import org.eclipse.swt.layout.GridLayout; 29 import org.eclipse.swt.widgets.Control; 30 import org.eclipse.swt.widgets.Display; 31 import org.eclipse.swt.widgets.Shell; 32 import org.eclipse.swt.widgets.Table; 33 import org.eclipse.swt.widgets.TableItem; 34 35 import org.eclipse.jface.contentassist.IContentAssistSubjectControl; 36 37 import org.eclipse.jface.text.ITextViewer; 38 import org.eclipse.jface.text.TextPresentation; 39 40 41 56 class ContextInformationPopup implements IContentAssistListener { 57 58 59 64 static class ContextFrame { 65 66 final int fBeginOffset; 67 final int fOffset; 68 final int fVisibleOffset; 69 final IContextInformation fInformation; 70 final IContextInformationValidator fValidator; 71 final IContextInformationPresenter fPresenter; 72 73 76 public ContextFrame(IContextInformation information, int beginOffset, int offset, int visibleOffset, IContextInformationValidator validator, IContextInformationPresenter presenter) { 77 fInformation = information; 78 fBeginOffset = beginOffset; 79 fOffset = offset; 80 fVisibleOffset = visibleOffset; 81 fValidator = validator; 82 fPresenter = presenter; 83 } 84 85 89 public boolean equals(Object obj) { 90 if (obj instanceof ContextFrame) { 91 ContextFrame frame= (ContextFrame) obj; 92 return fInformation.equals(frame.fInformation) && fBeginOffset == frame.fBeginOffset; 93 } 94 return super.equals(obj); 95 } 96 97 101 public int hashCode() { 102 return (fInformation.hashCode() << 16) | fBeginOffset; 103 } 104 } 105 106 private ITextViewer fViewer; 107 private ContentAssistant fContentAssistant; 108 109 private PopupCloser fPopupCloser= new PopupCloser(); 110 private Shell fContextSelectorShell; 111 private Table fContextSelectorTable; 112 private IContextInformation[] fContextSelectorInput; 113 private String fLineDelimiter= null; 114 115 private Shell fContextInfoPopup; 116 private StyledText fContextInfoText; 117 private TextPresentation fTextPresentation; 118 119 private Stack fContextFrameStack= new Stack (); 120 125 private IContentAssistSubjectControl fContentAssistSubjectControl; 126 131 private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter; 132 133 139 private SelectionListener fTextWidgetSelectionListener; 140 141 147 private ContextFrame fLastContext= null; 148 149 155 public ContextInformationPopup(ContentAssistant contentAssistant, ITextViewer viewer) { 156 fContentAssistant= contentAssistant; 157 fViewer= viewer; 158 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer); 159 } 160 161 168 public ContextInformationPopup(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl) { 169 fContentAssistant= contentAssistant; 170 fContentAssistSubjectControl= contentAssistSubjectControl; 171 fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl); 172 } 173 174 180 public String showContextProposals(final boolean autoActivated) { 181 final Control control= fContentAssistSubjectControlAdapter.getControl(); 182 BusyIndicator.showWhile(control.getDisplay(), new Runnable () { 183 public void run() { 184 185 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x; 186 187 IContextInformation[] contexts= computeContextInformation(offset); 188 int count = (contexts == null ? 0 : contexts.length); 189 if (count == 1) { 190 191 ContextFrame frame= createContextFrame(contexts[0], offset); 192 if (isDuplicate(frame)) 193 validateContextInformation(); 194 else 195 internalShowContextInfo(frame); 197 198 } else if (count > 0) { 199 200 for (int i= 0; i < contexts.length; i++) { 204 IContextInformation info= contexts[i]; 205 ContextFrame frame= createContextFrame(info, offset); 206 207 if (isDuplicate(frame)) { 209 validateContextInformation(); 210 return; 211 } 212 213 if (isLastFrame(frame)) { 214 internalShowContextInfo(frame); 215 return; 216 } 217 218 for (Iterator it= fContextFrameStack.iterator(); it.hasNext(); ) { 220 ContextFrame stackFrame= (ContextFrame) it.next(); 221 if (stackFrame.equals(frame)) { 222 validateContextInformation(); 223 return; 224 } 225 } 226 } 227 228 231 if (fLineDelimiter == null) 232 fLineDelimiter= fContentAssistSubjectControlAdapter.getLineDelimiter(); 233 234 createContextSelector(); 235 setContexts(contexts); 236 displayContextSelector(); 237 } 238 } 239 }); 240 241 return getErrorMessage(); 242 } 243 244 251 public void showContextInformation(final IContextInformation info, final int offset) { 252 Control control= fContentAssistSubjectControlAdapter.getControl(); 253 BusyIndicator.showWhile(control.getDisplay(), new Runnable () { 254 public void run() { 255 if (info == null) 256 validateContextInformation(); 257 else { 258 ContextFrame frame= createContextFrame(info, offset); 259 if (isDuplicate(frame)) 260 validateContextInformation(); 261 else 262 internalShowContextInfo(frame); 263 hideContextSelector(); 264 } 265 } 266 }); 267 } 268 269 275 private void internalShowContextInfo(ContextFrame frame) { 276 if (frame != null) { 277 fContextFrameStack.push(frame); 278 if (fContextFrameStack.size() == 1) 279 fLastContext= null; 280 internalShowContextFrame(frame, fContextFrameStack.size() == 1); 281 validateContextInformation(); 282 } 283 } 284 285 293 private ContextFrame createContextFrame(IContextInformation information, int offset) { 294 IContextInformationValidator validator= fContentAssistSubjectControlAdapter.getContextInformationValidator(fContentAssistant, offset); 295 296 if (validator != null) { 297 int beginOffset= (information instanceof IContextInformationExtension) ? ((IContextInformationExtension) information).getContextInformationPosition() : offset; 298 if (beginOffset == -1) beginOffset= offset; 299 int visibleOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x - (offset - beginOffset); 300 IContextInformationPresenter presenter = fContentAssistSubjectControlAdapter.getContextInformationPresenter(fContentAssistant, offset); 301 return new ContextFrame(information, beginOffset, offset, visibleOffset, validator, presenter); 302 } 303 304 return null; 305 } 306 307 315 private boolean isDuplicate(ContextFrame frame) { 316 if (frame == null) 317 return false; 318 if (fContextFrameStack.isEmpty()) 319 return false; 320 ContextFrame top= (ContextFrame) fContextFrameStack.peek(); 322 return frame.equals(top); 323 } 324 325 333 private boolean isLastFrame(ContextFrame frame) { 334 return frame != null && frame.equals(fLastContext); 335 } 336 337 344 private void internalShowContextFrame(ContextFrame frame, boolean initial) { 345 346 fContentAssistSubjectControlAdapter.installValidator(frame); 347 348 if (frame.fPresenter != null) { 349 if (fTextPresentation == null) 350 fTextPresentation= new TextPresentation(); 351 fContentAssistSubjectControlAdapter.installContextInformationPresenter(frame); 352 frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation); 353 } 354 355 createContextInfoPopup(); 356 357 fContextInfoText.setText(frame.fInformation.getInformationDisplayString()); 358 if (fTextPresentation != null) 359 TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText); 360 resize(frame.fVisibleOffset); 361 362 if (initial) { 363 if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP)) { 364 if (fContentAssistSubjectControlAdapter.getControl() != null) { 365 fTextWidgetSelectionListener= new SelectionAdapter() { 366 369 public void widgetSelected(SelectionEvent e) { 370 validateContextInformation(); 371 }}; 372 fContentAssistSubjectControlAdapter.addSelectionListener(fTextWidgetSelectionListener); 373 } 374 fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset); 375 fContextInfoPopup.setVisible(true); 376 } 377 } else { 378 fContentAssistant.layout(ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset); 379 } 380 } 381 382 389 private IContextInformation[] computeContextInformation(int offset) { 390 return fContentAssistSubjectControlAdapter.computeContextInformation(fContentAssistant, offset); 391 } 392 393 398 private String getErrorMessage() { 399 return fContentAssistant.getErrorMessage(); 400 } 401 402 405 private void createContextInfoPopup() { 406 if (Helper.okToUse(fContextInfoPopup)) 407 return; 408 409 Control control= fContentAssistSubjectControlAdapter.getControl(); 410 Display display= control.getDisplay(); 411 412 fContextInfoPopup= new Shell(control.getShell(), SWT.NO_TRIM | SWT.ON_TOP); 413 fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); 414 415 fContextInfoText= new StyledText(fContextInfoPopup, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP); 416 417 Color c= fContentAssistant.getContextInformationPopupBackground(); 418 if (c == null) 419 c= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND); 420 fContextInfoText.setBackground(c); 421 422 c= fContentAssistant.getContextInformationPopupForeground(); 423 if (c == null) 424 c= display.getSystemColor(SWT.COLOR_INFO_FOREGROUND); 425 fContextInfoText.setForeground(c); 426 } 427 428 434 private void resize(int offset) { 435 Point size= fContextInfoText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); 436 final int TEXT_PAD= 0; 437 final int BORDER_PAD= 2; 438 final int PAD= TEXT_PAD + BORDER_PAD; 439 size.x += PAD; 440 Rectangle bounds= fContentAssistant.getLayoutManager().computeBoundsAboveBelow(fContextInfoPopup, size, offset); 441 if (bounds.width < size.x) 442 size= fContextInfoText.computeSize(bounds.width - PAD, SWT.DEFAULT, true); 444 445 size.x += TEXT_PAD; 446 fContextInfoText.setSize(size); 447 fContextInfoText.setLocation(1,1); 448 size.x += BORDER_PAD; 449 size.y += BORDER_PAD; 450 fContextInfoPopup.setSize(size); 451 } 452 453 456 private void hideContextInfoPopup() { 457 458 if (Helper.okToUse(fContextInfoPopup)) { 459 460 int size= fContextFrameStack.size(); 461 if (size > 0) { 462 fLastContext= (ContextFrame) fContextFrameStack.pop(); 463 -- size; 464 } 465 466 if (size > 0) { 467 ContextFrame current= (ContextFrame) fContextFrameStack.peek(); 468 internalShowContextFrame(current, false); 469 } else { 470 471 fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP); 472 473 if (fContentAssistSubjectControlAdapter.getControl() != null) 474 fContentAssistSubjectControlAdapter.removeSelectionListener(fTextWidgetSelectionListener); 475 fTextWidgetSelectionListener= null; 476 477 fContextInfoPopup.setVisible(false); 478 fContextInfoPopup.dispose(); 479 fContextInfoPopup= null; 480 481 if (fTextPresentation != null) { 482 fTextPresentation.clear(); 483 fTextPresentation= null; 484 } 485 } 486 } 487 488 if (fContextInfoPopup == null) 489 fContentAssistant.contextInformationClosed(); 490 } 491 492 496 private void createContextSelector() { 497 if (Helper.okToUse(fContextSelectorShell)) 498 return; 499 500 Control control= fContentAssistSubjectControlAdapter.getControl(); 501 fContextSelectorShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE); 502 GridLayout layout= new GridLayout(); 503 layout.marginWidth= 0; 504 layout.marginHeight= 0; 505 fContextSelectorShell.setLayout(layout); 506 fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK)); 507 508 509 fContextSelectorTable= new Table(fContextSelectorShell, SWT.H_SCROLL | SWT.V_SCROLL); 510 fContextSelectorTable.setLocation(1, 1); 511 GridData gd= new GridData(GridData.FILL_BOTH); 512 gd.heightHint= fContextSelectorTable.getItemHeight() * 10; 513 gd.widthHint= 300; 514 fContextSelectorTable.setLayoutData(gd); 515 516 fContextSelectorShell.pack(true); 517 518 Color c= fContentAssistant.getContextSelectorBackground(); 519 if (c == null) 520 c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND); 521 fContextSelectorTable.setBackground(c); 522 523 c= fContentAssistant.getContextSelectorForeground(); 524 if (c == null) 525 c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND); 526 fContextSelectorTable.setForeground(c); 527 528 fContextSelectorTable.addSelectionListener(new SelectionListener() { 529 public void widgetSelected(SelectionEvent e) { 530 } 531 532 public void widgetDefaultSelected(SelectionEvent e) { 533 insertSelectedContext(); 534 hideContextSelector(); 535 } 536 }); 537 538 fPopupCloser.install(fContentAssistant, fContextSelectorTable); 539 540 fContextSelectorTable.setHeaderVisible(false); 541 fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset()); 542 } 543 544 551 int getMinimalHeight() { 552 int height= 0; 553 if (Helper.okToUse(fContextSelectorTable)) { 554 int items= fContextSelectorTable.getItemHeight() * 10; 555 Rectangle trim= fContextSelectorTable.computeTrim(0, 0, SWT.DEFAULT, items); 556 height= trim.height; 557 } 558 return height; 559 } 560 561 565 private void insertSelectedContext() { 566 int i= fContextSelectorTable.getSelectionIndex(); 567 568 if (i < 0 || i >= fContextSelectorInput.length) 569 return; 570 571 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x; 572 internalShowContextInfo(createContextFrame(fContextSelectorInput[i], offset)); 573 } 574 575 580 private void setContexts(IContextInformation[] contexts) { 581 if (Helper.okToUse(fContextSelectorTable)) { 582 583 fContextSelectorInput= contexts; 584 585 fContextSelectorTable.setRedraw(false); 586 fContextSelectorTable.removeAll(); 587 588 TableItem item; 589 IContextInformation t; 590 for (int i= 0; i < contexts.length; i++) { 591 t= contexts[i]; 592 item= new TableItem(fContextSelectorTable, SWT.NULL); 593 if (t.getImage() != null) 594 item.setImage(t.getImage()); 595 item.setText(t.getContextDisplayString()); 596 } 597 598 fContextSelectorTable.select(0); 599 fContextSelectorTable.setRedraw(true); 600 } 601 } 602 603 606 private void displayContextSelector() { 607 if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR)) 608 fContextSelectorShell.setVisible(true); 609 } 610 611 614 private void hideContextSelector() { 615 if (Helper.okToUse(fContextSelectorShell)) { 616 fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR); 617 618 fPopupCloser.uninstall(); 619 fContextSelectorShell.setVisible(false); 620 fContextSelectorShell.dispose(); 621 fContextSelectorShell= null; 622 } 623 624 if (!Helper.okToUse(fContextInfoPopup)) 625 fContentAssistant.contextInformationClosed(); 626 } 627 628 633 public boolean hasFocus() { 634 if (Helper.okToUse(fContextSelectorShell)) 635 return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl()); 636 637 return false; 638 } 639 640 643 public void hide() { 644 hideContextSelector(); 645 hideContextInfoPopup(); 646 } 647 648 654 public boolean isActive() { 655 return (Helper.okToUse(fContextInfoPopup) || Helper.okToUse(fContextSelectorShell)); 656 } 657 658 661 public boolean verifyKey(VerifyEvent e) { 662 if (Helper.okToUse(fContextSelectorShell)) 663 return contextSelectorKeyPressed(e); 664 if (Helper.okToUse(fContextInfoPopup)) 665 return contextInfoPopupKeyPressed(e); 666 return true; 667 } 668 669 675 private boolean contextSelectorKeyPressed(VerifyEvent e) { 676 677 char key= e.character; 678 if (key == 0) { 679 680 int change; 681 int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1; 682 int selection= fContextSelectorTable.getSelectionIndex(); 683 684 switch (e.keyCode) { 685 686 case SWT.ARROW_UP: 687 change= (fContextSelectorTable.getSelectionIndex() > 0 ? -1 : 0); 688 break; 689 690 case SWT.ARROW_DOWN: 691 change= (fContextSelectorTable.getSelectionIndex() < fContextSelectorTable.getItemCount() - 1 ? 1 : 0); 692 break; 693 694 case SWT.PAGE_DOWN : 695 change= visibleRows; 696 if (selection + change >= fContextSelectorTable.getItemCount()) 697 change= fContextSelectorTable.getItemCount() - selection; 698 break; 699 700 case SWT.PAGE_UP : 701 change= -visibleRows; 702 if (selection + change < 0) 703 change= -selection; 704 break; 705 706 case SWT.HOME : 707 change= -selection; 708 break; 709 710 case SWT.END : 711 change= fContextSelectorTable.getItemCount() - selection; 712 break; 713 714 default: 715 if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4) 716 hideContextSelector(); 717 return true; 718 } 719 720 fContextSelectorTable.setSelection(selection + change); 721 fContextSelectorTable.showSelection(); 722 e.doit= false; 723 return false; 724 725 } else if ('\t' == key) { 726 e.doit= false; 728 fContextSelectorShell.setFocus(); 729 return false; 730 } else if (key == 0x1B) { 731 hideContextSelector(); 733 } 734 735 return true; 736 } 737 738 744 private boolean contextInfoPopupKeyPressed(KeyEvent e) { 745 746 char key= e.character; 747 if (key == 0) { 748 749 switch (e.keyCode) { 750 751 case SWT.ARROW_LEFT: 752 case SWT.ARROW_RIGHT: 753 validateContextInformation(); 754 break; 755 default: 756 if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4) 757 hideContextInfoPopup(); 758 break; 759 } 760 761 } else if (key == 0x1B) { 762 hideContextInfoPopup(); 764 } else { 765 validateContextInformation(); 766 } 767 return true; 768 } 769 770 773 public void processEvent(VerifyEvent event) { 774 if (Helper.okToUse(fContextSelectorShell)) 775 contextSelectorProcessEvent(event); 776 if (Helper.okToUse(fContextInfoPopup)) 777 contextInfoPopupProcessEvent(event); 778 } 779 780 785 private void contextSelectorProcessEvent(VerifyEvent e) { 786 787 if (e.start == e.end && e.text != null && e.text.equals(fLineDelimiter)) { 788 e.doit= false; 789 insertSelectedContext(); 790 } 791 792 hideContextSelector(); 793 } 794 795 800 private void contextInfoPopupProcessEvent(VerifyEvent e) { 801 if (e.start != e.end && (e.text == null || e.text.length() == 0)) 802 validateContextInformation(); 803 } 804 805 808 private void validateContextInformation() { 809 815 if (!Helper.okToUse(fContextInfoPopup)) 816 return; 817 818 fContextInfoPopup.getDisplay().asyncExec(new Runnable () { 819 820 private ContextFrame fFrame= (ContextFrame) fContextFrameStack.peek(); 821 822 public void run() { 823 if (!fContextFrameStack.isEmpty() && fFrame == fContextFrameStack.peek()) { 825 int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x; 826 827 while (Helper.okToUse(fContextInfoPopup) && !fContextFrameStack.isEmpty()) { 829 ContextFrame top= (ContextFrame) fContextFrameStack.peek(); 830 if (top.fValidator == null || !top.fValidator.isContextInformationValid(offset)) { 831 hideContextInfoPopup(); } else if (top.fPresenter != null && top.fPresenter.updatePresentation(offset, fTextPresentation)) { 833 int widgetOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x; 834 TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText); 835 resize(widgetOffset); 836 break; 837 } else 838 break; 839 } 840 } 841 } 842 }); 843 } 844 } 845 | Popular Tags |