/*
Copyright (C) 2006 EBI
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the itmplied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.biomart.common.view.gui;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.KeyboardFocusManager;
import javax.swing.SwingUtilities;
import org.biomart.common.view.gui.dialogs.StackTrace;
/**
* This simple class wraps a thread, and displays an hourglass for as long as
* that thread is running. It is synchronised so that if multiple threads are
* running, only the first one will start the hourglass, and only the last one
* to end will stop it.
* <p>
* The hourglass portion of the thread is run using
* {@link SwingUtilities#invokeLater(java.lang.Runnable)}, so that it is
* thread-safe for Swing and can safely work with the GUI. Any parts of the
* process passed in that work with the GUI must also use this construct to
* avoid concurrent modification problems.
*
* @author Richard Holland <holland@ebi.ac.uk>
* @version $Revision: 1.12 $, $Date: 2007-10-25 15:17:17 $, modified by
* $Author: rh4 $
* @since 0.5
*/
public abstract class LongProcess {
private static Component mainWindow;
private static final Object lockObject = "My Hourglass Lock";
private static int longProcessCount = 0;
private static final Cursor HOURGLASS_CURSOR = new Cursor(
Cursor.WAIT_CURSOR);
private static final Cursor NORMAL_CURSOR = new Cursor(
Cursor.DEFAULT_CURSOR);
/**
* Tell the hourglass which window is the main window.
*
* @param mainWindow
* the main window.
*/
public static void setMainWindow(final Component mainWindow) {
LongProcess.mainWindow = mainWindow;
}
/**
* Runs the current background task as defined by {@link #run()}. Whilst
* the task is running, the hourglass is shown over the currently active
* window.
*/
public void start() {
new SwingWorker() {
public Object construct() {
// Which window needs the hourglass?
final Component window = KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusedWindow();
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
synchronized (LongProcess.lockObject) {
// Open the hourglass.
if (LongProcess.longProcessCount++ == 0) {
if (window != null)
window
.setCursor(LongProcess.HOURGLASS_CURSOR);
if (!LongProcess.mainWindow.equals(window))
LongProcess.mainWindow
.setCursor(LongProcess.HOURGLASS_CURSOR);
}
}
}
});
try {
// Let the process run.
LongProcess.this.run();
} catch (final Throwable t) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
StackTrace.showStackTrace(t);
}
});
}
} catch (final Throwable t) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
StackTrace.showStackTrace(t);
}
});
} finally {
// Decrease the number of processes currently running.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
synchronized (LongProcess.lockObject) {
// If that was the last one, stop the
// hourglass.
if (--LongProcess.longProcessCount <= 0) {
if (window != null)
window
.setCursor(LongProcess.NORMAL_CURSOR);
if (!LongProcess.mainWindow
.equals(window))
LongProcess.mainWindow
.setCursor(LongProcess.NORMAL_CURSOR);
}
}
}
});
} catch (final Throwable t) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
StackTrace.showStackTrace(t);
}
});
}
}
// Nothing to return.
return null;
}
}.start();
}
/**
* Override this method to provide the long-process background behaviour.
*
* @throws Exception
* if anything goes wrong.
*/
public abstract void run() throws Exception;
}