1 /******************************************************************************* 2 * Copyright (c) 2000, 2012 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.ui.text.java.hover; 15 16 import java.util.ArrayList; 17 import java.util.List; 18 19 import org.eclipse.swt.SWT; 20 import org.eclipse.swt.custom.StyleRange; 21 import org.eclipse.swt.custom.StyledText; 22 import org.eclipse.swt.events.DisposeEvent; 23 import org.eclipse.swt.events.DisposeListener; 24 import org.eclipse.swt.events.FocusListener; 25 import org.eclipse.swt.events.MenuEvent; 26 import org.eclipse.swt.events.MenuListener; 27 import org.eclipse.swt.events.MouseAdapter; 28 import org.eclipse.swt.events.MouseEvent; 29 import org.eclipse.swt.events.MouseTrackAdapter; 30 import org.eclipse.swt.events.MouseTrackListener; 31 import org.eclipse.swt.events.PaintEvent; 32 import org.eclipse.swt.events.PaintListener; 33 import org.eclipse.swt.graphics.Color; 34 import org.eclipse.swt.graphics.Cursor; 35 import org.eclipse.swt.graphics.Point; 36 import org.eclipse.swt.graphics.Rectangle; 37 import org.eclipse.swt.layout.GridData; 38 import org.eclipse.swt.layout.GridLayout; 39 import org.eclipse.swt.widgets.Canvas; 40 import org.eclipse.swt.widgets.Composite; 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.Layout; 45 import org.eclipse.swt.widgets.Listener; 46 import org.eclipse.swt.widgets.Menu; 47 import org.eclipse.swt.widgets.Shell; 48 import org.eclipse.swt.widgets.Widget; 49 50 import org.eclipse.jface.viewers.IDoubleClickListener; 51 52 import org.eclipse.jface.text.AbstractInformationControlManager; 53 import org.eclipse.jface.text.DefaultInformationControl; 54 import org.eclipse.jface.text.IInformationControl; 55 import org.eclipse.jface.text.IInformationControlCreator; 56 import org.eclipse.jface.text.IInformationControlExtension; 57 import org.eclipse.jface.text.IInformationControlExtension2; 58 import org.eclipse.jface.text.IInformationControlExtension5; 59 import org.eclipse.jface.text.IRegion; 60 import org.eclipse.jface.text.IViewportListener; 61 import org.eclipse.jface.text.Position; 62 import org.eclipse.jface.text.Region; 63 import org.eclipse.jface.text.TextViewer; 64 import org.eclipse.jface.text.source.Annotation; 65 import org.eclipse.jface.text.source.IAnnotationAccess; 66 import org.eclipse.jface.text.source.IAnnotationAccessExtension; 67 import org.eclipse.jface.text.source.IAnnotationModel; 68 import org.eclipse.jface.text.source.ISourceViewer; 69 import org.eclipse.jface.text.source.IVerticalRulerInfo; 70 import org.eclipse.jface.text.source.IVerticalRulerListener; 71 import org.eclipse.jface.text.source.VerticalRulerEvent; 72 73 74 /** 75 * A control that can display a number of annotations. The control can decide how it layouts the 76 * annotations to present them to the user. 77 * <p> 78 * This class got moved here form Platform Text since it was not used there 79 * and caused discouraged access warnings. It will be moved down again once 80 * annotation roll-over support is provided by Platform Text. 81 * </p> 82 * <p>Each annotation can have its custom context menu and hover.</p> 83 * 84 * @since 3.2 85 */ 86 public class AnnotationExpansionControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension2, IInformationControlExtension5 { 87 88 89 public interface ICallback { run(IInformationControlExtension2 control)90 void run(IInformationControlExtension2 control); 91 } 92 93 /** 94 * Input used by the control to display the annotations. 95 * TODO move to top-level class 96 * TODO encapsulate fields 97 * 98 * @since 3.0 99 */ 100 public static class AnnotationHoverInput { 101 public Annotation[] fAnnotations; 102 public ISourceViewer fViewer; 103 public IVerticalRulerInfo fRulerInfo; 104 public IVerticalRulerListener fAnnotationListener; 105 public IDoubleClickListener fDoubleClickListener; 106 public ICallback redoAction; 107 public IAnnotationModel model; 108 } 109 110 private final class Item { 111 Annotation fAnnotation; 112 Canvas canvas; 113 StyleRange[] oldStyles; 114 selected()115 public void selected() { 116 Display disp= fShell.getDisplay(); 117 canvas.setCursor(getHandCursor(disp)); 118 // TODO: shade - for now: set grey background 119 canvas.setBackground(getSelectionColor(disp)); 120 121 // highlight the viewer background at its position 122 oldStyles= setViewerBackground(fAnnotation); 123 124 // set the selection 125 fSelection= this; 126 127 if (fHoverManager != null) 128 fHoverManager.showInformation(); 129 130 if (fInput.fAnnotationListener != null) { 131 VerticalRulerEvent event= new VerticalRulerEvent(fAnnotation); 132 fInput.fAnnotationListener.annotationSelected(event); 133 } 134 135 } 136 defaultSelected(MouseEvent e)137 public void defaultSelected(MouseEvent e) { 138 if (fInput.fAnnotationListener != null) { 139 Event swtEvent= new Event(); 140 swtEvent.type= SWT.MouseDown; 141 swtEvent.display= e.display; 142 swtEvent.widget= e.widget; 143 swtEvent.time= e.time; 144 swtEvent.data= e.data; 145 swtEvent.x= e.x; 146 swtEvent.y= e.y; 147 swtEvent.button= e.button; 148 swtEvent.stateMask= e.stateMask; 149 swtEvent.count= e.count; 150 VerticalRulerEvent event= new VerticalRulerEvent(fAnnotation, swtEvent); 151 fInput.fAnnotationListener.annotationDefaultSelected(event); 152 } 153 154 dispose(); 155 } 156 deselect()157 public void deselect() { 158 // hide the popup 159 // fHoverManager.disposeInformationControl(); 160 161 // deselect 162 fSelection= null; 163 164 resetViewerBackground(oldStyles); 165 oldStyles= null; 166 167 Display disp= fShell.getDisplay(); 168 canvas.setCursor(null); 169 // TODO: remove shading - for now: set standard background 170 canvas.setBackground(disp.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); 171 172 } 173 174 } 175 176 /** 177 * Disposes of an item 178 */ 179 private final static class MyDisposeListener implements DisposeListener { 180 /* 181 * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) 182 */ 183 @Override widgetDisposed(DisposeEvent e)184 public void widgetDisposed(DisposeEvent e) { 185 Item item= (Item) ((Widget) e.getSource()).getData(); 186 item.deselect(); 187 item.canvas= null; 188 item.fAnnotation= null; 189 item.oldStyles= null; 190 191 ((Widget) e.getSource()).setData(null); 192 } 193 } 194 195 /** 196 * Listener on context menu invocation on the items 197 */ 198 private final class MyMenuDetectListener implements Listener { 199 /* 200 * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event) 201 */ 202 @Override handleEvent(Event event)203 public void handleEvent(Event event) { 204 if (event.type == SWT.MenuDetect) { 205 // TODO: show per-item menu 206 // for now: show ruler context menu 207 if (fInput != null) { 208 Control ruler= fInput.fRulerInfo.getControl(); 209 if (ruler != null && !ruler.isDisposed()) { 210 Menu menu= ruler.getMenu(); 211 if (menu != null && !menu.isDisposed()) { 212 menu.setLocation(event.x, event.y); 213 menu.addMenuListener(new MenuListener() { 214 215 @Override 216 public void menuHidden(MenuEvent e) { 217 dispose(); 218 } 219 220 @Override 221 public void menuShown(MenuEvent e) { 222 } 223 224 }); 225 menu.setVisible(true); 226 } 227 } 228 } 229 } 230 } 231 } 232 233 234 /** 235 * Listener on mouse events on the items. 236 */ 237 private final class MyMouseListener extends MouseAdapter { 238 /* 239 * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) 240 */ 241 @Override mouseDoubleClick(MouseEvent e)242 public void mouseDoubleClick(MouseEvent e) { 243 Item item= (Item) ((Widget) e.getSource()).getData(); 244 if (e.button == 1 && item.fAnnotation == fInput.fAnnotations[0] && fInput.fDoubleClickListener != null) { 245 fInput.fDoubleClickListener.doubleClick(null); 246 // special code for JDT to renew the annotation set. 247 if (fInput.redoAction != null) 248 fInput.redoAction.run(AnnotationExpansionControl.this); 249 } 250 // dispose(); 251 // TODO special action to invoke double-click action on the vertical ruler 252 // how about 253 // Canvas can= (Canvas) e.getSource(); 254 // Annotation a= (Annotation) can.getData(); 255 // if (a != null) { 256 // a.getDoubleClickAction().run(); 257 // } 258 } 259 260 /* 261 * Using mouseDown as mouseUp isn't fired on some Platforms, for 262 * details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=165533 263 * 264 * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) 265 */ 266 @Override mouseDown(MouseEvent e)267 public void mouseDown(MouseEvent e) { 268 Item item= (Item) ((Widget) e.getSource()).getData(); 269 // TODO for now, to make double click work: disable single click on the first item 270 // disable later when the annotationlistener selectively handles input 271 if (item != null && e.button == 1) // && item.fAnnotation != fInput.fAnnotations[0]) 272 item.defaultSelected(e); 273 } 274 275 } 276 277 /** 278 * Listener on mouse track events on the items. 279 */ 280 private final class MyMouseTrackListener implements MouseTrackListener { 281 /* 282 * @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent) 283 */ 284 @Override mouseEnter(MouseEvent e)285 public void mouseEnter(MouseEvent e) { 286 Item item= (Item) ((Widget) e.getSource()).getData(); 287 if (item != null) 288 item.selected(); 289 } 290 291 /* 292 * @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent) 293 */ 294 @Override mouseExit(MouseEvent e)295 public void mouseExit(MouseEvent e) { 296 297 Item item= (Item) ((Widget) e.getSource()).getData(); 298 if (item != null) 299 item.deselect(); 300 301 // if the event lies outside the entire popup, dispose 302 org.eclipse.swt.graphics.Region region= fShell.getRegion(); 303 Canvas can= (Canvas) e.getSource(); 304 Point p= can.toDisplay(e.x, e.y); 305 if (region == null) { 306 Rectangle bounds= fShell.getBounds(); 307 // p= fShell.toControl(p); 308 if (!bounds.contains(p)) 309 dispose(); 310 } else { 311 p= fShell.toControl(p); 312 if (!region.contains(p)) 313 dispose(); 314 } 315 316 317 } 318 319 /* 320 * @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent) 321 */ 322 @Override mouseHover(MouseEvent e)323 public void mouseHover(MouseEvent e) { 324 if (fHoverManager == null) { 325 fHoverManager= new HoverManager(); 326 fHoverManager.takesFocusWhenVisible(false); 327 fHoverManager.install(fComposite); 328 fHoverManager.showInformation(); 329 } 330 } 331 } 332 333 334 /** 335 * @since 3.0 336 */ 337 public class LinearLayouter { 338 339 private static final int ANNOTATION_SIZE= 14; 340 private static final int BORDER_WIDTH= 2; 341 getLayout(int itemCount)342 public Layout getLayout(int itemCount) { 343 // simple layout: a row of items 344 GridLayout layout= new GridLayout(itemCount, true); 345 layout.horizontalSpacing= 1; 346 layout.verticalSpacing= 0; 347 layout.marginHeight= 1; 348 layout.marginWidth= 1; 349 return layout; 350 } 351 getLayoutData()352 public Object getLayoutData() { 353 GridData gridData= new GridData(ANNOTATION_SIZE + 2 * BORDER_WIDTH, ANNOTATION_SIZE + 2 * BORDER_WIDTH); 354 gridData.horizontalAlignment= GridData.CENTER; 355 gridData.verticalAlignment= GridData.CENTER; 356 return gridData; 357 } 358 getAnnotationSize()359 public int getAnnotationSize() { 360 return ANNOTATION_SIZE; 361 } 362 getBorderWidth()363 public int getBorderWidth() { 364 return BORDER_WIDTH; 365 } 366 367 /** 368 * Gets the shell region for the given number of items. 369 * 370 * @param itemCount the item count 371 * @return the shell region 372 */ getShellRegion(int itemCount)373 public org.eclipse.swt.graphics.Region getShellRegion(int itemCount) { 374 // no special region - set to null for default shell size 375 return null; 376 } 377 378 } 379 380 381 /** 382 * Listener on paint events on the items. Paints the annotation image on the given <code>GC</code>. 383 */ 384 private final class MyPaintListener implements PaintListener { 385 /* 386 * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent) 387 */ 388 @Override paintControl(PaintEvent e)389 public void paintControl(PaintEvent e) { 390 Canvas can= (Canvas) e.getSource(); 391 Annotation a= ((Item) can.getData()).fAnnotation; 392 if (a != null) { 393 Rectangle rect= new Rectangle(fLayouter.getBorderWidth(), fLayouter.getBorderWidth(), fLayouter.getAnnotationSize(), fLayouter.getAnnotationSize()); 394 if (fAnnotationAccessExtension != null) 395 fAnnotationAccessExtension.paint(a, e.gc, can, rect); 396 } 397 } 398 } 399 400 /** 401 * Our own private hover manager used to shop per-item pop-ups. 402 */ 403 private final class HoverManager extends AbstractInformationControlManager { 404 405 /** 406 * 407 */ HoverManager()408 public HoverManager() { 409 super(new IInformationControlCreator() { 410 @Override 411 public IInformationControl createInformationControl(Shell parent) { 412 return new DefaultInformationControl(parent); 413 } 414 }); 415 416 setMargins(5, 10); 417 setAnchor(ANCHOR_BOTTOM); 418 setFallbackAnchors(new Anchor[] {ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_RIGHT} ); 419 } 420 421 /* 422 * @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformation() 423 */ 424 @Override computeInformation()425 protected void computeInformation() { 426 if (fSelection != null) { 427 Rectangle subjectArea= fSelection.canvas.getBounds(); 428 Annotation annotation= fSelection.fAnnotation; 429 String msg; 430 if (annotation != null) 431 msg= annotation.getText(); 432 else 433 msg= null; 434 435 setInformation(msg, subjectArea); 436 } 437 } 438 439 440 } 441 442 /** Model data. */ 443 protected AnnotationHoverInput fInput; 444 /** The control's shell */ 445 private Shell fShell; 446 /** The composite combining all the items. */ 447 protected Composite fComposite; 448 /** The currently selected item, or <code>null</code> if none is selected. */ 449 private Item fSelection; 450 /** The hover manager for the per-item hovers. */ 451 private HoverManager fHoverManager; 452 /** The annotation access extension. */ 453 private IAnnotationAccessExtension fAnnotationAccessExtension; 454 455 456 /* listener legion */ 457 private final MyPaintListener fPaintListener; 458 private final MyMouseTrackListener fMouseTrackListener; 459 private final MyMouseListener fMouseListener; 460 private final MyMenuDetectListener fMenuDetectListener; 461 private final DisposeListener fDisposeListener; 462 private final IViewportListener fViewportListener; 463 464 private LinearLayouter fLayouter; 465 466 /** 467 * Creates a new control. 468 * 469 * @param parent parent shell 470 * @param shellStyle additional style flags 471 * @param access the annotation access 472 */ AnnotationExpansionControl(Shell parent, int shellStyle, IAnnotationAccess access)473 public AnnotationExpansionControl(Shell parent, int shellStyle, IAnnotationAccess access) { 474 fPaintListener= new MyPaintListener(); 475 fMouseTrackListener= new MyMouseTrackListener(); 476 fMouseListener= new MyMouseListener(); 477 fMenuDetectListener= new MyMenuDetectListener(); 478 fDisposeListener= new MyDisposeListener(); 479 fViewportListener= new IViewportListener() { 480 481 @Override 482 public void viewportChanged(int verticalOffset) { 483 dispose(); 484 } 485 486 }; 487 fLayouter= new LinearLayouter(); 488 489 if (access instanceof IAnnotationAccessExtension) 490 fAnnotationAccessExtension= (IAnnotationAccessExtension) access; 491 492 fShell= new Shell(parent, shellStyle | SWT.NO_FOCUS | SWT.ON_TOP); 493 Display display= fShell.getDisplay(); 494 fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); 495 fComposite= new Composite(fShell, SWT.NO_FOCUS | SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM); 496 // fComposite= new Composite(fShell, SWT.NO_FOCUS | SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM | SWT.V_SCROLL); 497 498 GridLayout layout= new GridLayout(1, true); 499 layout.marginHeight= 0; 500 layout.marginWidth= 0; 501 fShell.setLayout(layout); 502 503 GridData data= new GridData(GridData.FILL_BOTH); 504 data.heightHint= fLayouter.getAnnotationSize() + 2 * fLayouter.getBorderWidth() + 4; 505 fComposite.setLayoutData(data); 506 fComposite.addMouseTrackListener(new MouseTrackAdapter() { 507 508 @Override 509 public void mouseExit(MouseEvent e) { 510 if (fComposite == null) 511 return; 512 Rectangle bounds= null; 513 for (Control child : fComposite.getChildren()) { 514 if (bounds == null) { 515 bounds= child.getBounds(); 516 } else { 517 bounds.add(child.getBounds()); 518 } 519 if (bounds.contains(e.x, e.y)) 520 return; 521 } 522 523 // if none of the children contains the event, we leave the popup 524 dispose(); 525 } 526 527 }); 528 529 // fComposite.getVerticalBar().addListener(SWT.Selection, new Listener() { 530 // 531 // public void handleEvent(Event event) { 532 // Rectangle bounds= fShell.getBounds(); 533 // int x= bounds.x - fLayouter.getAnnotationSize() - fLayouter.getBorderWidth(); 534 // int y= bounds.y; 535 // fShell.setBounds(x, y, bounds.width, bounds.height); 536 // } 537 // 538 // }); 539 540 Cursor handCursor= getHandCursor(display); 541 fShell.setCursor(handCursor); 542 fComposite.setCursor(handCursor); 543 544 setInfoSystemColor(); 545 } 546 setInfoSystemColor()547 private void setInfoSystemColor() { 548 Display display= fShell.getDisplay(); 549 setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); 550 setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); 551 } 552 553 /* 554 * @see org.eclipse.jface.text.IInformationControl#setInformation(java.lang.String) 555 */ 556 @Override setInformation(String information)557 public void setInformation(String information) { 558 setInput(null); 559 } 560 561 562 /* 563 * @see org.eclipse.jface.text.IInformationControlExtension2#setInput(java.lang.Object) 564 */ 565 @Override setInput(Object input)566 public void setInput(Object input) { 567 if (fInput != null && fInput.fViewer != null) 568 fInput.fViewer.removeViewportListener(fViewportListener); 569 570 if (input instanceof AnnotationHoverInput) 571 fInput= (AnnotationHoverInput) input; 572 else 573 fInput= null; 574 575 inputChanged(fInput, null); 576 } 577 578 /** 579 * Internal hook method called when the input is 580 * initially set or subsequently changed. 581 * 582 * @param newInput the new input 583 * @param newSelection the new selection 584 */ inputChanged(Object newInput, Object newSelection)585 protected void inputChanged(Object newInput, Object newSelection) { 586 refresh(); 587 } 588 refresh()589 protected void refresh() { 590 adjustItemNumber(); 591 592 if (fInput == null) 593 return; 594 595 if (fInput.fAnnotations == null) 596 return; 597 598 if (fInput.fViewer != null) 599 fInput.fViewer.addViewportListener(fViewportListener); 600 601 fShell.setRegion(fLayouter.getShellRegion(fInput.fAnnotations.length)); 602 603 Layout layout= fLayouter.getLayout(fInput.fAnnotations.length); 604 fComposite.setLayout(layout); 605 606 Control[] children= fComposite.getChildren(); 607 for (int i= 0; i < fInput.fAnnotations.length; i++) { 608 Canvas canvas= (Canvas) children[i]; 609 Item item= new Item(); 610 item.canvas= canvas; 611 item.fAnnotation= fInput.fAnnotations[i]; 612 canvas.setData(item); 613 canvas.redraw(); 614 } 615 616 } 617 adjustItemNumber()618 protected void adjustItemNumber() { 619 if (fComposite == null) 620 return; 621 622 Control[] children= fComposite.getChildren(); 623 int oldSize= children.length; 624 int newSize= fInput == null ? 0 : fInput.fAnnotations.length; 625 626 Display display= fShell.getDisplay(); 627 628 // add missing items 629 for (int i= oldSize; i < newSize; i++) { 630 Canvas canvas= new Canvas(fComposite, SWT.NONE); 631 Object gridData= fLayouter.getLayoutData(); 632 canvas.setLayoutData(gridData); 633 canvas.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); 634 635 canvas.addPaintListener(fPaintListener); 636 637 canvas.addMouseTrackListener(fMouseTrackListener); 638 639 canvas.addMouseListener(fMouseListener); 640 641 canvas.addListener(SWT.MenuDetect, fMenuDetectListener); 642 643 canvas.addDisposeListener(fDisposeListener); 644 } 645 646 // dispose of exceeding resources 647 for (int i= oldSize; i > newSize; i--) { 648 Item item= (Item) children[i - 1].getData(); 649 item.deselect(); 650 children[i - 1].dispose(); 651 } 652 653 } 654 655 /* 656 * @see IInformationControl#setVisible(boolean) 657 */ 658 @Override setVisible(boolean visible)659 public void setVisible(boolean visible) { 660 fShell.setVisible(visible); 661 } 662 663 /* 664 * @see IInformationControl#dispose() 665 */ 666 @Override dispose()667 public void dispose() { 668 if (fShell != null) { 669 if (!fShell.isDisposed()) 670 fShell.dispose(); 671 fShell= null; 672 fComposite= null; 673 if (fHoverManager != null) 674 fHoverManager.dispose(); 675 fHoverManager= null; 676 fSelection= null; 677 } 678 } 679 680 /* 681 * @see org.eclipse.jface.text.IInformationControlExtension#hasContents() 682 */ 683 @Override hasContents()684 public boolean hasContents() { 685 return fInput.fAnnotations != null && fInput.fAnnotations.length > 0; 686 } 687 688 /* 689 * @see org.eclipse.jface.text.IInformationControl#setSizeConstraints(int, int) 690 */ 691 @Override setSizeConstraints(int maxWidth, int maxHeight)692 public void setSizeConstraints(int maxWidth, int maxHeight) { 693 //fMaxWidth= maxWidth; 694 //fMaxHeight= maxHeight; 695 } 696 697 /* 698 * @see org.eclipse.jface.text.IInformationControl#computeSizeHint() 699 */ 700 @Override computeSizeHint()701 public Point computeSizeHint() { 702 return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT); 703 } 704 705 /* 706 * @see IInformationControl#setLocation(Point) 707 */ 708 @Override setLocation(Point location)709 public void setLocation(Point location) { 710 fShell.setLocation(location); 711 } 712 713 /* 714 * @see IInformationControl#setSize(int, int) 715 */ 716 @Override setSize(int width, int height)717 public void setSize(int width, int height) { 718 fShell.setSize(width, height); 719 } 720 721 /* 722 * @see IInformationControl#addDisposeListener(DisposeListener) 723 */ 724 @Override addDisposeListener(DisposeListener listener)725 public void addDisposeListener(DisposeListener listener) { 726 fShell.addDisposeListener(listener); 727 } 728 729 /* 730 * @see IInformationControl#removeDisposeListener(DisposeListener) 731 */ 732 @Override removeDisposeListener(DisposeListener listener)733 public void removeDisposeListener(DisposeListener listener) { 734 fShell.removeDisposeListener(listener); 735 } 736 737 /* 738 * @see IInformationControl#setForegroundColor(Color) 739 */ 740 @Override setForegroundColor(Color foreground)741 public void setForegroundColor(Color foreground) { 742 fComposite.setForeground(foreground); 743 } 744 745 /* 746 * @see IInformationControl#setBackgroundColor(Color) 747 */ 748 @Override setBackgroundColor(Color background)749 public void setBackgroundColor(Color background) { 750 fComposite.setBackground(background); 751 } 752 753 /* 754 * @see IInformationControl#isFocusControl() 755 */ 756 @Override isFocusControl()757 public boolean isFocusControl() { 758 return fShell.getDisplay().getActiveShell() == fShell; 759 } 760 761 /* 762 * @see IInformationControl#setFocus() 763 */ 764 @Override setFocus()765 public void setFocus() { 766 fShell.forceFocus(); 767 } 768 769 /* 770 * @see IInformationControl#addFocusListener(FocusListener) 771 */ 772 @Override addFocusListener(FocusListener listener)773 public void addFocusListener(FocusListener listener) { 774 fShell.addFocusListener(listener); 775 } 776 777 /* 778 * @see IInformationControl#removeFocusListener(FocusListener) 779 */ 780 @Override removeFocusListener(FocusListener listener)781 public void removeFocusListener(FocusListener listener) { 782 fShell.removeFocusListener(listener); 783 } 784 setViewerBackground(Annotation annotation)785 private StyleRange[] setViewerBackground(Annotation annotation) { 786 StyledText text= fInput.fViewer.getTextWidget(); 787 if (text == null || text.isDisposed()) 788 return null; 789 790 Display disp= text.getDisplay(); 791 792 Position pos= fInput.model.getPosition(annotation); 793 if (pos == null) 794 return null; 795 796 IRegion region= ((TextViewer)fInput.fViewer).modelRange2WidgetRange(new Region(pos.offset, pos.length)); 797 if (region == null) 798 return null; 799 800 StyleRange[] ranges= text.getStyleRanges(region.getOffset(), region.getLength()); 801 802 List<StyleRange> undoRanges= new ArrayList<>(ranges.length); 803 for (StyleRange range : ranges) { 804 undoRanges.add((StyleRange) range.clone()); 805 } 806 807 int offset= region.getOffset(); 808 StyleRange current= undoRanges.size() > 0 ? undoRanges.get(0) : null; 809 int curStart= current != null ? current.start : region.getOffset() + region.getLength(); 810 int curEnd= current != null ? current.start + current.length : -1; 811 int index= 0; 812 813 // fill no-style regions 814 while (curEnd < region.getOffset() + region.getLength()) { 815 // add empty range 816 if (curStart > offset) { 817 StyleRange undoRange= new StyleRange(offset, curStart - offset, null, null); 818 undoRanges.add(index, undoRange); 819 index++; 820 } 821 822 // step 823 index++; 824 if (index < undoRanges.size()) { 825 offset= curEnd; 826 current= undoRanges.get(index); 827 curStart= current.start; 828 curEnd= current.start + current.length; 829 } else if (index == undoRanges.size()) { 830 // last one 831 offset= curEnd; 832 current= null; 833 curStart= region.getOffset() + region.getLength(); 834 curEnd= -1; 835 } else 836 curEnd= region.getOffset() + region.getLength(); 837 } 838 839 // create modified styles (with background) 840 List<StyleRange> shadedRanges= new ArrayList<>(undoRanges.size()); 841 for (StyleRange styleRange : undoRanges) { 842 StyleRange range= (StyleRange) styleRange.clone(); 843 shadedRanges.add(range); 844 range.background= getHighlightColor(disp); 845 } 846 847 // set the ranges one by one 848 for (StyleRange styleRange : shadedRanges) { 849 text.setStyleRange(styleRange); 850 851 } 852 853 return undoRanges.toArray(undoRanges.toArray(new StyleRange[0])); 854 } 855 resetViewerBackground(StyleRange[] oldRanges)856 private void resetViewerBackground(StyleRange[] oldRanges) { 857 858 if (oldRanges == null) 859 return; 860 861 if (fInput == null) 862 return; 863 864 StyledText text= fInput.fViewer.getTextWidget(); 865 if (text == null || text.isDisposed()) 866 return; 867 868 // set the ranges one by one 869 for (StyleRange oldRange : oldRanges) { 870 text.setStyleRange(oldRange); 871 } 872 } 873 getHighlightColor(Display disp)874 private Color getHighlightColor(Display disp) { 875 return disp.getSystemColor(SWT.COLOR_GRAY); 876 } 877 getSelectionColor(Display disp)878 private Color getSelectionColor(Display disp) { 879 return disp.getSystemColor(SWT.COLOR_GRAY); 880 } 881 getHandCursor(Display disp)882 private Cursor getHandCursor(Display disp) { 883 return disp.getSystemCursor(SWT.CURSOR_HAND); 884 } 885 886 /* 887 * @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int) 888 * @since 3.4 889 */ 890 @Override computeSizeConstraints(int widthInChars, int heightInChars)891 public Point computeSizeConstraints(int widthInChars, int heightInChars) { 892 return null; 893 } 894 895 /* 896 * @see org.eclipse.jface.text.IInformationControlExtension5#containsControl(org.eclipse.swt.widgets.Control) 897 * @since 3.4 898 */ 899 @Override containsControl(Control control)900 public boolean containsControl(Control control) { 901 do { 902 if (control == fShell) 903 return true; 904 if (control instanceof Shell) 905 return false; 906 control= control.getParent(); 907 } while (control != null); 908 return false; 909 } 910 911 /* 912 * @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator() 913 * @since 3.4 914 */ 915 @Override getInformationPresenterControlCreator()916 public IInformationControlCreator getInformationPresenterControlCreator() { 917 return null; 918 } 919 920 /* 921 * @see org.eclipse.jface.text.IInformationControlExtension5#isVisible() 922 */ 923 @Override isVisible()924 public boolean isVisible() { 925 return fShell != null && !fShell.isDisposed() && fShell.isVisible(); 926 } 927 928 } 929