KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > java > hover > BrowserInformationControl


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
12 package org.eclipse.jdt.internal.ui.text.java.hover;
13
14 import java.io.IOException JavaDoc;
15 import java.io.StringReader JavaDoc;
16 import java.util.Iterator JavaDoc;
17
18 import org.eclipse.core.runtime.ListenerList;
19
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.SWTError;
22 import org.eclipse.swt.browser.Browser;
23 import org.eclipse.swt.browser.LocationAdapter;
24 import org.eclipse.swt.browser.LocationEvent;
25 import org.eclipse.swt.custom.StyleRange;
26 import org.eclipse.swt.events.DisposeEvent;
27 import org.eclipse.swt.events.DisposeListener;
28 import org.eclipse.swt.events.FocusEvent;
29 import org.eclipse.swt.events.FocusListener;
30 import org.eclipse.swt.events.KeyEvent;
31 import org.eclipse.swt.events.KeyListener;
32 import org.eclipse.swt.graphics.Color;
33 import org.eclipse.swt.graphics.Font;
34 import org.eclipse.swt.graphics.FontData;
35 import org.eclipse.swt.graphics.Point;
36 import org.eclipse.swt.graphics.Rectangle;
37 import org.eclipse.swt.graphics.TextLayout;
38 import org.eclipse.swt.graphics.TextStyle;
39 import org.eclipse.swt.layout.GridData;
40 import org.eclipse.swt.layout.GridLayout;
41 import org.eclipse.swt.widgets.Composite;
42 import org.eclipse.swt.widgets.Display;
43 import org.eclipse.swt.widgets.Event;
44 import org.eclipse.swt.widgets.Label;
45 import org.eclipse.swt.widgets.Listener;
46 import org.eclipse.swt.widgets.Menu;
47 import org.eclipse.swt.widgets.Shell;
48
49 import org.eclipse.jface.text.IInformationControl;
50 import org.eclipse.jface.text.IInformationControlExtension;
51 import org.eclipse.jface.text.IInformationControlExtension3;
52 import org.eclipse.jface.text.TextPresentation;
53
54 import org.eclipse.jdt.internal.ui.JavaPlugin;
55 import org.eclipse.jdt.internal.ui.text.HTML2TextReader;
56 import org.eclipse.jdt.internal.ui.text.HTMLPrinter;
57 import org.eclipse.jdt.internal.ui.text.IInformationControlExtension4;
58
59
60 /**
61  * Displays textual information in a {@link org.eclipse.swt.browser.Browser}
62  * widget.
63  *
64  * <p>
65  * This class may be instantiated; it is not intended to be subclassed.
66  * </p>
67  * <p>
68  * Current problems:
69  * - the size computation is too small
70  * - focusLost event is not sent (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532)
71  * </p>
72  *
73  * @since 3.1
74  */

