KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > pde > internal > ui > editor > contentassist > display > BrowserInformationControl


1 /*******************************************************************************
2  * Copyright (c) 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.pde.internal.ui.editor.contentassist.display;
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.pde.internal.ui.PDEPlugin;
21 import org.eclipse.pde.internal.ui.editor.text.HTMLPrinter;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.SWTError;
24 import org.eclipse.swt.browser.Browser;
25 import org.eclipse.swt.browser.LocationAdapter;
26 import org.eclipse.swt.browser.LocationEvent;
27 import org.eclipse.swt.custom.StyleRange;
28 import org.eclipse.swt.events.DisposeEvent;
29 import org.eclipse.swt.events.DisposeListener;
30 import org.eclipse.swt.events.FocusEvent;
31 import org.eclipse.swt.events.FocusListener;
32 import org.eclipse.swt.events.KeyEvent;
33 import org.eclipse.swt.events.KeyListener;
34 import org.eclipse.swt.graphics.Color;
35 import org.eclipse.swt.graphics.Font;
36 import org.eclipse.swt.graphics.FontData;
37 import org.eclipse.swt.graphics.Point;
38 import org.eclipse.swt.graphics.Rectangle;
39 import org.eclipse.swt.graphics.TextLayout;
40 import org.eclipse.swt.graphics.TextStyle;
41 import org.eclipse.swt.layout.GridData;
42 import org.eclipse.swt.layout.GridLayout;
43 import org.eclipse.swt.widgets.Composite;
44 import org.eclipse.swt.widgets.Display;
45 import org.eclipse.swt.widgets.Event;
46 import org.eclipse.swt.widgets.Label;
47 import org.eclipse.swt.widgets.Listener;
48 import org.eclipse.swt.widgets.Menu;
49 import org.eclipse.swt.widgets.Shell;
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 /**
58  * Displays textual information in a {@link org.eclipse.swt.browser.Browser}
59  * widget.
60  *
61  * <p>
62  * This class may be instantiated; it is not intended to be subclassed.
63  * </p>
64  * <p>
65  * Current problems:
66  * - the size computation is too small
67  * - focusLost event is not sent (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532)
68  * </p>
69  *
70  * @since 3.1
71  */

72 public 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                 if (parent == null)
86                     parent= PDEPlugin.getActiveWorkbenchShell();
87                 if (parent == null)
88                     return false; // don't store this value - try again later
89

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

111     private static final int MIN_WIDTH= 80;
112     private static final int MIN_HEIGHT= 80;
113
114     
115     /**
116      * Availability checking cache.
117      */

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

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

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

222         fBrowser.addLocationListener(new LocationAdapter() {
223             /*
224              * @see org.eclipse.swt.browser.LocationAdapter#changing(org.eclipse.swt.browser.LocationEvent)
225              */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

502     public boolean restoresSize() {
503         return false;
504     }
505
506     /*
507      * @see IInformationControl#addDisposeListener(DisposeListener)
508      */

509     public void addDisposeListener(DisposeListener listener) {
510         fShell.addDisposeListener(listener);
511     }
512
513     /*
514      * @see IInformationControl#removeDisposeListener(DisposeListener)
515      */

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

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

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

537     public boolean isFocusControl() {
538         return fBrowser.isFocusControl();
539     }
540
541     /*
542      * @see IInformationControl#setFocus()
543      */

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

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

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

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

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

592     public boolean hasContents() {
593         return fBrowserHasContent;
594     }
595 }
596
597
Popular Tags