]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/SWTAWTComponent.java
Sync git svn branch with SVN repository r33269.
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / SWTAWTComponent.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2013 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *     Semantum Oy - workaround for Simantics issue #3518\r
12  *******************************************************************************/\r
13 package org.simantics.utils.ui;\r
14 \r
15 import java.awt.AWTEvent;\r
16 import java.awt.Component;\r
17 import java.awt.Container;\r
18 import java.awt.EventQueue;\r
19 import java.awt.Frame;\r
20 import java.awt.GridLayout;\r
21 import java.awt.Toolkit;\r
22 import java.awt.event.AWTEventListener;\r
23 import java.awt.event.MouseEvent;\r
24 import java.awt.event.WindowAdapter;\r
25 import java.awt.event.WindowEvent;\r
26 import java.lang.reflect.InvocationTargetException;\r
27 import java.lang.reflect.Method;\r
28 import java.util.concurrent.Semaphore;\r
29 import java.util.concurrent.TimeUnit;\r
30 import java.util.concurrent.atomic.AtomicBoolean;\r
31 import java.util.function.Consumer;\r
32 \r
33 import javax.swing.JApplet;\r
34 import javax.swing.SwingUtilities;\r
35 import javax.swing.UIManager;\r
36 import javax.swing.plaf.FontUIResource;\r
37 \r
38 import org.eclipse.core.runtime.IStatus;\r
39 import org.eclipse.core.runtime.Status;\r
40 import org.eclipse.swt.SWT;\r
41 import org.eclipse.swt.awt.SWT_AWT;\r
42 import org.eclipse.swt.events.DisposeEvent;\r
43 import org.eclipse.swt.events.DisposeListener;\r
44 import org.eclipse.swt.graphics.Font;\r
45 import org.eclipse.swt.graphics.FontData;\r
46 import org.eclipse.swt.layout.FillLayout;\r
47 import org.eclipse.swt.widgets.Composite;\r
48 import org.eclipse.swt.widgets.Control;\r
49 import org.eclipse.swt.widgets.Display;\r
50 import org.eclipse.swt.widgets.Event;\r
51 import org.eclipse.swt.widgets.Listener;\r
52 import org.eclipse.swt.widgets.Shell;\r
53 import org.simantics.utils.threads.AWTThread;\r
54 import org.simantics.utils.threads.ThreadUtils;\r
55 import org.simantics.utils.threads.logger.ITask;\r
56 import org.simantics.utils.threads.logger.ThreadLogger;\r
57 import org.simantics.utils.ui.awt.AwtEnvironment;\r
58 import org.simantics.utils.ui.awt.AwtFocusHandler;\r
59 import org.simantics.utils.ui.awt.CleanResizeListener;\r
60 import org.simantics.utils.ui.awt.EmbeddedChildFocusTraversalPolicy;\r
61 import org.simantics.utils.ui.awt.SwtFocusHandler;\r
62 import org.simantics.utils.ui.internal.Activator;\r
63 \r
64 \r
65 /**\r
66  * <pre>\r
67  *        embeddedComposite = new SWTAWTComposite(parent, SWT.NONE) {\r
68  *            protected JComponent createSwingComponent() {\r
69  *                scrollPane = new JScrollPane();\r
70  *                table = new JTable();\r
71  *                scrollPane.setViewportView(table);\r
72  *                return scrollPane;\r
73  *            }\r
74  *        };\r
75  *        // For asynchronous AWT UI population of the swing components:\r
76  *        embeddedComposite.populate();\r
77  *        // and optionally you can wait until the AWT UI population\r
78  *        // has finished:\r
79  *        embeddedComposite.waitUntilPopulated();\r
80  *\r
81  *        // OR:\r
82  *\r
83  *        // Do both things above in one call to block until the\r
84  *        // AWT UI population is complete:\r
85  *        embeddedComposite.syncPopulate();\r
86  *\r
87  *        // OR:\r
88  *\r
89  *        // Set a callback for asynchronous completion in the AWT thread:\r
90  *        embeddedComposite.populate(component -> {\r
91               // AWT components have been created for component\r
92  *        });\r
93  *\r
94  *        // All methods assume all invocations are made from the SWT display thread.\r
95  * </pre>\r
96  * <p>\r
97  * \r
98  * @author Tuukka Lehtonen\r
99  */\r
100 public abstract class SWTAWTComponent extends Composite {\r
101 \r
102     private static class AwtContext {\r
103         private Frame frame;\r
104         private Component swingComponent;\r
105 \r
106         AwtContext(Frame frame) {\r
107             assert frame != null;\r
108             this.frame = frame;\r
109         }\r
110 \r
111         Frame getFrame() {\r
112             return frame;\r
113         }\r
114 \r
115         void setSwingComponent(Component swingComponent) {\r
116             this.swingComponent = swingComponent;\r
117         }\r
118 \r
119         Component getSwingComponent() {\r
120             return swingComponent;\r
121         }\r
122 \r
123     }\r
124 \r
125     private Font                    currentSystemFont;\r
126     private AwtContext              awtContext;\r
127     private AwtFocusHandler         awtHandler;\r
128 \r
129     private JApplet                 panel;\r
130 \r
131     private final AtomicBoolean     populationStarted   = new AtomicBoolean(false);\r
132 \r
133     private final AtomicBoolean     populated           = new AtomicBoolean(false);\r
134 \r
135     private final Semaphore         populationSemaphore = new Semaphore(0);\r
136 \r
137     private Consumer<SWTAWTComponent> populatedCallback;\r
138 \r
139     private static AWTEventListener awtListener         = null;\r
140 \r
141     private Listener settingsListener = new Listener() {\r
142         public void handleEvent(Event event) {\r
143             handleSettingsChange();\r
144         }\r
145     };\r
146 \r
147     // This listener helps ensure that Swing popup menus are properly dismissed when\r
148     // a menu item off the SWT main menu bar is shown.\r
149     private final Listener menuListener = new Listener() {\r
150         public void handleEvent(Event event) {\r
151             assert awtHandler != null;\r
152             awtHandler.postHidePopups();\r
153         }\r
154     };\r
155 \r
156     public SWTAWTComponent(Composite parent, int style) {\r
157         super(parent, style | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.EMBEDDED);\r
158         getDisplay().addListener(SWT.Settings, settingsListener);\r
159         setLayout(new FillLayout());\r
160         currentSystemFont = getFont();\r
161         this.addDisposeListener(new DisposeListener() {\r
162             @Override\r
163             public void widgetDisposed(DisposeEvent e) {\r
164                 doDispose();\r
165             }\r
166         });\r
167     }\r
168 \r
169     protected void doDispose() {\r
170         getDisplay().removeListener(SWT.Settings, settingsListener);\r
171         getDisplay().removeFilter(SWT.Show, menuListener);\r
172 \r
173         ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
174             @Override\r
175             public void run() {\r
176                 AwtContext ctx = awtContext;\r
177                 if (ctx != null) {\r
178                     ctx.frame.dispose();\r
179                 }\r
180                 awtContext = null;\r
181                 if (panel != null) {\r
182                     panel.removeAll();\r
183                     panel = null;\r
184                 }\r
185             }\r
186         });\r
187     }\r
188 \r
189     static class FocusRepairListener implements AWTEventListener {\r
190         @Override\r
191         public void eventDispatched(AWTEvent e) {\r
192             if (e.getID() == MouseEvent.MOUSE_PRESSED) {\r
193                 Object src = e.getSource();\r
194                 if (src instanceof Component) {\r
195                     ((Component) src).requestFocus();\r
196                 }\r
197             }\r
198         }\r
199     }\r
200 \r
201     /**\r
202      * Create a global AWTEventListener for focus management.\r
203      * This helps at least with Linux/GTK problems of transferring focus\r
204      * to workbench parts when clicking on AWT screen territory.\r
205      * \r
206      * NOTE: There is really no need to dispose this once it's been initialized.\r
207      * \r
208      * NOTE: must be invoked from AWT thread.\r
209      */\r
210     private static synchronized void initAWTEventListener() {\r
211         if (!AWTThread.getThreadAccess().currentThreadAccess())\r
212             throw new AssertionError("not invoked from AWT thread");\r
213         if (awtListener == null) {\r
214             awtListener = new FocusRepairListener();\r
215             Toolkit.getDefaultToolkit().addAWTEventListener(awtListener, AWTEvent.MOUSE_EVENT_MASK);\r
216         }\r
217     }\r
218 \r
219     protected Container getContainer() {\r
220         return panel;\r
221     }\r
222 \r
223     public Component getAWTComponent() {\r
224         assert awtContext != null;\r
225         return awtContext.getSwingComponent();\r
226     }\r
227 \r
228     /**\r
229      * This method must always be called from SWT thread. This method should be\r
230      * used with extreme care since it will block the calling thread (i.e. the\r
231      * SWT thread) while the AWT thread initializes itself by spinning and\r
232      * dispatching SWT events. This diminishes the possibility of deadlock\r
233      * (reported between AWT and SWT) but still all UI's are recommended to use\r
234      * the asynchronous non-blocking UI population offered by\r
235      * {@link #populate(Consumer)}\r
236      * \r
237      * @see #populate(Consumer)\r
238      */\r
239     public void syncPopulate() {\r
240         populate();\r
241         waitUntilPopulated();\r
242     }\r
243 \r
244     /**\r
245      * This method must always be called from SWT thread. This will schedule the\r
246      * real AWT component creation into the AWT thread and call the provided\r
247      * asynchronous callback after the UI population is complete.\r
248      * This prevents the possibility of deadlocking.\r
249      */\r
250     public void populate(Consumer<SWTAWTComponent> callback) {\r
251         populate();\r
252         this.populatedCallback = callback;\r
253     }\r
254 \r
255     /**\r
256      * This method will create an AWT {@link Frame} through {@link SWT_AWT} and\r
257      * schedule AWT canvas initialization into the AWT thread. It will not wait\r
258      * for AWT initialization to complete.\r
259      */\r
260     public void populate() {\r
261         if (!populationStarted.compareAndSet(false, true))\r
262             throw new IllegalStateException(this + ".populate was invoked multiple times");\r
263 \r
264         checkWidget();\r
265         ITask task = ThreadLogger.getInstance().begin("createFrame");\r
266         createFrame();\r
267         task.finish();\r
268         scheduleComponentCreation();\r
269     }\r
270 \r
271     public void waitUntilPopulated() {\r
272         if (populated.get())\r
273             return;\r
274 \r
275         try {\r
276             boolean done = false;\r
277             while (!done) {\r
278                 done = populationSemaphore.tryAcquire(10, TimeUnit.MILLISECONDS);\r
279                 while (!done && getDisplay().readAndDispatch()) {\r
280                     /*\r
281                      * Note: readAndDispatch can cause this to be disposed.\r
282                      */\r
283                     if(isDisposed()) return;\r
284                     done = populationSemaphore.tryAcquire();\r
285                 }\r
286             }\r
287         } catch (InterruptedException e) {\r
288             throw new Error("EmbeddedSwingComposite population interrupted for class " + this, e);\r
289         }\r
290     }\r
291 \r
292     /**\r
293      * Returns the embedded AWT frame. The returned frame is the root of the AWT containment\r
294      * hierarchy for the embedded Swing component. This method can be called from \r
295      * any thread. \r
296      *    \r
297      * @return the embedded frame\r
298      */\r
299     public Frame getFrame() {\r
300         // Intentionally leaving out checkWidget() call. This may need to be called from within user's \r
301         // createSwingComponent() method. Accessing from a non-SWT thread is OK, but we still check\r
302         // for disposal\r
303         if (getDisplay() == null || isDisposed()) {\r
304             SWT.error(SWT.ERROR_WIDGET_DISPOSED);\r
305         }\r
306         AwtContext ctx = awtContext;\r
307         return (ctx != null) ? ctx.getFrame() : null;\r
308     }\r
309 \r
310     private void createFrame() {\r
311         assert Display.getCurrent() != null;     // On SWT event thread\r
312 \r
313         // Make sure Awt environment is initialized. \r
314         AwtEnvironment.getInstance(getDisplay());\r
315 \r
316         if (awtContext != null) {\r
317             final Frame oldFrame = awtContext.getFrame();\r
318             // Schedule disposal of old frame on AWT thread so that there are no problems with\r
319             // already-scheduled operations that have not completed.\r
320             // Note: the implementation of Frame.dispose() would schedule the use of the AWT \r
321             // thread even if it was not done here, but it uses invokeAndWait() which is \r
322             // prone to deadlock (and not necessary for this case). \r
323             EventQueue.invokeLater(new Runnable() {\r
324                 public void run() {\r
325                     oldFrame.dispose();\r
326                 }\r
327             });\r
328         }\r
329         Frame frame = SWT_AWT.new_Frame(this);\r
330         awtContext = new AwtContext(frame);\r
331 \r
332         // See Simantics issue #3518\r
333         workaroundJava7FocusProblem(frame);\r
334 \r
335         // Glue the two frameworks together. Do this before anything is added to the frame\r
336         // so that all necessary listeners are in place.\r
337         createFocusHandlers();\r
338 \r
339         // This listener clears garbage during resizing, making it looker much cleaner \r
340         addControlListener(new CleanResizeListener());\r
341     }\r
342 \r
343     private void workaroundJava7FocusProblem(Frame frame) {\r
344         String ver = System.getProperty("java.version");\r
345         if (ver.startsWith("1.7") || ver.startsWith("1.8")) {\r
346             try {\r
347                 frame.addWindowListener(new Java7FocusFixListener(this, frame));\r
348             } catch (SecurityException e) {\r
349                 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
350             } catch (NoSuchMethodException e) {\r
351                 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
352             }\r
353         }\r
354     }\r
355 \r
356     static class Java7FocusFixListener extends WindowAdapter {\r
357 \r
358         Method shellSetActiveControl;\r
359         Control control;\r
360         Frame frame;\r
361 \r
362         public Java7FocusFixListener(Control control, Frame frame) throws NoSuchMethodException, SecurityException {\r
363             this.shellSetActiveControl = Shell.class.getDeclaredMethod("setActiveControl", Control.class);\r
364             this.frame = frame;\r
365             this.control = control;\r
366         }\r
367 \r
368         @Override\r
369         public void windowActivated(WindowEvent e) {\r
370             SWTUtils.asyncExec(control, new Runnable() {\r
371                 @Override\r
372                 public void run() {\r
373                     if (control.isDisposed())\r
374                         return;\r
375                     if (control.getDisplay().getFocusControl() == control) {\r
376                         try {\r
377                             boolean accessible = shellSetActiveControl.isAccessible();\r
378                             if (!accessible)\r
379                                 shellSetActiveControl.setAccessible(true);\r
380                             shellSetActiveControl.invoke(control.getShell(), control);\r
381                             if (!accessible)\r
382                                 shellSetActiveControl.setAccessible(false);\r
383                         } catch (SecurityException e) {\r
384                             Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
385                         } catch (IllegalArgumentException e) {\r
386                             Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
387                         } catch (IllegalAccessException e) {\r
388                             Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));\r
389                         } catch (InvocationTargetException e) {\r
390                             Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getCause().getMessage(), e.getCause()));\r
391                         }\r
392                     }\r
393                 }\r
394             });\r
395         }\r
396 \r
397     }\r
398 \r
399     private void createFocusHandlers() {\r
400         assert awtContext != null;\r
401         assert Display.getCurrent() != null;     // On SWT event thread\r
402 \r
403         Frame frame = awtContext.getFrame();\r
404         awtHandler = new AwtFocusHandler(frame);   \r
405         SwtFocusHandler swtHandler = new SwtFocusHandler(this);\r
406         awtHandler.setSwtHandler(swtHandler);\r
407         swtHandler.setAwtHandler(awtHandler);\r
408 \r
409         // Ensure that AWT pop-ups are dismissed whenever a SWT menu is shown\r
410         getDisplay().addFilter(SWT.Show, menuListener);\r
411 \r
412         EmbeddedChildFocusTraversalPolicy policy = new EmbeddedChildFocusTraversalPolicy(awtHandler);\r
413         frame.setFocusTraversalPolicy(policy);\r
414     }\r
415 \r
416     private void scheduleComponentCreation() {\r
417         assert awtContext != null;\r
418 \r
419         // Create AWT/Swing components on the AWT thread. This is \r
420         // especially necessary to avoid an AWT leak bug (6411042).\r
421         final AwtContext currentContext = awtContext;\r
422         ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {\r
423             @Override\r
424             public void run() {\r
425                 // Make sure AWT focus fix is in place.\r
426                 initAWTEventListener();\r
427 \r
428                 panel = addRootPaneContainer(currentContext.getFrame());\r
429                 panel.setLayout(new GridLayout(1,1,0,0));\r
430                 try {\r
431                     Component swingComponent = createSwingComponent();\r
432                     currentContext.setSwingComponent(swingComponent);\r
433                     panel.getRootPane().getContentPane().add(swingComponent);\r
434                     //panel.add(swingComponent);\r
435                     setComponentFont();\r
436                 } finally {\r
437                     // Needed to support #waitUntilPopulated\r
438                     populated.set(true);\r
439                     if (populationSemaphore != null)\r
440                         populationSemaphore.release();\r
441                     if (populatedCallback != null) {\r
442                         populatedCallback.accept(SWTAWTComponent.this);\r
443                         populatedCallback = null;\r
444                     }\r
445                 }\r
446             }\r
447         });\r
448     }\r
449 \r
450     /**\r
451      * Adds a root pane container to the embedded AWT frame. Override this to provide your own \r
452      * {@link javax.swing.RootPaneContainer} implementation. In most cases, it is not necessary\r
453      * to override this method.    \r
454      * <p>\r
455      * This method is called from the AWT event thread. \r
456      * <p> \r
457      * If you are defining your own root pane container, make sure that there is at least one\r
458      * heavyweight (AWT) component in the frame's containment hierarchy; otherwise, event \r
459      * processing will not work correctly. See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4982522\r
460      * for more information.  \r
461      *   \r
462      * @param frame the frame to which the root pane container is added \r
463      * @return a non-null Swing component\r
464      */\r
465     protected JApplet addRootPaneContainer(Frame frame) {\r
466         assert EventQueue.isDispatchThread();    // On AWT event thread\r
467         assert frame != null;\r
468 \r
469         // It is important to set up the proper top level components in the frame:\r
470         // 1) For Swing to work properly, Sun documents that there must be an implementor of \r
471         // javax.swing.RootPaneContainer at the top of the component hierarchy. \r
472         // 2) For proper event handling there must be a heavyweight \r
473         // an AWT frame must contain a heavyweight component (see \r
474         // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4982522)\r
475         // 3) The Swing implementation further narrows the options by expecting that the \r
476         // top of the hierarchy be a JFrame, JDialog, JWindow, or JApplet. See javax.swing.PopupFactory.\r
477         // All this drives the choice of JApplet for the top level Swing component. It is the \r
478         // only single component that satisfies all the above. This does not imply that \r
479         // we have a true applet; in particular, there is no notion of an applet lifecycle in this\r
480         // context. \r
481         JApplet applet = new JApplet();\r
482         \r
483         // In JRE 1.4, the JApplet makes itself a focus cycle root. This\r
484         // interferes with the focus handling installed on the parent frame, so\r
485         // change it back to a non-root here. \r
486         // TODO: consider moving the focus policy from the Frame down to the JApplet\r
487         applet.setFocusCycleRoot(false);\r
488 \r
489         frame.add(applet);\r
490 \r
491         return applet;\r
492     }\r
493 \r
494     /**\r
495      * Override this to customize what kind of AWT/Swing UI is created by this\r
496      * {@link SWTAWTComponent}.\r
497      * \r
498      * @return the AWT/Swing component created by this SWTAWT bridging control\r
499      * @thread AWT\r
500      */\r
501     protected abstract Component createSwingComponent();\r
502 \r
503     private void setComponentFont() {\r
504         assert currentSystemFont != null;\r
505         assert EventQueue.isDispatchThread();    // On AWT event thread\r
506 \r
507         Component swingComponent = (awtContext != null) ? awtContext.getSwingComponent() : null;\r
508         if ((swingComponent != null) && !currentSystemFont.getDevice().isDisposed()) {\r
509             FontData fontData = currentSystemFont.getFontData()[0];\r
510             \r
511             // AWT font sizes assume a 72 dpi resolution, always. The true screen resolution must be \r
512             // used to convert the platform font size into an AWT point size that matches when displayed. \r
513             int resolution = Toolkit.getDefaultToolkit().getScreenResolution();\r
514             int awtFontSize = (int)Math.round((double)fontData.getHeight() * resolution / 72.0);\r
515             \r
516             // The style constants for SWT and AWT map exactly, and since they are int constants, they should\r
517             // never change. So, the SWT style is passed through as the AWT style. \r
518             java.awt.Font awtFont = new java.awt.Font(fontData.getName(), fontData.getStyle(), awtFontSize);\r
519 \r
520             // Update the look and feel defaults to use new font.\r
521             updateLookAndFeel(awtFont);\r
522 \r
523             // Allow subclasses to react to font change if necessary. \r
524             updateAwtFont(awtFont);\r
525 \r
526             // Allow components to update their UI based on new font \r
527             // TODO: should the update method be called on the root pane instead?\r
528             Container contentPane = SwingUtilities.getRootPane(swingComponent).getContentPane();\r
529             SwingUtilities.updateComponentTreeUI(contentPane);\r
530         }\r
531     }\r
532 \r
533     private void updateLookAndFeel(java.awt.Font awtFont) {\r
534         assert awtFont != null;\r
535         assert EventQueue.isDispatchThread();    // On AWT event thread\r
536 \r
537         // The FontUIResource class marks the font as replaceable by the look and feel \r
538         // implementation if font settings are later changed. \r
539         FontUIResource fontResource = new FontUIResource(awtFont);\r
540 \r
541         // Assign the new font to the relevant L&F font properties. These are \r
542         // the properties that are initially assigned to the system font\r
543         // under the Windows look and feel. \r
544         // TODO: It's possible that other platforms will need other assignments.\r
545         // TODO: This does not handle fonts other than the "system" font. \r
546         // Other fonts may change, and the Swing L&F may not be adjusting.\r
547 \r
548         UIManager.put("Button.font", fontResource); //$NON-NLS-1$\r
549         UIManager.put("CheckBox.font", fontResource); //$NON-NLS-1$\r
550         UIManager.put("ComboBox.font", fontResource); //$NON-NLS-1$\r
551         UIManager.put("EditorPane.font", fontResource); //$NON-NLS-1$\r
552         UIManager.put("Label.font", fontResource); //$NON-NLS-1$\r
553         UIManager.put("List.font", fontResource); //$NON-NLS-1$\r
554         UIManager.put("Panel.font", fontResource); //$NON-NLS-1$\r
555         UIManager.put("ProgressBar.font", fontResource); //$NON-NLS-1$\r
556         UIManager.put("RadioButton.font", fontResource); //$NON-NLS-1$\r
557         UIManager.put("ScrollPane.font", fontResource); //$NON-NLS-1$\r
558         UIManager.put("TabbedPane.font", fontResource); //$NON-NLS-1$\r
559         UIManager.put("Table.font", fontResource); //$NON-NLS-1$\r
560         UIManager.put("TableHeader.font", fontResource); //$NON-NLS-1$\r
561         UIManager.put("TextField.font", fontResource); //$NON-NLS-1$\r
562         UIManager.put("TextPane.font", fontResource); //$NON-NLS-1$\r
563         UIManager.put("TitledBorder.font", fontResource); //$NON-NLS-1$\r
564         UIManager.put("ToggleButton.font", fontResource); //$NON-NLS-1$\r
565         UIManager.put("TreeFont.font", fontResource); //$NON-NLS-1$\r
566         UIManager.put("ViewportFont.font", fontResource); //$NON-NLS-1$\r
567     }\r
568 \r
569     /**\r
570      * Performs custom updates to newly set fonts. This method is called whenever a change\r
571      * to the system font through the system settings (i.e. control panel) is detected.\r
572      * <p>\r
573      * This method is called from the AWT event thread.  \r
574      * <p>\r
575      * In most cases it is not necessary to override this method.  Normally, the implementation\r
576      * of this class will automatically propogate font changes to the embedded Swing components \r
577      * through Swing's Look and Feel support. However, if additional \r
578      * special processing is necessary, it can be done inside this method. \r
579      *    \r
580      * @param newFont New AWT font\r
581      */\r
582     protected void updateAwtFont(java.awt.Font newFont) {\r
583     }\r
584 \r
585     private void handleSettingsChange() {\r
586         Font newFont = getDisplay().getSystemFont();\r
587         if (!newFont.equals(currentSystemFont)) { \r
588             currentSystemFont = newFont;\r
589             EventQueue.invokeLater(new Runnable() {\r
590                 public void run() {\r
591                     setComponentFont();\r
592                 }\r
593             });\r
594         }\r
595     }\r
596 \r
597 }\r