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