/******************************************************************************* * Copyright (c) 2003, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.progress; import java.util.*; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.rwt.RWT; import org.eclipse.rwt.SessionSingletonBase; import org.eclipse.rwt.lifecycle.UICallBack; import org.eclipse.rwt.service.ISessionStore; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.progress.WorkbenchJob; /** * The AnimationManager is the class that keeps track of the animation items to * update. */ public class AnimationManager extends SessionSingletonBase { // private static AnimationManager singleton; boolean animated = false; private final IJobProgressManagerListener listener; IAnimationProcessor animationProcessor; WorkbenchJob animationUpdateJob; Display display; public static AnimationManager getInstance() { AnimationManager instance = ( AnimationManager )getInstance( AnimationManager.class ); if( instance.display == null ) { instance.display = Display.getCurrent(); } return instance; // if (singleton == null) { // singleton = new AnimationManager(); // } // return singleton; } /** * Get the background color to be used. * * @param control * The source of the display. * @return Color */ static Color getItemBackgroundColor(final Control control) { return control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND); } AnimationManager() { // This is a helping flag used to avoid a memory leak due to // thread management. // Note that this is still under investigation. // see comment in JobManagerAdapter final boolean[] done = new boolean[ 1 ]; animationProcessor = new ProgressAnimationProcessor(this); animationUpdateJob = new WorkbenchJob(ProgressMessages.get().AnimationManager_AnimationStart){ /* * (non-Javadoc) * * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus runInUIThread(final IProgressMonitor monitor) { if (animated) { animationProcessor.animationStarted(); } else { animationProcessor.animationFinished(); } return Status.OK_STATUS; } // This is a helping mechanism used to avoid a memory leak due to // thread management. // Note that this is still under investigation. // see comment in JobManagerAdapter public Object getAdapter( final Class adapter ) { Object result; if( adapter == IJobMarker.class ) { result = new IJobMarker() { public boolean canBeRemoved() { return done[ 0 ]; } }; } else { result = super.getAdapter( adapter ); } return result; } }; animationUpdateJob.setSystem(true); listener = getProgressListener(); ProgressManager.getInstance().addListener(listener); // This is a helping mechanism used to avoid a memory leak due to // thread management. // Note that this is still under investigation. // see comment in JobManagerAdapter ISessionStore session = RWT.getSessionStore(); String watchDogKey = getClass().getName() + ".watchDog"; if( session.getAttribute( watchDogKey ) == null ) { session.setAttribute( watchDogKey, new HttpSessionBindingListener() { public void valueBound( final HttpSessionBindingEvent event ) { } public void valueUnbound( final HttpSessionBindingEvent event ) { if( animationUpdateJob != null ) { animationUpdateJob.cancel(); animationUpdateJob.addJobChangeListener( new JobCanceler() ); done[ 0 ] = true; } } } ); } } /** * Add an item to the list * * @param item */ void addItem(final AnimationItem item) { animationProcessor.addItem(item); } /** * Remove an item from the list * * @param item */ void removeItem(final AnimationItem item) { animationProcessor.removeItem(item); } /** * Return whether or not the current state is animated. * * @return boolean */ boolean isAnimated() { return animated; } /** * Set whether or not the receiver is animated. * * @param boolean */ void setAnimated(final boolean bool) { animated = bool; Runnable scheduler = new Runnable() { public void run() { animationUpdateJob.schedule(100); } }; UICallBack.runNonUIThreadWithFakeContext( display, scheduler ); } /** * Dispose the images in the receiver. */ void dispose() { setAnimated(false); ProgressManager.getInstance().removeListener(listener); } private IJobProgressManagerListener getProgressListener() { return new IJobProgressManagerListener() { Set jobs = Collections.synchronizedSet(new HashSet()); /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#addJob(org.eclipse.ui.internal.progress.JobInfo) */ public void addJob(final JobInfo info) { incrementJobCount(info); } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshJobInfo(org.eclipse.ui.internal.progress.JobInfo) */ public void refreshJobInfo(final JobInfo info) { int state = info.getJob().getState(); if (state == Job.RUNNING) { addJob(info); } else { removeJob(info); } } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshAll() */ public void refreshAll() { ProgressManager manager = ProgressManager.getInstance(); jobs.clear(); setAnimated(false); JobInfo[] currentInfos = manager.getJobInfos(showsDebug()); for (int i = 0; i < currentInfos.length; i++) { addJob(currentInfos[i]); } } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#remove(org.eclipse.ui.internal.progress.JobInfo) */ public void removeJob(final JobInfo info) { decrementJobCount(info.getJob()); } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#showsDebug() */ public boolean showsDebug() { return false; } private void incrementJobCount(final JobInfo info) { //Don't count the animate job itself if (isNotTracked(info)) { return; } if (jobs.isEmpty()) { setAnimated(true); } jobs.add(info.getJob()); } /* * Decrement the job count for the job */ private void decrementJobCount(final Job job) { jobs.remove(job); if (jobs.isEmpty()) { setAnimated(false); } } /** * If this is one of our jobs or not running then don't bother. */ private boolean isNotTracked(final JobInfo info) { //We always track errors Job job = info.getJob(); return job.getState() != Job.RUNNING || animationProcessor.isProcessorJob(job); } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#addGroup(org.eclipse.ui.internal.progress.GroupInfo) */ public void addGroup(final GroupInfo info) { //Don't care about groups } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#removeGroup(org.eclipse.ui.internal.progress.GroupInfo) */ public void removeGroup(final GroupInfo group) { //Don't care about groups } /* * (non-Javadoc) * * @see org.eclipse.ui.internal.progress.IJobProgressManagerListener#refreshGroup(org.eclipse.ui.internal.progress.GroupInfo) */ public void refreshGroup(final GroupInfo info) { //Don't care about groups } }; } /** * Get the preferred width for widgets displaying the animation. * * @return int. Return 0 if there is no image data. */ int getPreferredWidth() { return animationProcessor.getPreferredWidth(); } }