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