View Javadoc

1   /*
2    * Copyright (c) 1998, 2005 Gargoyle Software Inc. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    *
7    * 1. Redistributions of source code must retain the above copyright notice,
8    *    this list of conditions and the following disclaimer.
9    * 2. Redistributions in binary form must reproduce the above copyright notice,
10   *    this list of conditions and the following disclaimer in the documentation
11   *    and/or other materials provided with the distribution.
12   * 3. The end-user documentation included with the redistribution, if any, must
13   *    include the following acknowledgment:
14   *
15   *       "This product includes software developed by Gargoyle Software Inc.
16   *        (http://www.GargoyleSoftware.com/)."
17   *
18   *    Alternately, this acknowledgment may appear in the software itself, if
19   *    and wherever such third-party acknowledgments normally appear.
20   * 4. The name "Gargoyle Software" must not be used to endorse or promote
21   *    products derived from this software without prior written permission.
22   *    For written permission, please contact info@GargoyleSoftware.com.
23   * 5. Products derived from this software may not be called "GSBase", nor may
24   *    "GSBase" appear in their name, without prior written permission of
25   *    Gargoyle Software Inc.
26   *
27   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
28   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
29   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
30   * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
33   * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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         // Display the waiting component
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                         // Ignore this
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         // Perform delayed loading of the listeners collection
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 }