1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package com.gargoylesoftware.base.gui;
39
40 import com.gargoylesoftware.base.util.DetailedIllegalArgumentException;
41 import com.gargoylesoftware.base.util.DetailedNullPointerException;
42 import java.awt.BorderLayout;
43 import java.awt.Component;
44 import java.util.Collections;
45 import java.util.HashSet;
46 import java.util.Set;
47 import javax.swing.JComponent;
48 import javax.swing.JLabel;
49 import javax.swing.SwingUtilities;
50 import javax.swing.UIManager;
51 import javax.swing.JScrollPane;
52
53 /***
54 * A panel that supports delayed loading of its contents. This is useful when
55 * the component that will fill this panel will take a long time to load. A "please
56 * wait" message will be displayed while the component is being loaded. When loading
57 * is complete, the new component will be made a child of this panel so that it can
58 * become visible.<p>
59 *
60 * The following sample will create a delayed loader with a default "please wait" message.
61 * <pre>
62 * final DelayedComponentLoaderPanel panel = new DelayedComponentLoaderPanel();
63 * panel.setComponentLoader( new DefaultComponentLoader(MyExpensiveComponent.class) );
64 * </pre>
65 *
66 * This sample has a custom message.
67 * <pre>
68 * final JLabel label = new JLabel("My custom wait message");
69 * final DelayedComponentLoaderPanel panel = new DelayedComponentLoaderPanel(label);
70 * panel.setComponentLoader( new DefaultComponentLoader(MyExpensiveComponent.class) );
71 * </pre>
72 *
73 * @version $Revision: 1.7 $
74 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
75 */
76 public class DelayedComponentLoaderPanel extends JComponent {
77 private static final long serialVersionUID = 1;
78 private static final int LOADER_STARTED = 1;
79 private static final int LOADER_FINISHED = 2;
80
81 private Set listeners_;
82 private Component waitingComponent_;
83
84 /***
85 * Create a new panel that will display a simple "please wait" message
86 * when it is loading.
87 */
88 public DelayedComponentLoaderPanel() {
89 this( new JLabel("Loading ... Please wait.") );
90 JLabel label = (JLabel)waitingComponent_;
91 label.setIcon( UIManager.getIcon("OptionPane.informationIcon") );
92 label.setHorizontalAlignment( JLabel.CENTER );
93 label.setOpaque(true);
94 }
95
96 /***
97 * Create a new panel that will display the specified waitingComponent
98 * while it is loading.
99 *
100 * @param waitingComponent The component to display while loading is in progress.
101 */
102 public DelayedComponentLoaderPanel( final Component waitingComponent ) {
103 assertNotNull("waitingComponent", waitingComponent);
104
105 setLayout( new BorderLayout() );
106 waitingComponent_ = waitingComponent;
107 add( BorderLayout.CENTER, waitingComponent_ );
108 }
109
110 /***
111 * Set the new component loader. The waiting component will be displayed
112 * immediately and component loading will start on a background thread.
113 * If the new loader is null then no loading will take place and the waiting
114 * component will be displayed indefinitely
115 *
116 * @param loader The new component loader.
117 */
118 public void setComponentLoader( final ComponentLoader loader ) {
119
120 replaceComponent( waitingComponent_ );
121
122 if( loader != null ) {
123 fireComponentLoadingEvent(LOADER_STARTED, loader, null);
124 new Thread() {
125 public void run() {
126 try {
127 sleep(500);
128 }
129 catch( final InterruptedException e ) {
130
131 }
132
133 Component loadedComponent;
134
135 try {
136 loadedComponent = loader.loadComponent();
137 }
138 catch( final Throwable t ) {
139 loadedComponent = new JScrollPane(new ThrowablePanel(t));
140 t.printStackTrace();
141 }
142
143 final Component finalLoadedComponent = loadedComponent;
144 SwingUtilities.invokeLater( new Runnable() {
145 public void run() {
146 replaceComponent( finalLoadedComponent );
147 fireComponentLoadingEvent(LOADER_FINISHED, loader,
148 finalLoadedComponent );
149 }
150 } );
151 }
152 }.start();
153 }
154 }
155
156 /***
157 * Replace the current child with the specified component
158 * @param component The new component.
159 */
160 private void replaceComponent( final Component component ) {
161 removeAll();
162 add( BorderLayout.CENTER, component );
163 revalidate();
164 }
165
166 /***
167 * Add the specified listener.
168 *
169 * @param listener The new listener
170 */
171 public void addDelayedComponentLoaderListener( final DelayedComponentLoaderListener listener ) {
172
173 assertNotNull("listener", listener);
174
175
176 if( listeners_ == null ) {
177 synchronized(this) {
178 if( listeners_ == null ) {
179 listeners_ = Collections.synchronizedSet(new HashSet());
180 }
181 }
182 }
183 listeners_.add(listener);
184 }
185
186 /***
187 * Remove the specified listener.
188 * @param listener the listener to remove.
189 */
190 public void removeDelayedComponentLoaderListener(final DelayedComponentLoaderListener listener ) {
191
192 assertNotNull("listener", listener);
193
194 if( listeners_ != null ) {
195 listeners_.remove(listener);
196 }
197 }
198
199 /***
200 * Fire the component loading event.
201 * @param action The action id
202 * @param loader The loader
203 * @param loadedComponent The component that was just loaded.
204 */
205 private void fireComponentLoadingEvent(
206 final int action,
207 final ComponentLoader loader,
208 final Component loadedComponent ) {
209
210 if( listeners_ != null && listeners_.isEmpty() == false ) {
211 DelayedComponentLoaderListener listenerArray[];
212 synchronized(listeners_) {
213 listenerArray = new DelayedComponentLoaderListener[listeners_.size()];
214 listeners_.toArray(listenerArray);
215 }
216
217 final DelayedComponentLoaderEvent event
218 = new DelayedComponentLoaderEvent(this, loader, loadedComponent);
219
220 int i;
221 for( i=0; i<listenerArray.length; i++ ) {
222 switch( action ) {
223 case LOADER_STARTED:
224 listenerArray[i].componentLoadingStarted(event);
225 break;
226
227 case LOADER_FINISHED:
228 listenerArray[i].componentLoadingFinished(event);
229 break;
230
231 default:
232 throw new DetailedIllegalArgumentException("action", new Integer(action), "Unexpected value");
233 }
234 }
235 }
236 }
237
238
239 /***
240 * Throw an exception if the specified object is null
241 * @param fieldName The name of the paremeter we are checking
242 * @param object The value of the parameter we are checking
243 */
244 protected final void assertNotNull( final String fieldName, final Object object ) {
245 if( object == null ) {
246 throw new DetailedNullPointerException(fieldName);
247 }
248 }
249 }