1 11 package org.eclipse.jdt.internal.ui.text.java.hover; 12 13 import java.util.ArrayList ; 14 import java.util.Iterator ; 15 import java.util.List ; 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.custom.StyleRange; 19 import org.eclipse.swt.custom.StyledText; 20 import org.eclipse.swt.events.DisposeEvent; 21 import org.eclipse.swt.events.DisposeListener; 22 import org.eclipse.swt.events.FocusListener; 23 import org.eclipse.swt.events.MenuEvent; 24 import org.eclipse.swt.events.MenuListener; 25 import org.eclipse.swt.events.MouseAdapter; 26 import org.eclipse.swt.events.MouseEvent; 27 import org.eclipse.swt.events.MouseTrackAdapter; 28 import org.eclipse.swt.events.MouseTrackListener; 29 import org.eclipse.swt.events.PaintEvent; 30 import org.eclipse.swt.events.PaintListener; 31 import org.eclipse.swt.graphics.Color; 32 import org.eclipse.swt.graphics.Cursor; 33 import org.eclipse.swt.graphics.Point; 34 import org.eclipse.swt.graphics.Rectangle; 35 import org.eclipse.swt.layout.GridData; 36 import org.eclipse.swt.layout.GridLayout; 37 import org.eclipse.swt.widgets.Canvas; 38 import org.eclipse.swt.widgets.Composite; 39 import org.eclipse.swt.widgets.Control; 40 import org.eclipse.swt.widgets.Display; 41 import org.eclipse.swt.widgets.Event; 42 import org.eclipse.swt.widgets.Layout; 43 import org.eclipse.swt.widgets.Listener; 44 import org.eclipse.swt.widgets.Menu; 45 import org.eclipse.swt.widgets.Shell; 46 import org.eclipse.swt.widgets.Widget; 47 48 import org.eclipse.jface.viewers.IDoubleClickListener; 49 50 import org.eclipse.jface.text.AbstractInformationControlManager; 51 import org.eclipse.jface.text.DefaultInformationControl; 52 import org.eclipse.jface.text.IInformationControl; 53 import org.eclipse.jface.text.IInformationControlCreator; 54 import org.eclipse.jface.text.IInformationControlExtension; 55 import org.eclipse.jface.text.IInformationControlExtension2; 56 import org.eclipse.jface.text.IRegion; 57 import org.eclipse.jface.text.IViewportListener; 58 import org.eclipse.jface.text.Position; 59 import org.eclipse.jface.text.Region; 60 import org.eclipse.jface.text.TextViewer; 61 import org.eclipse.jface.text.source.Annotation; 62 import org.eclipse.jface.text.source.IAnnotationAccess; 63 import org.eclipse.jface.text.source.IAnnotationAccessExtension; 64 import org.eclipse.jface.text.source.IAnnotationModel; 65 import org.eclipse.jface.text.source.ISourceViewer; 66 import org.eclipse.jface.text.source.IVerticalRulerInfo; 67 import org.eclipse.jface.text.source.IVerticalRulerListener; 68 import org.eclipse.jface.text.source.VerticalRulerEvent; 69 70 71 83 public class AnnotationExpansionControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension2 { 84 85 86 public interface ICallback { 87 void run(IInformationControlExtension2 control); 88 } 89 90 97 public static class AnnotationHoverInput { 98 public Annotation[] fAnnotations; 99 public ISourceViewer fViewer; 100 public IVerticalRulerInfo fRulerInfo; 101 public IVerticalRulerListener fAnnotationListener; 102 public IDoubleClickListener fDoubleClickListener; 103 public ICallback redoAction; 104 public IAnnotationModel model; 105 } 106 107 private final class Item { 108 Annotation fAnnotation; 109 Canvas canvas; 110 StyleRange[] oldStyles; 111 112 public void selected() { 113 Display disp= fShell.getDisplay(); 114 canvas.setCursor(fHandCursor); 115 canvas.setBackground(getSelectionColor(disp)); 117 118 oldStyles= setViewerBackground(fAnnotation); 120 121 fSelection= this; 123 124 if (fHoverManager != null) 125 fHoverManager.showInformation(); 126 127 if (fInput.fAnnotationListener != null) { 128 VerticalRulerEvent event= new VerticalRulerEvent(fAnnotation); 129 fInput.fAnnotationListener.annotationSelected(event); 130 } 131 132 } 133 134 public void defaultSelected() { 135 if (fInput.fAnnotationListener != null) { 136 VerticalRulerEvent event= new VerticalRulerEvent(fAnnotation); 137 fInput.fAnnotationListener.annotationDefaultSelected(event); 138 } 139 140 dispose(); 141 } 142 143 public void showContextMenu(Menu menu) { 144 if (fInput.fAnnotationListener != null) { 145 VerticalRulerEvent event= new VerticalRulerEvent(fAnnotation); 146 fInput.fAnnotationListener.annotationContextMenuAboutToShow(event, menu); 147 } 148 } 149 150 public void deselect() { 151 154 fSelection= null; 156 157 resetViewerBackground(oldStyles); 158 oldStyles= null; 159 160 Display disp= fShell.getDisplay(); 161 canvas.setCursor(null); 162 canvas.setBackground(disp.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); 164 165 } 166 167 } 168 169 172 private final static class MyDisposeListener implements DisposeListener { 173 176 public void widgetDisposed(DisposeEvent e) { 177 Item item= (Item) ((Widget) e.getSource()).getData(); 178 item.deselect(); 179 item.canvas= null; 180 item.fAnnotation= null; 181 item.oldStyles= null; 182 183 ((Widget) e.getSource()).setData(null); 184 } 185 } 186 187 190 private final class MyMenuDetectListener implements Listener { 191 194 public void handleEvent(Event event) { 195 if (event.type == SWT.MenuDetect) { 196 if (fInput != null) { 199 Control ruler= fInput.fRulerInfo.getControl(); 200 if (ruler != null && !ruler.isDisposed()) { 201 Menu menu= ruler.getMenu(); 202 if (menu != null && !menu.isDisposed()) { 203 menu.setLocation(event.x, event.y); 204 menu.addMenuListener(new MenuListener() { 205 206 public void menuHidden(MenuEvent e) { 207 dispose(); 208 } 209 210 public void menuShown(MenuEvent e) { 211 } 212 213 }); 214 menu.setVisible(true); 215 } 216 } 217 } 218 } 219 } 220 } 221 222 223 226 private final class MyMouseListener extends MouseAdapter { 227 230 public void mouseDoubleClick(MouseEvent e) { 231 Item item= (Item) ((Widget) e.getSource()).getData(); 232 if (e.button == 1 && item.fAnnotation == fInput.fAnnotations[0] && fInput.fDoubleClickListener != null) { 233 fInput.fDoubleClickListener.doubleClick(null); 234 if (fInput.redoAction != null) 236 fInput.redoAction.run(AnnotationExpansionControl.this); 237 } 238 } 247 248 254 public void mouseDown(MouseEvent e) { 255 Item item= (Item) ((Widget) e.getSource()).getData(); 256 if (item != null && e.button == 1) item.defaultSelected(); 260 } 261 262 } 263 264 267 private final class MyMouseTrackListener implements MouseTrackListener { 268 271 public void mouseEnter(MouseEvent e) { 272 Item item= (Item) ((Widget) e.getSource()).getData(); 273 if (item != null) 274 item.selected(); 275 } 276 277 280 public void mouseExit(MouseEvent e) { 281 282 Item item= (Item) ((Widget) e.getSource()).getData(); 283 if (item != null) 284 item.deselect(); 285 286 org.eclipse.swt.graphics.Region region= fShell.getRegion(); 288 Canvas can= (Canvas) e.getSource(); 289 Point p= can.toDisplay(e.x, e.y); 290 if (region == null) { 291 Rectangle bounds= fShell.getBounds(); 292 if (!bounds.contains(p)) 294 dispose(); 295 } else { 296 p= fShell.toControl(p); 297 if (!region.contains(p)) 298 dispose(); 299 } 300 301 302 } 303 304 307 public void mouseHover(MouseEvent e) { 308 if (fHoverManager == null) { 309 fHoverManager= new HoverManager(); 310 fHoverManager.takesFocusWhenVisible(false); 311 fHoverManager.install(fComposite); 312 fHoverManager.showInformation(); 313 } 314 } 315 } 316 317 318 323 public class LinearLayouter { 324 325 private static final int ANNOTATION_SIZE= 14; 326 private static final int BORDER_WIDTH= 2; 327 328 public Layout getLayout(int itemCount) { 329 GridLayout layout= new GridLayout(itemCount, true); 331 layout.horizontalSpacing= 1; 332 layout.verticalSpacing= 0; 333 layout.marginHeight= 1; 334 layout.marginWidth= 1; 335 return layout; 336 } 337 338 public Object getLayoutData() { 339 GridData gridData= new GridData(ANNOTATION_SIZE + 2 * BORDER_WIDTH, ANNOTATION_SIZE + 2 * BORDER_WIDTH); 340 gridData.horizontalAlignment= GridData.CENTER; 341 gridData.verticalAlignment= GridData.CENTER; 342 return gridData; 343 } 344 345 public int getAnnotationSize() { 346 return ANNOTATION_SIZE; 347 } 348 349 public int getBorderWidth() { 350 return BORDER_WIDTH; 351 } 352 353 public org.eclipse.swt.graphics.Region getShellRegion(int itemCount) { 354 return null; 356 } 357 358 } 359 360 361 364 private final class MyPaintListener implements PaintListener { 365 368 public void paintControl(PaintEvent e) { 369 Canvas can= (Canvas) e.getSource(); 370 Annotation a= ((Item) can.getData()).fAnnotation; 371 if (a != null) { 372 Rectangle rect= new Rectangle(fLayouter.getBorderWidth(), fLayouter.getBorderWidth(), fLayouter.getAnnotationSize(), fLayouter.getAnnotationSize()); 373 if (fAnnotationAccessExtension != null) 374 fAnnotationAccessExtension.paint(a, e.gc, can, rect); 375 } 376 } 377 } 378 379 382 private final class HoverManager extends AbstractInformationControlManager { 383 384 387 public HoverManager() { 388 super(new IInformationControlCreator() { 389 public IInformationControl createInformationControl(Shell parent) { 390 return new DefaultInformationControl(parent); 391 } 392 }); 393 394 setMargins(5, 10); 395 setAnchor(ANCHOR_BOTTOM); 396 setFallbackAnchors(new Anchor[] {ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_RIGHT} ); 397 } 398 399 402 protected void computeInformation() { 403 if (fSelection != null) { 404 Rectangle subjectArea= fSelection.canvas.getBounds(); 405 Annotation annotation= fSelection.fAnnotation; 406 String msg; 407 if (annotation != null) 408 msg= annotation.getText(); 409 else 410 msg= null; 411 412 setInformation(msg, subjectArea); 413 } 414 } 415 416 417 } 418 419 420 protected AnnotationHoverInput fInput; 421 422 private Shell fShell; 423 424 protected Composite fComposite; 425 426 private Cursor fHandCursor; 427 428 private Item fSelection; 429 430 private HoverManager fHoverManager; 431 432 private IAnnotationAccessExtension fAnnotationAccessExtension; 433 434 435 436 private final MyPaintListener fPaintListener; 437 private final MyMouseTrackListener fMouseTrackListener; 438 private final MyMouseListener fMouseListener; 439 private final MyMenuDetectListener fMenuDetectListener; 440 private final DisposeListener fDisposeListener; 441 private final IViewportListener fViewportListener; 442 443 private LinearLayouter fLayouter; 444 445 452 public AnnotationExpansionControl(Shell parent, int shellStyle, IAnnotationAccess access) { 453 fPaintListener= new MyPaintListener(); 454 fMouseTrackListener= new MyMouseTrackListener(); 455 fMouseListener= new MyMouseListener(); 456 fMenuDetectListener= new MyMenuDetectListener(); 457 fDisposeListener= new MyDisposeListener(); 458 fViewportListener= new IViewportListener() { 459 460 public void viewportChanged(int verticalOffset) { 461 dispose(); 462 } 463 464 }; 465 fLayouter= new LinearLayouter(); 466 467 if (access instanceof IAnnotationAccessExtension) 468 fAnnotationAccessExtension= (IAnnotationAccessExtension) access; 469 470 fShell= new Shell(parent, shellStyle | SWT.NO_FOCUS | SWT.ON_TOP); 471 Display display= fShell.getDisplay(); 472 fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); 473 fComposite= new Composite(fShell, SWT.NO_FOCUS | SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM); 474 476 GridLayout layout= new GridLayout(1, true); 477 layout.marginHeight= 0; 478 layout.marginWidth= 0; 479 fShell.setLayout(layout); 480 481 GridData data= new GridData(GridData.FILL_BOTH); 482 data.heightHint= fLayouter.getAnnotationSize() + 2 * fLayouter.getBorderWidth() + 4; 483 fComposite.setLayoutData(data); 484 fComposite.addMouseTrackListener(new MouseTrackAdapter() { 485 486 public void mouseExit(MouseEvent e) { 487 if (fComposite == null) 488 return; 489 Control[] children= fComposite.getChildren(); 490 Rectangle bounds= null; 491 for (int i= 0; i < children.length; i++) { 492 if (bounds == null) 493 bounds= children[i].getBounds(); 494 else 495 bounds.add(children[i].getBounds()); 496 if (bounds.contains(e.x, e.y)) 497 return; 498 } 499 500 dispose(); 502 } 503 504 }); 505 506 517 fHandCursor= new Cursor(display, SWT.CURSOR_HAND); 518 fShell.setCursor(fHandCursor); 519 fComposite.setCursor(fHandCursor); 520 521 setInfoSystemColor(); 522 } 523 524 private void setInfoSystemColor() { 525 Display display= fShell.getDisplay(); 526 setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); 527 setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); 528 } 529 530 533 public void setInformation(String information) { 534 setInput(null); 535 } 536 537 538 541 public void setInput(Object input) { 542 if (fInput != null && fInput.fViewer != null) 543 fInput.fViewer.removeViewportListener(fViewportListener); 544 545 if (input instanceof AnnotationHoverInput) 546 fInput= (AnnotationHoverInput) input; 547 else 548 fInput= null; 549 550 inputChanged(fInput, null); 551 } 552 553 protected void inputChanged(Object newInput, Object newSelection) { 554 refresh(); 555 } 556 557 protected void refresh() { 558 adjustItemNumber(); 559 560 if (fInput == null) 561 return; 562 563 if (fInput.fAnnotations == null) 564 return; 565 566 if (fInput.fViewer != null) 567 fInput.fViewer.addViewportListener(fViewportListener); 568 569 fShell.setRegion(fLayouter.getShellRegion(fInput.fAnnotations.length)); 570 571 Layout layout= fLayouter.getLayout(fInput.fAnnotations.length); 572 fComposite.setLayout(layout); 573 574 Control[] children= fComposite.getChildren(); 575 for (int i= 0; i < fInput.fAnnotations.length; i++) { 576 Canvas canvas= (Canvas) children[i]; 577 Item item= new Item(); 578 item.canvas= canvas; 579 item.fAnnotation= fInput.fAnnotations[i]; 580 canvas.setData(item); 581 canvas.redraw(); 582 } 583 584 } 585 586 protected void adjustItemNumber() { 587 if (fComposite == null) 588 return; 589 590 Control[] children= fComposite.getChildren(); 591 int oldSize= children.length; 592 int newSize= fInput == null ? 0 : fInput.fAnnotations.length; 593 594 Display display= fShell.getDisplay(); 595 596 for (int i= oldSize; i < newSize; i++) { 598 Canvas canvas= new Canvas(fComposite, SWT.NONE); 599 Object gridData= fLayouter.getLayoutData(); 600 canvas.setLayoutData(gridData); 601 canvas.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); 602 603 canvas.addPaintListener(fPaintListener); 604 605 canvas.addMouseTrackListener(fMouseTrackListener); 606 607 canvas.addMouseListener(fMouseListener); 608 609 canvas.addListener(SWT.MenuDetect, fMenuDetectListener); 610 611 canvas.addDisposeListener(fDisposeListener); 612 } 613 614 for (int i= oldSize; i > newSize; i--) { 616 Item item= (Item) children[i - 1].getData(); 617 item.deselect(); 618 children[i - 1].dispose(); 619 } 620 621 } 622 623 626 public void setVisible(boolean visible) { 627 fShell.setVisible(visible); 628 } 629 630 633 public void dispose() { 634 if (fShell != null) { 635 if (!fShell.isDisposed()) 636 fShell.dispose(); 637 fShell= null; 638 fComposite= null; 639 if (fHandCursor != null) 640 fHandCursor.dispose(); 641 fHandCursor= null; 642 if (fHoverManager != null) 643 fHoverManager.dispose(); 644 fHoverManager= null; 645 fSelection= null; 646 } 647 } 648 649 652 public boolean hasContents() { 653 return fInput.fAnnotations != null && fInput.fAnnotations.length > 0; 654 } 655 656 659 public void setSizeConstraints(int maxWidth, int maxHeight) { 660 } 663 664 667 public Point computeSizeHint() { 668 return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT); 669 } 670 671 674 public void setLocation(Point location) { 675 fShell.setLocation(location); 676 } 677 678 681 public void setSize(int width, int height) { 682 fShell.setSize(width, height); 683 } 684 685 688 public void addDisposeListener(DisposeListener listener) { 689 fShell.addDisposeListener(listener); 690 } 691 692 695 public void removeDisposeListener(DisposeListener listener) { 696 fShell.removeDisposeListener(listener); 697 } 698 699 702 public void setForegroundColor(Color foreground) { 703 fComposite.setForeground(foreground); 704 } 705 706 709 public void setBackgroundColor(Color background) { 710 fComposite.setBackground(background); 711 } 712 713 716 public boolean isFocusControl() { 717 if (fComposite.isFocusControl()) 718 return true; 719 720 Control[] children= fComposite.getChildren(); 721 for (int i= 0; i < children.length; i++) { 722 if (children[i].isFocusControl()) 723 return true; 724 } 725 return false; 726 } 727 728 731 public void setFocus() { 732 fShell.forceFocus(); 733 } 734 735 738 public void addFocusListener(FocusListener listener) { 739 fShell.addFocusListener(listener); 740 } 741 742 745 public void removeFocusListener(FocusListener listener) { 746 fShell.removeFocusListener(listener); 747 } 748 749 private StyleRange[] setViewerBackground(Annotation annotation) { 750 StyledText text= fInput.fViewer.getTextWidget(); 751 if (text == null || text.isDisposed()) 752 return null; 753 754 Display disp= text.getDisplay(); 755 756 Position pos= fInput.model.getPosition(annotation); 757 if (pos == null) 758 return null; 759 760 IRegion region= ((TextViewer)fInput.fViewer).modelRange2WidgetRange(new Region(pos.offset, pos.length)); 761 if (region == null) 762 return null; 763 764 StyleRange[] ranges= text.getStyleRanges(region.getOffset(), region.getLength()); 765 766 List undoRanges= new ArrayList (ranges.length); 767 for (int i= 0; i < ranges.length; i++) { 768 undoRanges.add(ranges[i].clone()); 769 } 770 771 int offset= region.getOffset(); 772 StyleRange current= undoRanges.size() > 0 ? (StyleRange) undoRanges.get(0) : null; 773 int curStart= current != null ? current.start : region.getOffset() + region.getLength(); 774 int curEnd= current != null ? current.start + current.length : -1; 775 int index= 0; 776 777 while (curEnd < region.getOffset() + region.getLength()) { 779 if (curStart > offset) { 781 StyleRange undoRange= new StyleRange(offset, curStart - offset, null, null); 782 undoRanges.add(index, undoRange); 783 index++; 784 } 785 786 index++; 788 if (index < undoRanges.size()) { 789 offset= curEnd; 790 current= (StyleRange) undoRanges.get(index); 791 curStart= current.start; 792 curEnd= current.start + current.length; 793 } else if (index == undoRanges.size()) { 794 offset= curEnd; 796 current= null; 797 curStart= region.getOffset() + region.getLength(); 798 curEnd= -1; 799 } else 800 curEnd= region.getOffset() + region.getLength(); 801 } 802 803 List shadedRanges= new ArrayList (undoRanges.size()); 805 for (Iterator it= undoRanges.iterator(); it.hasNext(); ) { 806 StyleRange range= (StyleRange) ((StyleRange) it.next()).clone(); 807 shadedRanges.add(range); 808 range.background= getHighlightColor(disp); 809 } 810 811 for (Iterator iter= shadedRanges.iterator(); iter.hasNext(); ) { 813 text.setStyleRange((StyleRange) iter.next()); 814 815 } 816 817 return (StyleRange[]) undoRanges.toArray(undoRanges.toArray(new StyleRange[0])); 818 } 819 820 private void resetViewerBackground(StyleRange[] oldRanges) { 821 822 if (oldRanges == null) 823 return; 824 825 if (fInput == null) 826 return; 827 828 StyledText text= fInput.fViewer.getTextWidget(); 829 if (text == null || text.isDisposed()) 830 return; 831 832 for (int i= 0; i < oldRanges.length; i++) { 834 text.setStyleRange(oldRanges[i]); 835 } 836 } 837 838 private Color getHighlightColor(Display disp) { 839 return disp.getSystemColor(SWT.COLOR_GRAY); 840 } 841 842 private Color getSelectionColor(Display disp) { 843 return disp.getSystemColor(SWT.COLOR_GRAY); 844 } 845 846 } 847 | Popular Tags |