001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.util;
043:
044: import java.awt.Component;
045: import java.awt.event.ActionEvent;
046: import java.awt.event.ActionListener;
047: import java.awt.event.HierarchyEvent;
048: import java.awt.event.HierarchyListener;
049: import javax.swing.SwingUtilities;
050: import javax.swing.Timer;
051:
052: /** Performance helper class, allows to run post-init task for given component.
053: * Can also handle cancel logic if contained in AsyncGUIJob.
054: * Class is designed for one time use, can't be used to perform async init
055: * more then once.
056: * Restrictions: Note that for correct functionality given component must not
057: * be showing at construction time of this class, however shouldn't stay hidden
058: * forever as memory leak may occur.
059: *
060: * @author Dafe Simonek
061: */
062: final class AsyncInitSupport implements HierarchyListener, Runnable,
063: ActionListener {
064: /** lock for access to wasCancelled flag */
065: private static final Object CANCELLED_LOCK = new Object();
066:
067: /** task in which post init code from AsyncJob is executed */
068: private Task initTask;
069:
070: /** true after cancel request came, false otherwise */
071: private boolean wasCancelled;
072:
073: /** Component requesting asynchronous initialization */
074: private Component comp4Init;
075:
076: /** Job that performs async init task */
077: private AsyncGUIJob initJob;
078:
079: /** Timer for delaying asynchronous init job to enable some painting first */
080: Timer timer = null;
081:
082: /** Creates a new instance of AsyncInitComponent
083: * @param comp4Init Component to be initialized. Mustn't be showing at this
084: * time. IllegalStateException is thrown if component is already showing.
085: * @param initJob Instance of initialization job.
086: */
087: public AsyncInitSupport(Component comp4Init, AsyncGUIJob initJob) {
088: this .comp4Init = comp4Init;
089: this .initJob = initJob;
090: if (comp4Init.isShowing()) {
091: throw new IllegalStateException(
092: "Component already shown, can't be inited: "
093: + comp4Init);
094: }
095:
096: comp4Init.addHierarchyListener(this );
097: }
098:
099: /** Impl of HierarchyListener, starts init job with delay when component shown,
100: * stops listening to asociated component it isn't showing anymore,
101: * calls cancel if desirable.
102: * @param evt hierarchy event
103: */
104: public void hierarchyChanged(HierarchyEvent evt) {
105: if (((evt.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0)) {
106: boolean isShowing = comp4Init.isShowing();
107: if (timer == null && isShowing) {
108: timer = new Timer(20, this );
109: timer.setRepeats(false);
110: timer.start();
111: } else if (!isShowing) {
112: comp4Init.removeHierarchyListener(this );
113: cancel();
114: }
115: }
116: }
117:
118: /** Impl of ActionListener, called from hierarchyChanged through a Timer,
119: * starts the job */
120: public void actionPerformed(ActionEvent ae) {
121: if (wasCancelled || (initTask != null)) {
122: //If cancelled or already started, our job is done, go away.
123: detach();
124: return;
125: }
126:
127: if ((comp4Init != null) && comp4Init.isDisplayable()) {
128: //If the component has a parent onscreen, we're ready to run.
129: start();
130: }
131: }
132:
133: private void start() {
134: detach();
135:
136: if (initTask == null) {
137: initTask = RequestProcessor.getDefault().post(this );
138: }
139: }
140:
141: private void detach() {
142: if (timer != null) {
143: timer.stop();
144: }
145: }
146:
147: /** Body of task executed in RequestProcessor. Runs AsyncGUIJob's worker
148: * method and after its completion posts AsyncJob's UI update method
149: * to AWT thread.
150: */
151: public void run() {
152: if (!SwingUtilities.isEventDispatchThread()) {
153: // first pass, executed in some of RP threads
154: initJob.construct();
155: comp4Init.removeHierarchyListener(this );
156:
157: // continue to invoke finished method only if hasn't been cancelled
158: boolean localCancel;
159:
160: synchronized (CANCELLED_LOCK) {
161: localCancel = wasCancelled;
162: }
163:
164: if (!localCancel) {
165: SwingUtilities.invokeLater(this );
166: }
167: } else {
168: // second pass, executed in event dispatch thread
169: initJob.finished();
170: }
171: }
172:
173: /** Delegates valid cancel requests to asociated AsyncGUIJob, in the case
174: * job supports cancelling. */
175: private void cancel() {
176: if ((initTask != null) && !initTask.isFinished()
177: && (initJob instanceof Cancellable)) {
178: synchronized (CANCELLED_LOCK) {
179: wasCancelled = true;
180: }
181: ((Cancellable) initJob).cancel();
182: }
183: }
184:
185: }
|