1
2
3
4
5 package ui;
6
7 import java.awt.Dialog;
8 import java.awt.event.ComponentEvent;
9 import java.awt.event.ComponentListener;
10 import java.util.HashMap;
11
12 import javax.swing.JFrame;
13 import org.apache.log4j.Logger;
14
15 /***
16 * This class works around the fact that a modal dialog box uses a
17 * busy loop to force the modal behaviour. This means that all other
18 * threads starve while the modal dialog box is active. To circumvent this
19 * behaviour this class places a glass pane over the frame and raises the
20 * dialog. The glass pane over the frame makes the dialog appear as if it is a modal
21 * dialog. <b>Note: Currently the dialog does not always stay on top of the frame</b>.
22 * When the <code>componentHidden</code> event for the dialog is received the glassPane is made non visible.
23 * <p>
24 *
25 * @author Barrie Treloar */
26 public class ModalDialogThreadLockFix implements ComponentListener {
27 private JFrame toDisable;
28
29 /***
30 * A 1-1 mapping of JFrames to reference count (Integer).
31 * This is used to keep track of how many times a frame is made modal so that
32 * its state can be set back to the correct value when the reference count
33 * is incremented or decremented.
34 */
35 private static HashMap referenceCountMap = new HashMap();
36
37 /***
38 * Only one instance of this class is necessary as the referenceCountMap keeps track
39 * of when to add and remove the beeper from the JFrame
40 */
41 private static BeepOnMouseEvents beeper = new BeepOnMouseEvents();
42
43 private static final Logger logger = Logger.getLogger("ui");
44
45 /***
46 * No instances of this class are necessary outside of this class.
47 */
48 private ModalDialogThreadLockFix(JFrame toDisable) {
49 this.toDisable = toDisable;
50 }
51
52 /***
53 * Install a glass pane on the JFrame and show the dialog centred on the screen.
54 */
55 public static void show(JFrame toDisable, Dialog dialog) {
56 ModalDialogThreadLockFix instance = new ModalDialogThreadLockFix(toDisable);
57 increaseReferenceCount(toDisable);
58 dialog.addComponentListener(instance);
59 dialog.setLocation(PointUtilities.centeredOnScreen(dialog));
60 dialog.setVisible(true);
61 }
62
63 /***
64 * Increase the reference count for the frame. If the reference count is
65 * one then add the listeners and make the glass pane visible.
66 */
67 private static void increaseReferenceCount(JFrame frame) {
68 int referenceCount = 0;
69 Integer referenceCountInteger = (Integer) referenceCountMap.get(frame);
70 if (referenceCountInteger != null) {
71 referenceCount = referenceCountInteger.intValue();
72 }
73 referenceCount = referenceCount + 1;
74 if (referenceCount == 1) {
75 frame.getGlassPane().addMouseListener(beeper);
76 frame.getGlassPane().addMouseMotionListener(beeper);
77 frame.getGlassPane().setVisible(true);
78 }
79 referenceCountMap.put(frame, new Integer(referenceCount));
80 }
81
82 /***
83 * Decrease the reference count for the frame. If the reference count is
84 * zero then delete the listeners and make the glass pane invisible.
85 */
86 private static void decreaseReferenceCount(JFrame frame) {
87 int referenceCount = ((Integer) referenceCountMap.get(frame)).intValue();
88 referenceCount = referenceCount - 1;
89 if (referenceCount == 0) {
90 frame.getGlassPane().removeMouseListener(beeper);
91 frame.getGlassPane().removeMouseMotionListener(beeper);
92 frame.getGlassPane().setVisible(false);
93 }
94 referenceCountMap.put(frame, new Integer(referenceCount));
95 }
96
97 /***
98 * Invoked when the component has been made visible.
99 * <b>NOTE: This is a workaround to Bug ID: 4180584. <code>componentShown></code> does
100 * not get called but <code>componentMoved</code> does.
101 */
102 public void componentShown(ComponentEvent e) {
103 }
104
105 /***
106 * Invoked when the component has been made invisible.
107 * If the dialog associated with this event is still the current modal dialog
108 * then
109 */
110 public void componentHidden(ComponentEvent e) {
111 decreaseReferenceCount(toDisable);
112 }
113
114 /***
115 * Invoked when the component's size changes.
116 */
117 public void componentResized(ComponentEvent e) {
118 }
119
120 /***
121 * Invoked when the component's position changes.
122 * <p>
123 * <b>NOTE: This is a workaround to Bug ID: 4180584. <code>componentShown></code> does
124 * not get called but <code>componentMoved</code> does.
125 * <p>
126 * For the dialog associated with this event find all JFrames that require disabling.
127 * For each JFrame call <code>getGlassPane().setVisible( true )<code>.
128 */
129 public void componentMoved(ComponentEvent e) {
130 }
131 }