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