comparison org.eclipse.jface.text/src/org/eclipse/jface/internal/text/html/BrowserInformationControl.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children dbfb303e8fb0
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
1 /*******************************************************************************
2 * Copyright (c) 2000, 2008 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.jface.internal.text.html.BrowserInformationControl;
14
15 import org.eclipse.jface.internal.text.html.HTML2TextReader; // packageimport
16 import org.eclipse.jface.internal.text.html.HTMLPrinter; // packageimport
17 import org.eclipse.jface.internal.text.html.SubstitutionTextReader; // packageimport
18 import org.eclipse.jface.internal.text.html.HTMLTextPresenter; // packageimport
19 import org.eclipse.jface.internal.text.html.BrowserInput; // packageimport
20 import org.eclipse.jface.internal.text.html.SingleCharReader; // packageimport
21 import org.eclipse.jface.internal.text.html.BrowserInformationControlInput; // packageimport
22 import org.eclipse.jface.internal.text.html.HTMLMessages; // packageimport
23
24 import java.lang.all;
25 import java.util.Iterator;
26 import java.util.Set;
27
28 import org.eclipse.swt.widgets.Shell;
29 import org.eclipse.swt.widgets.Composite;
30 import org.eclipse.jface.text.AbstractInformationControl;
31 import org.eclipse.jface.text.IInformationControlExtension2;
32 import org.eclipse.jface.text.IDelayedInputChangeProvider;
33 import org.eclipse.jface.text.IInputChangedListener;
34
35 //pragma( msg, "org.eclipse.jface.internal.text.html.BrowserInformationControl: SWT browser control missing" );
36 class BrowserInformationControl : AbstractInformationControl , IInformationControlExtension2, IDelayedInputChangeProvider {
37 public this(Shell parent, String symbolicFontName, bool resizable){
38 super(parent, resizable);
39 implMissing(__FILE__,__LINE__);
40 }
41 public static bool isAvailable(Composite parent){
42 implMissing(__FILE__,__LINE__);
43 return false;
44 }
45 public void setInformation(String content) {
46 implMissing(__FILE__,__LINE__);
47 }
48 protected void createContent(Composite parent) {
49 implMissing(__FILE__,__LINE__);
50 }
51 public void setInput(Object input) {
52 implMissing(__FILE__,__LINE__);
53 }
54 public void setDelayedInputChangeListener(IInputChangedListener inputChangeListener) {
55 implMissing(__FILE__,__LINE__);
56 }
57 public bool hasContents() {
58 implMissing(__FILE__,__LINE__);
59 return false;
60 }
61 }
62
63 // FIXME needs Browser :/
64 /++
65
66 import org.eclipse.dwtxhelper.StringReader;
67
68 import org.eclipse.swt.SWT;
69 import org.eclipse.swt.SWTError;
70 import org.eclipse.swt.browser.Browser;
71 import org.eclipse.swt.browser.LocationListener;
72 import org.eclipse.swt.browser.ProgressAdapter;
73 import org.eclipse.swt.browser.ProgressEvent;
74 import org.eclipse.swt.custom.StyleRange;
75 import org.eclipse.swt.events.KeyEvent;
76 import org.eclipse.swt.events.KeyListener;
77 import org.eclipse.swt.graphics.Color;
78 import org.eclipse.swt.graphics.Font;
79 import org.eclipse.swt.graphics.FontData;
80 import org.eclipse.swt.graphics.GC;
81 import org.eclipse.swt.graphics.Point;
82 import org.eclipse.swt.graphics.Rectangle;
83 import org.eclipse.swt.graphics.TextLayout;
84 import org.eclipse.swt.graphics.TextStyle;
85 import org.eclipse.swt.widgets.Composite;
86 import org.eclipse.swt.widgets.Display;
87 import org.eclipse.swt.widgets.Menu;
88 import org.eclipse.swt.widgets.Shell;
89 import org.eclipse.swt.widgets.Slider;
90 import org.eclipse.core.runtime.Assert;
91 import org.eclipse.core.runtime.ListenerList;
92 import org.eclipse.jface.action.ToolBarManager;
93 import org.eclipse.jface.resource.JFaceResources;
94 import org.eclipse.jface.text.AbstractInformationControl;
95 import org.eclipse.jface.text.IDelayedInputChangeProvider;
96 import org.eclipse.jface.text.IInformationControlExtension2;
97 import org.eclipse.jface.text.IInputChangedListener;
98 import org.eclipse.jface.text.TextPresentation;
99
100
101 /**
102 * Displays HTML information in a {@link org.eclipse.swt.browser.Browser} widget.
103 * <p>
104 * This {@link IInformationControlExtension2} expects {@link #setInput(Object)} to be
105 * called with an argument of type {@link BrowserInformationControlInput}.
106 * </p>
107 * <p>
108 * Moved into this package from <code>org.eclipse.jface.internal.text.revisions</code>.</p>
109 * <p>
110 * This class may be instantiated; it is not intended to be subclassed.</p>
111 * <p>
112 * Current problems:
113 * <ul>
114 * <li>the size computation is too small</li>
115 * <li>focusLost event is not sent - see https://bugs.eclipse.org/bugs/show_bug.cgi?id=84532</li>
116 * </ul>
117 * </p>
118 *
119 * @since 3.2
120 */
121 public class BrowserInformationControl : AbstractInformationControl , IInformationControlExtension2, IDelayedInputChangeProvider {
122
123
124 /**
125 * Tells whether the SWT Browser widget and hence this information
126 * control is available.
127 *
128 * @param parent the parent component used for checking or <code>null</code> if none
129 * @return <code>true</code> if this control is available
130 */
131 public static bool isAvailable(Composite parent) {
132 if (!fgAvailabilityChecked) {
133 try {
134 Browser browser= new Browser(parent, SWT.NONE);
135 browser.dispose();
136 fgIsAvailable= true;
137
138 Slider sliderV= new Slider(parent, SWT.VERTICAL);
139 Slider sliderH= new Slider(parent, SWT.HORIZONTAL);
140 int width= sliderV.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
141 int height= sliderH.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
142 fgScrollBarSize= new Point(width, height);
143 sliderV.dispose();
144 sliderH.dispose();
145 } catch (SWTError er) {
146 fgIsAvailable= false;
147 } finally {
148 fgAvailabilityChecked= true;
149 }
150 }
151
152 return fgIsAvailable;
153 }
154
155
156 /**
157 * Minimal size constraints.
158 * @since 3.2
159 */
160 private static const int MIN_WIDTH= 80;
161 private static const int MIN_HEIGHT= 50;
162
163
164 /**
165 * Availability checking cache.
166 */
167 private static bool fgIsAvailable= false;
168 private static bool fgAvailabilityChecked= false;
169
170 /**
171 * Cached scroll bar width and height
172 * @since 3.4
173 */
174 private static Point fgScrollBarSize;
175
176 /** The control's browser widget */
177 private Browser fBrowser;
178 /** Tells whether the browser has content */
179 private bool fBrowserHasContent;
180 /** Text layout used to approximate size of content when rendered in browser */
181 private TextLayout fTextLayout;
182 /** Bold text style */
183 private TextStyle fBoldStyle;
184
185 private BrowserInformationControlInput fInput;
186
187 /**
188 * <code>true</code> iff the browser has completed loading of the last
189 * input set via {@link #setInformation(String)}.
190 * @since 3.4
191 */
192 private bool fCompleted= false;
193
194 /**
195 * The listener to be notified when a delayed location changing event happened.
196 * @since 3.4
197 */
198 private IInputChangedListener fDelayedInputChangeListener;
199
200 /**
201 * The listeners to be notified when the input changed.
202 * @since 3.4
203 */
204 private ListenerList/*<IInputChangedListener>*/ fInputChangeListeners= new ListenerList(ListenerList.IDENTITY);
205
206 /**
207 * The symbolic name of the font used for size computations, or <code>null</code> to use dialog font.
208 * @since 3.4
209 */
210 private const String fSymbolicFontName;
211
212
213 /**
214 * Creates a browser information control with the given shell as parent.
215 *
216 * @param parent the parent shell
217 * @param symbolicFontName the symbolic name of the font used for size computations
218 * @param resizable <code>true</code> if the control should be resizable
219 * @since 3.4
220 */
221 public this(Shell parent, String symbolicFontName, bool resizable) {
222 super(parent, resizable);
223 fSymbolicFontName= symbolicFontName;
224 create();
225 }
226
227 /**
228 * Creates a browser information control with the given shell as parent.
229 *
230 * @param parent the parent shell
231 * @param symbolicFontName the symbolic name of the font used for size computations
232 * @param statusFieldText the text to be used in the optional status field
233 * or <code>null</code> if the status field should be hidden
234 * @since 3.4
235 */
236 public this(Shell parent, String symbolicFontName, String statusFieldText) {
237 super(parent, statusFieldText);
238 fSymbolicFontName= symbolicFontName;
239 create();
240 }
241
242 /**
243 * Creates a browser information control with the given shell as parent.
244 *
245 * @param parent the parent shell
246 * @param symbolicFontName the symbolic name of the font used for size computations
247 * @param toolBarManager the manager or <code>null</code> if toolbar is not desired
248 * @since 3.4
249 */
250 public this(Shell parent, String symbolicFontName, ToolBarManager toolBarManager) {
251 super(parent, toolBarManager);
252 fSymbolicFontName= symbolicFontName;
253 create();
254 }
255
256 /*
257 * @see org.eclipse.jface.text.AbstractInformationControl#createContent(org.eclipse.swt.widgets.Composite)
258 */
259 protected void createContent(Composite parent) {
260 fBrowser= new Browser(parent, SWT.NONE);
261
262 Display display= getShell().getDisplay();
263 fBrowser.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
264 fBrowser.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
265 fBrowser.addKeyListener(new class() KeyListener {
266
267 public void keyPressed(KeyEvent e) {
268 if (e.character is 0x1B) // ESC
269 getShell().dispose(); // XXX: Just hide? Would avoid constant recreations.
270 }
271
272 public void keyReleased(KeyEvent e) {}
273 });
274
275 fBrowser.addProgressListener(new class() ProgressAdapter {
276 public void completed(ProgressEvent event) {
277 fCompleted= true;
278 }
279 });
280
281 // Replace browser's built-in context menu with none
282 fBrowser.setMenu(new Menu(getShell(), SWT.NONE));
283
284 createTextLayout();
285 }
286
287 /**
288 * {@inheritDoc}
289 * @deprecated use {@link #setInput(Object)}
290 */
291 public void setInformation(String content) {
292 setInput(new class(null,content) BrowserInformationControlInput {
293 String content_;
294 this(BrowserInformationControlInput input, String a){
295 super(input);
296 content_=a;
297 }
298 public String getHtml() {
299 return content_;
300 }
301
302 public String getInputName() {
303 return ""; //$NON-NLS-1$
304 }
305
306 public Object getInputElement() {
307 return stringcast(content_);
308 }
309 });
310 }
311
312 /**
313 * {@inheritDoc} This control can handle {@link String} and
314 * {@link BrowserInformationControlInput}.
315 */
316 public void setInput(Object input) {
317 Assert.isLegal(input is null || cast(String)input || cast(BrowserInformationControlInput)input );
318
319 if ( cast(String)input ) {
320 setInformation(cast(String)input);
321 return;
322 }
323
324 fInput= cast(BrowserInformationControlInput)input;
325
326 String content= null;
327 if (fInput !is null)
328 content= fInput.getHtml();
329
330 fBrowserHasContent= content !is null && content.length() > 0;
331
332 if (!fBrowserHasContent)
333 content= "<html><body ></html>"; //$NON-NLS-1$
334
335 bool RTL= (getShell().getStyle() & SWT.RIGHT_TO_LEFT) !is 0;
336 bool resizable= isResizable();
337
338 // The default "overflow:auto" would not result in a predictable width for the client area
339 // and the re-wrapping would cause visual noise
340 String[] styles= null;
341 if (RTL && resizable)
342 styles= [ "direction:rtl;", "overflow:scroll;", "word-wrap:break-word;" ]; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
343 else if (RTL && !resizable)
344 styles= [ "direction:rtl;", "overflow:hidden;", "word-wrap:break-word;" ]; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
345 else if (!resizable)
346 //XXX: In IE, "word-wrap: break-word;" causes bogus wrapping even in non-broken words :-(see e.g. Javadoc of String).
347 // Re-check whether we really still need this now that the Javadoc Hover header already sets this style.
348 styles= [ "overflow:hidden;"/*, "word-wrap: break-word;"*/ ]; //$NON-NLS-1$
349 else
350 styles= [ "overflow:scroll;" ]; //$NON-NLS-1$
351
352 StringBuffer buffer= new StringBuffer(content);
353 HTMLPrinter.insertStyles(buffer, styles);
354 content= buffer.toString();
355
356 /*
357 * XXX: Should add some JavaScript here that shows something like
358 * "(continued...)" or "..." at the end of the visible area when the page overflowed
359 * with "overflow:hidden;".
360 */
361
362 fCompleted= false;
363 fBrowser.setText(content);
364
365 Object[] listeners= fInputChangeListeners.getListeners();
366 for (int i= 0; i < listeners.length; i++)
367 (cast(IInputChangedListener)listeners[i]).inputChanged(fInput);
368 }
369
370 /*
371 * @see IInformationControl#setVisible(bool)
372 */
373 public void setVisible(bool visible) {
374 Shell shell= getShell();
375 if (shell.isVisible() is visible)
376 return;
377
378 if (!visible) {
379 super.setVisible(false);
380 setInput(null);
381 return;
382 }
383
384 /*
385 * The Browser widget flickers when made visible while it is not completely loaded.
386 * The fix is to delay the call to setVisible until either loading is completed
387 * (see ProgressListener in constructor), or a timeout has been reached.
388 */
389 final Display display= shell.getDisplay();
390
391 // Make sure the display wakes from sleep after timeout:
392 display.timerExec(100, new class() Runnable {
393 public void run() {
394 fCompleted= true;
395 }
396 });
397
398 while (!fCompleted) {
399 // Drive the event loop to process the events required to load the browser widget's contents:
400 if (!display.readAndDispatch()) {
401 display.sleep();
402 }
403 }
404
405 shell= getShell();
406 if (shell is null || shell.isDisposed())
407 return;
408
409 /*
410 * Avoids flickering when replacing hovers, especially on Vista in ON_CLICK mode.
411 * Causes flickering on GTK. Carbon does not care.
412 */
413 if ("win32".equals(SWT.getPlatform())) //$NON-NLS-1$
414 shell.moveAbove(null);
415
416 super.setVisible(true);
417 }
418
419 /*
420 * @see org.eclipse.jface.text.AbstractInformationControl#setSize(int, int)
421 */
422 public void setSize(int width, int height) {
423 fBrowser.setRedraw(false); // avoid flickering
424 try {
425 super.setSize(width, height);
426 } finally {
427 fBrowser.setRedraw(true);
428 }
429 }
430
431 /**
432 * Creates and initializes the text layout used
433 * to compute the size hint.
434 *
435 * @since 3.2
436 */
437 private void createTextLayout() {
438 fTextLayout= new TextLayout(fBrowser.getDisplay());
439
440 // Initialize fonts
441 Font font= fSymbolicFontName is null ? JFaceResources.getDialogFont() : JFaceResources.getFont(fSymbolicFontName);
442 fTextLayout.setFont(font);
443 fTextLayout.setWidth(-1);
444 FontData[] fontData= font.getFontData();
445 for (int i= 0; i < fontData.length; i++)
446 fontData[i].setStyle(SWT.BOLD);
447 font= new Font(getShell().getDisplay(), fontData);
448 fBoldStyle= new TextStyle(font, null, null);
449
450 // Compute and set tab width
451 fTextLayout.setText(" "); //$NON-NLS-1$
452 int tabWidth = fTextLayout.getBounds().width;
453 fTextLayout.setTabs([tabWidth]);
454
455 fTextLayout.setText(""); //$NON-NLS-1$
456 }
457
458 /*
459 * @see IInformationControl#dispose()
460 */
461 public void dispose() {
462 if (fTextLayout !is null) {
463 fTextLayout.dispose();
464 fTextLayout= null;
465 }
466 if (fBoldStyle !is null) {
467 fBoldStyle.font.dispose();
468 fBoldStyle= null;
469 }
470 fBrowser= null;
471
472 super.dispose();
473 }
474
475 /*
476 * @see IInformationControl#computeSizeHint()
477 */
478 public Point computeSizeHint() {
479 Point sizeConstraints= getSizeConstraints();
480 Rectangle trim= computeTrim();
481 int height= trim.height;
482
483 //FIXME: The HTML2TextReader does not render <p> like a browser.
484 // Instead of inserting an empty line, it just adds a single line break.
485 // Furthermore, the indentation of <dl><dd> elements is too small (e.g with a long @see line)
486 TextPresentation presentation= new TextPresentation();
487 HTML2TextReader reader= new HTML2TextReader(new StringReader(fInput.getHtml()), presentation);
488 String text;
489 try {
490 text= reader.getString();
491 } catch (IOException e) {
492 text= ""; //$NON-NLS-1$
493 }
494
495 fTextLayout.setText(text);
496 fTextLayout.setWidth(sizeConstraints is null ? SWT.DEFAULT : sizeConstraints.x - trim.width);
497 Iterator iter= presentation.getAllStyleRangeIterator();
498 while (iter.hasNext()) {
499 StyleRange sr= cast(StyleRange)iter.next();
500 if (sr.fontStyle is SWT.BOLD)
501 fTextLayout.setStyle(fBoldStyle, sr.start, sr.start + sr.length - 1);
502 }
503
504 Rectangle bounds= fTextLayout.getBounds(); // does not return minimum width, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=217446
505 int lineCount= fTextLayout.getLineCount();
506 int textWidth= 0;
507 for (int i= 0; i < lineCount; i++) {
508 Rectangle rect= fTextLayout.getLineBounds(i);
509 int lineWidth= rect.x + rect.width;
510 if (i is 0)
511 lineWidth += fInput.getLeadingImageWidth();
512 textWidth= Math.max(textWidth, lineWidth);
513 }
514 bounds.width= textWidth;
515 fTextLayout.setText(""); //$NON-NLS-1$
516
517 int minWidth= bounds.width;
518 height= height + bounds.height;
519
520 // Add some air to accommodate for different browser renderings
521 minWidth+= 15;
522 height+= 15;
523
524
525 // Apply max size constraints
526 if (sizeConstraints !is null) {
527 if (sizeConstraints.x !is SWT.DEFAULT)
528 minWidth= Math.min(sizeConstraints.x, minWidth + trim.width);
529 if (sizeConstraints.y !is SWT.DEFAULT)
530 height= Math.min(sizeConstraints.y, height);
531 }
532
533 // Ensure minimal size
534 int width= Math.max(MIN_WIDTH, minWidth);
535 height= Math.max(MIN_HEIGHT, height);
536
537 return new Point(width, height);
538 }
539
540 /*
541 * @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
542 */
543 public Rectangle computeTrim() {
544 Rectangle trim= super.computeTrim();
545 if (isResizable()) {
546 bool RTL= (getShell().getStyle() & SWT.RIGHT_TO_LEFT) !is 0;
547 if (RTL) {
548 trim.x-= fgScrollBarSize.x;
549 }
550 trim.width+= fgScrollBarSize.x;
551 trim.height+= fgScrollBarSize.y;
552 }
553 return trim;
554 }
555
556 /**
557 * Adds the listener to the collection of listeners who will be
558 * notified when the current location has changed or is about to change.
559 *
560 * @param listener the location listener
561 * @since 3.4
562 */
563 public void addLocationListener(LocationListener listener) {
564 fBrowser.addLocationListener(listener);
565 }
566
567 /*
568 * @see IInformationControl#setForegroundColor(Color)
569 */
570 public void setForegroundColor(Color foreground) {
571 super.setForegroundColor(foreground);
572 fBrowser.setForeground(foreground);
573 }
574
575 /*
576 * @see IInformationControl#setBackgroundColor(Color)
577 */
578 public void setBackgroundColor(Color background) {
579 super.setBackgroundColor(background);
580 fBrowser.setBackground(background);
581 }
582
583 /*
584 * @see IInformationControlExtension#hasContents()
585 */
586 public bool hasContents() {
587 return fBrowserHasContent;
588 }
589
590 /**
591 * Adds a listener for input changes to this input change provider.
592 * Has no effect if an identical listener is already registered.
593 *
594 * @param inputChangeListener the listener to add
595 * @since 3.4
596 */
597 public void addInputChangeListener(IInputChangedListener inputChangeListener) {
598 Assert.isNotNull(inputChangeListener);
599 fInputChangeListeners.add(inputChangeListener);
600 }
601
602 /**
603 * Removes the given input change listener from this input change provider.
604 * Has no effect if an identical listener is not registered.
605 *
606 * @param inputChangeListener the listener to remove
607 * @since 3.4
608 */
609 public void removeInputChangeListener(IInputChangedListener inputChangeListener) {
610 fInputChangeListeners.remove(inputChangeListener);
611 }
612
613 /*
614 * @see org.eclipse.jface.text.IDelayedInputChangeProvider#setDelayedInputChangeListener(org.eclipse.jface.text.IInputChangedListener)
615 * @since 3.4
616 */
617 public void setDelayedInputChangeListener(IInputChangedListener inputChangeListener) {
618 fDelayedInputChangeListener= inputChangeListener;
619 }
620
621 /**
622 * Tells whether a delayed input change listener is registered.
623 *
624 * @return <code>true</code> iff a delayed input change
625 * listener is currently registered
626 * @since 3.4
627 */
628 public bool hasDelayedInputChangeListener() {
629 return fDelayedInputChangeListener !is null;
630 }
631
632 /**
633 * Notifies listeners of a delayed input change.
634 *
635 * @param newInput the new input, or <code>null</code> to request cancellation
636 * @since 3.4
637 */
638 public void notifyDelayedInputChange(Object newInput) {
639 if (fDelayedInputChangeListener !is null)
640 fDelayedInputChangeListener.inputChanged(newInput);
641 }
642
643 /*
644 * @see java.lang.Object#toString()
645 * @since 3.4
646 */
647 public override String toString() {
648 String style= (getShell().getStyle() & SWT.RESIZE) is 0 ? "fixed" : "resizeable"; //$NON-NLS-1$ //$NON-NLS-2$
649 return super.toString() + " - style: " + style; //$NON-NLS-1$
650 }
651
652 /**
653 * @return the current browser input or <code>null</code>
654 */
655 public BrowserInformationControlInput getInput() {
656 return fInput;
657 }
658
659 /*
660 * @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
661 */
662 public Point computeSizeConstraints(int widthInChars, int heightInChars) {
663 if (fSymbolicFontName is null)
664 return null;
665
666 GC gc= new GC(fBrowser);
667 Font font= fSymbolicFontName is null ? JFaceResources.getDialogFont() : JFaceResources.getFont(fSymbolicFontName);
668 gc.setFont(font);
669 int width= gc.getFontMetrics().getAverageCharWidth();
670 int height= gc.getFontMetrics().getHeight();
671 gc.dispose();
672
673 return new Point(widthInChars * width, heightInChars * height);
674 }
675
676 }
677
678 ++/