KickJava   Java API By Example, From Geeks To Geeks.

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

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

82     public static boolean isAvailable(Composite parent) {
83         if (!fgAvailabilityChecked) {
84             try {
85                 Browser browser= new Browser(parent, SWT.NONE);
86                 browser.dispose();
87                 fgIsAvailable= true;
88             } catch (SWTError er) {
89                 fgIsAvailable= false;
90             } finally {
91                 fgAvailabilityChecked= true;
92             }
93         }
94
95         return fgIsAvailable;
96     }
97
98
99     /** Border thickness in pixels. */
100     private static final int BORDER= 1;
101     
102     /**
103      * Minimal size constraints.
104      * @since 3.2
105      */

106     private static final int MIN_WIDTH= 80;
107     private static final int MIN_HEIGHT= 80;
108
109     
110     /**
111      * Availability checking cache.
112      */

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

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

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

217         fBrowser.addLocationListener(new LocationAdapter() {
218             /*
219              * @see org.eclipse.swt.browser.LocationAdapter#changing(org.eclipse.swt.browser.LocationEvent)
220              */

221             public void changing(LocationEvent event) {
222                 String JavaDoc location= event.location;
223                 /*
224                  * Using the Browser.setText API triggers a location change to "about:blank" with
225                  * the mozilla widget. The Browser on carbon uses yet another kind of special
226                  * initialization URLs.
227                  * TODO remove this code once https://bugs.eclipse.org/bugs/show_bug.cgi?id=130314 is fixed
228                  */

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

272     public BrowserInformationControl(Shell parent,int style) {
273         this(parent, SWT.TOOL | SWT.NO_TRIM, style);
274     }
275
276     /**
277      * Creates a default information control with the given shell as parent.
278      * No information presenter is used to process the information
279      * to be displayed. No additional styles are applied to the styled text widget.
280      *
281      * @param parent the parent shell
282      */

283     public BrowserInformationControl(Shell parent) {
284         this(parent, SWT.NONE);
285     }
286
287
288     /*
289      * @see IInformationControl#setInformation(String)
290      */

291     public void setInformation(String JavaDoc content) {
292         fBrowserHasContent= content != null && content.length() > 0;
293
294         if (!fBrowserHasContent)
295             content= "<html><body ></html>"; //$NON-NLS-1$
296

297         fInputText= content;
298
299         int shellStyle= fShell.getStyle();
300         boolean RTL= (shellStyle & SWT.RIGHT_TO_LEFT) != 0;
301
302         String JavaDoc[] styles= null;
303         if (RTL && !fHideScrollBars)
304             styles= new String JavaDoc[] { "direction:rtl;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$
305
else if (RTL && fHideScrollBars)
306             styles= new String JavaDoc[] { "direction:rtl;", "overflow:hidden;", "word-wrap:break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
307
else if (fHideScrollBars && true)
308             styles= new String JavaDoc[] { "overflow:hidden;", "word-wrap: break-word;" }; //$NON-NLS-1$ //$NON-NLS-2$
309

310         content= content.replaceAll("overflow: auto;", ""); //$NON-NLS-1$ //$NON-NLS-2$
311

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

326     public void setStatusText(String JavaDoc statusFieldText) {
327         fStatusFieldText= statusFieldText;
328     }
329
330     /*
331      * @see IInformationControl#setVisible(boolean)
332      */

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

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

382     public void dispose() {
383         fTextLayout.dispose();
384         fTextLayout= null;
385         fBoldStyle.font.dispose();
386         fBoldStyle= null;
387         if (fShell != null && !fShell.isDisposed())
388             fShell.dispose();
389         else
390             widgetDisposed(null);
391     }
392
393     /*
394      * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
395      */

396     public void widgetDisposed(DisposeEvent event) {
397         if (fStatusTextFont != null && !fStatusTextFont.isDisposed())
398             fStatusTextFont.dispose();
399
400         fShell= null;
401         fBrowser= null;
402         fStatusTextFont= null;
403     }
404
405     /*
406      * @see IInformationControl#setSize(int, int)
407      */

408     public void setSize(int width, int height) {
409         fShell.setSize(Math.min(width, fMaxWidth), Math.min(height, fMaxHeight));
410     }
411
412     /*
413      * @see IInformationControl#setLocation(Point)
414      */

415     public void setLocation(Point location) {
416         fShell.setLocation(location);
417     }
418
419     /*
420      * @see IInformationControl#setSizeConstraints(int, int)
421      */

422     public void setSizeConstraints(int maxWidth, int maxHeight) {
423         fMaxWidth= maxWidth;
424         fMaxHeight= maxHeight;
425     }
426
427     /*
428      * @see IInformationControl#computeSizeHint()
429      */

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

478     public Rectangle computeTrim() {
479         return fShell.computeTrim(0, 0, 0, 0);
480     }
481
482     /*
483      * @see org.eclipse.jface.text.IInformationControlExtension3#getBounds()
484      */

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

492     public boolean restoresLocation() {
493         return false;
494     }
495
496     /*
497      * @see org.eclipse.jface.text.IInformationControlExtension3#restoresSize()
498      */

499     public boolean restoresSize() {
500         return false;
501     }
502
503     /*
504      * @see IInformationControl#addDisposeListener(DisposeListener)
505      */

506     public void addDisposeListener(DisposeListener listener) {
507         fShell.addDisposeListener(listener);
508     }
509
510     /*
511      * @see IInformationControl#removeDisposeListener(DisposeListener)
512      */

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

520     public void setForegroundColor(Color foreground) {
521         fBrowser.setForeground(foreground);
522     }
523
524     /*
525      * @see IInformationControl#setBackgroundColor(Color)
526      */

527     public void setBackgroundColor(Color background) {
528         fBrowser.setBackground(background);
529     }
530
531     /*
532      * @see IInformationControl#isFocusControl()
533      */

534     public boolean isFocusControl() {
535         return fBrowser.isFocusControl();
536     }
537
538     /*
539      * @see IInformationControl#setFocus()
540      */

541     public void setFocus() {
542         fShell.forceFocus();
543         fBrowser.setFocus();
544     }
545
546     /*
547      * @see IInformationControl#addFocusListener(FocusListener)
548      */

549     public void addFocusListener(final FocusListener listener) {
550         fBrowser.addFocusListener(listener);
551
552         /*
553          * FIXME: This is a workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532
554          * (Browser widget does not send focusLost event)
555          */

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

572     public void removeFocusListener(FocusListener listener) {
573         fBrowser.removeFocusListener(listener);
574
575         /*
576          * FIXME: This is a workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532
577          * (Browser widget does not send focusLost event)
578          */

579         fFocusListeners.remove(listener);
580         if (fFocusListeners.isEmpty()) {
581             fBrowser.getShell().removeListener(SWT.Deactivate, fDeactivateListener);
582             fDeactivateListener= null;
583         }
584     }
585
586     /*
587      * @see IInformationControlExtension#hasContents()
588      */

589     public boolean hasContents() {
590         return fBrowserHasContent;
591     }
592 }
593
Popular Tags