75 public class BrowserInformationControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension3, IInformationControlExtension4, DisposeListener {
76
77
78     /**
79      * Tells whether the SWT Browser widget and hence this information
80      * control is available.
81      *
82      * @param parent the parent component used for checking or <code>null</code> if none
83      * @return <code>true</code> if this control is available
84      */

85     public static boolean isAvailable(Composite parent) {
86         if (!fgAvailabilityChecked) {
87             try {
88                 if (parent == null)
89                     parent= JavaPlugin.getActiveWorkbenchShell();
90                 if (parent == null)
91                     return false; // don't store this value - try again later
92

93                 Browser browser= new Browser(parent, SWT.NONE);
94                 browser.dispose();
95                 fgIsAvailable= true;
96             } catch (SWTError er) {
97                 fgIsAvailable= false;
98             } finally {
99                 fgAvailabilityChecked= true;
100             }
101         }
102
103         return fgIsAvailable;
104     }
105
106
107     /** Border thickness in pixels. */
108     private static final int BORDER= 1;
109     
110     /**
111      * Minimal size constraints.
112      * @since 3.2
113      */

114     private static final int MIN_WIDTH= 80;
115     private static final int MIN_HEIGHT= 80;
116
117     
118     /**
119      * Availability checking cache.
120      */

121     private static boolean fgIsAvailable= false;
122     private static boolean fgAvailabilityChecked= false;
123
124     /** The control's shell */
125     private Shell fShell;
126     /** The control's browser widget */
127     private Browser fBrowser;
128     /** Tells whether the browser has content */
129     private boolean fBrowserHasContent;
130     /** The control width constraint */
131     private int fMaxWidth= -1;
132     /** The control height constraint */
133     private int fMaxHeight= -1;
134     private Font fStatusTextFont;
135     private Label fStatusTextField;
136     private String JavaDoc fStatusFieldText;
137     private boolean fHideScrollBars;
138     private Listener fDeactivateListener;
139     private ListenerList fFocusListeners= new ListenerList();
140     private Label fSeparator;
141     private String JavaDoc fInputText;
142     private TextLayout fTextLayout;
143
144     private TextStyle fBoldStyle;
145
146     /**
147      * Creates a default information control with the given shell as parent. The given
148      * information presenter is used to process the information to be displayed. The given
149      * styles are applied to the created styled text widget.
150      *
151      * @param parent the parent shell
152      * @param shellStyle the additional styles for the shell
153      * @param style the additional styles for the styled text widget
154      */

155     public BrowserInformationControl(Shell parent, int shellStyle, int style) {
156         this(parent, shellStyle, style, null);
157     }
158
159     /**
160      * Creates a default information control with the given shell as parent. The given
161      * information presenter is used to process the information to be displayed. The given
162      * styles are applied to the created styled text widget.
163      *
164      * @param parent the parent shell
165      * @param shellStyle the additional styles for the shell
166      * @param style the additional styles for the styled text widget
167      * @param statusFieldText the text to be used in the optional status field
168      * or <code>null</code> if the status field should be hidden
169      */

170     public BrowserInformationControl(Shell parent, int shellStyle, int style, String JavaDoc statusFieldText) {
171         fStatusFieldText= statusFieldText;
172         
173         fShell= new Shell(parent, SWT.NO_FOCUS | SWT.ON_TOP | shellStyle);
174         Display display= fShell.getDisplay();
175         fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
176         fTextLayout= new TextLayout(display);
177
178         Composite composite= fShell;
179         GridLayout layout= new GridLayout(1, false);
180         int border= ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER;
181         layout.marginHeight= border;
182         layout.marginWidth= border;
183         composite.setLayout(layout);
184         
185         if (statusFieldText != null) {
186             composite= new Composite(composite, SWT.NONE);
187             layout= new GridLayout(1, false);
188             layout.marginHeight= 0;
189             layout.marginWidth= 0;
190             layout.verticalSpacing= 1;
191             layout.horizontalSpacing= 1;
192             composite.setLayout(layout);
193             
194             GridData gd= new GridData(GridData.FILL_BOTH);
195             composite.setLayoutData(gd);
196             
197             composite.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
198             composite.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
199         }
200
201         // Browser field
202
fBrowser= new Browser(composite, SWT.NONE);
203         fHideScrollBars= (style & SWT.V_SCROLL) == 0 && (style & SWT.H_SCROLL) == 0;
204         
205         GridData gd= new GridData(GridData.BEGINNING | GridData.FILL_BOTH);
206         fBrowser.setLayoutData(gd);
207         
208         fBrowser.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
209         fBrowser.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
210         fBrowser.addKeyListener(new KeyListener() {
211
212             public void keyPressed(KeyEvent e) {
213                 if (e.character == 0x1B) // ESC
214
fShell.dispose();
215             }
216
217             public void keyReleased(KeyEvent e) {}
218         });
219         /*
220          * XXX revisit when the Browser support is better
221          * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=107629. Choosing a link to a
222          * non-available target will show an error dialog behind the ON_TOP shell that seemingly
223          * blocks the workbench. Disable links completely for now.
224          */

225         fBrowser.addLocationListener(new LocationAdapter() {
226             /*
227              * @see org.eclipse.swt.browser.LocationAdapter#changing(org.eclipse.swt.browser.LocationEvent)
228              */

229             public void changing(LocationEvent event) {
230                 String JavaDoc location= event.location;
231                 /*
232                  * Using the Browser.setText API triggers a location change to "about:blank" with
233                  * the mozilla widget. The Browser on carbon uses yet another kind of special
234                  * initialization URLs.
235                  * TODO remove this code once https://bugs.eclipse.org/bugs/show_bug.cgi?id=130314 is fixed
236                  */

237                 if (!"about:blank".equals(location) && !("carbon".equals(SWT.getPlatform()) && location.startsWith("applewebdata:"))) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
238
event.doit= false;
239             }
240         });
241
242         // Replace browser's built-in context menu with none
243
fBrowser.setMenu(new Menu(fShell, SWT.NONE));
244
245         // Status field
246
if (statusFieldText != null) {
247
248             fSeparator= new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT);
249             fSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
250
251             // Status field label
252
fStatusTextField= new Label(composite, SWT.RIGHT);
253             fStatusTextField.setText(statusFieldText);
254             Font font= fStatusTextField.getFont();
255             FontData[] fontDatas= font.getFontData();
256             for (int i= 0; i < fontDatas.length; i++)
257                 fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
258             fStatusTextFont= new Font(fStatusTextField.getDisplay(), fontDatas);
259             fStatusTextField.setFont(fStatusTextFont);
260             gd= new GridData(GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_BEGINNING);
261             fStatusTextField.setLayoutData(gd);
262
263             fStatusTextField.setForeground(display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW));
264
265             fStatusTextField.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
266         }
267
268         addDisposeListener(this);
269         createTextLayout();
270     }
271
272     /**
273      * Creates a default information control with the given shell as parent. The given
274      * information presenter is used to process the information to be displayed. The given
275      * styles are applied to the created styled text widget.
276      *
277      * @param parent the parent shell
278      * @param style the additional styles for the browser widget
279      */

