View Javadoc

1   /*
2   @See License.txt@
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 }