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