280     public BrowserInformationControl(Shell parent,int style) {
281         this(parent, SWT.TOOL | SWT.NO_TRIM, style);
282     }
283
284     /**
285      * Creates a default information control with the given shell as parent.
286      * No information presenter is used to process the information
287      * to be displayed. No additional styles are applied to the styled text widget.
288      *
289      * @param parent the parent shell
290      */

291     public BrowserInformationControl(Shell parent) {
292         this(parent, SWT.NONE);
293     }
294
295
296     /*
297      * @see IInformationControl#setInformation(String)
298      */

299     public void setInformation(String JavaDoc content) {
300         fBrowserHasContent= content != null && content.length() > 0;
301
302         if (!fBrowserHasContent)
303             content= "<html><body ></html>"; //$NON-NLS-1$
304

305         fInputText= content;
306
307         int shellStyle= fShell.getStyle();
308         boolean RTL= (shellStyle & SWT.RIGHT_TO_LEFT) != 0;
309
310         String JavaDoc[] styles= null;
311         if (RTL && !fHideScrollBars)
312             styles= new String JavaDoc[] { "direction:rtl;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$
313
else if (RTL && fHideScrollBars)
314             styles= new String JavaDoc[] { "direction:rtl;", "overflow:hidden;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
315
else if (fHideScrollBars && true)
316             styles= new String JavaDoc[] { "overflow:hidden;", "word-wrap: break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$
317

318         if (styles != null) {
319             StringBuffer JavaDoc buffer= new StringBuffer JavaDoc(content);
320             HTMLPrinter.insertStyles(buffer, styles);
321             content= buffer.toString();
322         }
323         
324         fBrowser.setText(content);
325     
326     }
327
328     /*
329      * @see org.eclipse.jdt.internal.ui.text.IInformationControlExtension4#setStatusText(java.lang.String)
330      * @since 3.2
331      */

332     public void setStatusText(String JavaDoc statusFieldText) {
333         fStatusFieldText= statusFieldText;
334     }
335
336     /*
337      * @see IInformationControl#setVisible(boolean)
338      */

339     public void setVisible(boolean visible) {
340         if (fShell.isVisible() == visible)
341             return;
342         
343         if (visible) {
344             if (fStatusTextField != null) {
345                 boolean state= fStatusFieldText != null;
346                 if (state)
347                     fStatusTextField.setText(fStatusFieldText);
348                 fStatusTextField.setVisible(state);
349                 fSeparator.setVisible(state);
350             }
351         }
352
353         fShell.setVisible(visible);
354         if (!visible)
355             setInformation(""); //$NON-NLS-1$
356
}
357
358     /**
359      * Creates and initializes the text layout used
360      * to compute the size hint.
361      *
362      * @since 3.2
363      */

364     private void createTextLayout() {
365         fTextLayout= new TextLayout(fBrowser.getDisplay());
366         
367         // Initialize fonts
368
Font font= fBrowser.getFont();
369         fTextLayout.setFont(font);
370         fTextLayout.setWidth(-1);
371         FontData[] fontData= font.getFontData();
372         for (int i= 0; i < fontData.length; i++)
373             fontData[i].setStyle(SWT.BOLD);
374         font= new Font(fShell.getDisplay(), fontData);
375         fBoldStyle= new TextStyle(font, null, null);
376         
377         // Compute and set tab width
378
fTextLayout.setText(" "); //$NON-NLS-1$
379
int tabWidth = fTextLayout.getBounds().width;
380         fTextLayout.setTabs(new int[] {tabWidth});
381
382         fTextLayout.setText(""); //$NON-NLS-1$
383
}
384
385     /*
386      * @see IInformationControl#dispose()
387      */

388     public void dispose() {
389         fTextLayout.dispose();
390         fTextLayout= null;
391         fBoldStyle.font.dispose();
392         fBoldStyle= null;
393         if (fShell != null && !fShell.isDisposed())
394             fShell.dispose();
395         else
396             widgetDisposed(null);
397     }
398
399     /*
400      * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
401      */

402     public void widgetDisposed(DisposeEvent event) {
403         if (fStatusTextFont != null && !fStatusTextFont.isDisposed())
404             fStatusTextFont.dispose();
405
406         fShell= null;
407         fBrowser= null;
408         fStatusTextFont= null;
409     }
410
411     /*
412      * @see IInformationControl#setSize(int, int)
413      */

414     public void setSize(int width, int height) {
415         fShell.setSize(Math.min(width, fMaxWidth), Math.min(height, fMaxHeight));
416     }
417
418     /*
419      * @see IInformationControl#setLocation(Point)
420      */

421     public void setLocation(Point location) {
422         fShell.setLocation(location);
423     }
424
425     /*
426      * @see IInformationControl#setSizeConstraints(int, int)
427      */

428     public void setSizeConstraints(int maxWidth, int maxHeight) {
429         fMaxWidth= maxWidth;
430         fMaxHeight= maxHeight;
431     }
432
433     /*
434      * @see IInformationControl#computeSizeHint()
435      */

436     public Point computeSizeHint() {
437         TextPresentation presentation= new TextPresentation();
438         HTML2TextReader reader= new HTML2TextReader(new StringReader JavaDoc(fInputText), presentation);
439         String JavaDoc text;
440         try {
441             text= reader.getString();
442         } catch (IOException JavaDoc e) {
443             text= ""; //$NON-NLS-1$
444
}
445
446         fTextLayout.setText(text);
447         Iterator JavaDoc iter= presentation.getAllStyleRangeIterator();
448         while (iter.hasNext()) {
449             StyleRange sr= (StyleRange)iter.next();
450             if (sr.fontStyle == SWT.BOLD)
451                 fTextLayout.setStyle(fBoldStyle, sr.start, sr.start + sr.length - 1);
452         }
453         Rectangle bounds= fTextLayout.getBounds();
454         int width= bounds.width;
455         int height= bounds.height;
456         
457         width += 15;
458         height += 25;
459
460         if (fStatusFieldText != null && fSeparator != null) {
461             fTextLayout.setText(fStatusFieldText);
462             Rectangle statusBounds= fTextLayout.getBounds();
463             Rectangle separatorBounds= fSeparator.getBounds();
464             width= Math.max(width, statusBounds.width);
465             height= height + statusBounds.height + separatorBounds.height;
466         }
467
468         // Apply size constraints
469
if (fMaxWidth != SWT.DEFAULT)
470             width= Math.min(fMaxWidth, width);
471         if (fMaxHeight != SWT.DEFAULT)
472             height= Math.min(fMaxHeight, height);
473
474         // Ensure minimal size
475
width= Math.max(MIN_WIDTH, width);
476         height= Math.max(MIN_HEIGHT, height);
477         
478         return new Point(width, height);
479     }
480
481     /*
482      * @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
483      */

484     public Rectangle computeTrim() {
485         return fShell.computeTrim(0, 0, 0, 0);
486     }
487
488     /*
489      * @see org.eclipse.jface.text.IInformationControlExtension3#getBounds()
490      */

491     public Rectangle getBounds() {
492         return fShell.getBounds();
493     }
494
495     /*
496      * @see org.eclipse.jface.text.IInformationControlExtension3#restoresLocation()
497      */

498     public boolean restoresLocation() {
499         return false;
500     }
501
502     /*
503      * @see org.eclipse.jface.text.IInformationControlExtension3#restoresSize()
504      */

505     public boolean restoresSize() {
506         return false;
507     }
508
509     /*
510      * @see IInformationControl#addDisposeListener(DisposeListener)
511      */

512     public void addDisposeListener(DisposeListener listener) {
513         fShell.addDisposeListener(listener);
514     }
515
516     /*
517      * @see IInformationControl#removeDisposeListener(DisposeListener)
518      */

519     public void removeDisposeListener(DisposeListener listener) {
520         fShell.removeDisposeListener(listener);
521     }
522
523     /*
524      * @see IInformationControl#setForegroundColor(Color)
525      */

526     public void setForegroundColor(Color foreground) {
527         fBrowser.setForeground(foreground);
528     }
529
530     /*
531      * @see IInformationControl#setBackgroundColor(Color)
532      */

533     public void setBackgroundColor(Color background) {
534         fBrowser.setBackground(background);
535     }
536
537     /*
538      * @see IInformationControl#isFocusControl()
539      */

540     public boolean isFocusControl() {
541         return fBrowser.isFocusControl();
542     }
543
544     /*
545      * @see IInformationControl#setFocus()
546      */

547     public void setFocus() {
548         fShell.forceFocus();
549         fBrowser.setFocus();
550     }
551
552     /*
553      * @see IInformationControl#addFocusListener(FocusListener)
554      */

555     public void addFocusListener(final FocusListener listener) {
556         fBrowser.addFocusListener(listener);
557
558         /*
559          * FIXME: This is a workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532
560          * (Browser widget does not send focusLost event)
561          */

562         if (fFocusListeners.isEmpty()) {
563             fDeactivateListener= new Listener() {
564                 public void handleEvent(Event event) {
565                     Object JavaDoc[] listeners= fFocusListeners.getListeners();
566                     for (int i = 0; i < listeners.length; i++)
567                         ((FocusListener)listeners[i]).focusLost(new FocusEvent(event));
568                 }
569             };
570             fBrowser.getShell().addListener(SWT.Deactivate, fDeactivateListener);
571         }
572         fFocusListeners.add(listener);
573     }
574
575     /*
576      * @see IInformationControl#removeFocusListener(FocusListener)
577      */

578     public void removeFocusListener(FocusListener listener) {
579         fBrowser.removeFocusListener(listener);
580
581         /*
582          * FIXME: This is a workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532
583          * (Browser widget does not send focusLost event)
584          */

585         fFocusListeners.remove(listener);
586         if (fFocusListeners.isEmpty()) {
587             fBrowser.getShell().removeListener(SWT.Deactivate, fDeactivateListener);
588             fDeactivateListener= null;
589         }
590     }
591
592     /*
593      * @see IInformationControlExtension#hasContents()
594      */

595     public boolean hasContents() {
596         return fBrowserHasContent;
597     }
598 }
599
600
Popular Tags