/*
* ProcessPanel.java
* (FScape)
*
* Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU General Public License v3+
*
*
* For further information, please contact Hanns Holger Rutz at
* contact@sciss.de
*
*
* Changelog:
* 21-May-05 modernized
* 03-Oct-06 updated
*/
package de.sciss.fscape.gui;
import de.sciss.app.BasicEvent;
import de.sciss.app.EventManager;
import de.sciss.fscape.proc.Processor;
import de.sciss.fscape.proc.ProcessorEvent;
import de.sciss.fscape.proc.ProcessorListener;
import de.sciss.gui.GUIUtil;
import de.sciss.gui.ProgressComponent;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* GUI container with a progress bar and
* start / stop button.
*/
public class ProcessPanel
extends JPanel
implements EventManager.Processor {
// -------- public variables --------
public static final int TYPE_ASYNC = 0x04;
public static final int STATE_STOPPED = 0;
public static final int STATE_RUNNING = 1;
public static final int STATE_PAUSING = 2;
public static final int STATE_WARMUP = 3; // Process has started but not given feedback yet
// -------- private variables --------
private final ProgressPanel pProgress;
private final Action actionClose;
private final ActionProcess actionProcess;
private final JButton ggProcess;
private final EventManager elm = new EventManager( this );
private final ProcessorListener pl;
private Window win;
private int state = STATE_STOPPED;
private Processor proc;
private Thread procThread;
private static final String txt[] = { " Render ", "Stop", "Resume", "???" };
// -------- public methods --------
/**
* @param type TYPE_... (OR-combined)
* @param proc algorithm to run
*/
public ProcessPanel( final int type, final ProgressPanel pProgress, Processor proc )
{
super();
setLayout( new BoxLayout( this, BoxLayout.X_AXIS ));
this.pProgress = pProgress;
actionClose = new ActionClose("Close");
actionProcess = new ActionProcess();
pl = new ProcessorListener() {
public void processorProgress( ProcessorEvent e )
{
pProgress.setProgression( e.getProcessor().getProgression() );
}
public void processorStarted( ProcessorEvent e )
{
actionClose.setEnabled( false );
state = STATE_RUNNING;
if( (type & TYPE_ASYNC) != 0 ) {
pProgress.setProgression( -1f );
}
updateSchnucki( e );
}
public void processorStopped( ProcessorEvent e )
{
actionClose.setEnabled( true );
if( e.getProcessor().getError() != null ) {
pProgress.finishProgression( ProgressComponent.FAILED );
} else if( e.getProcessor().getProgression() == 1.0f ) {
pProgress.finishProgression( ProgressComponent.DONE );
} else if( (type & TYPE_ASYNC) != 0 ) {
pProgress.resetProgression();
}
state = STATE_STOPPED;
updateSchnucki( e );
}
public void processorPaused( ProcessorEvent e )
{
state = STATE_PAUSING;
pProgress.pause();
updateSchnucki( e );
}
public void processorResumed( ProcessorEvent e )
{
state = STATE_RUNNING;
pProgress.resume();
updateSchnucki( e );
}
private void updateSchnucki( ProcessorEvent e )
{
actionProcess.updateState();
if( state != STATE_RUNNING ) setEnabled( true );
elm.dispatchEvent( new ProcessorEvent( e.getSource(), e.getID(),
System.currentTimeMillis(), e.getProcessor() ));
}
};
ggProcess = new JButton( actionProcess );
final InputMap imap = ggProcess.getInputMap( JComponent.WHEN_IN_FOCUSED_WINDOW );
final ActionMap amap = ggProcess.getActionMap();
imap.put( KeyStroke.getKeyStroke( KeyEvent.VK_PERIOD, InputEvent.META_MASK ), "stop" );
amap.put( "stop", new ActionStop() );
add( pProgress );
add( ggProcess );
// add( CoverGrowBox.create() );
add(Box.createHorizontalStrut(16));
ggProcess.setFocusable( false );
addPropertyChangeListener( "font", new PropertyChangeListener() {
public void propertyChange( PropertyChangeEvent e )
{
final Dimension d = ggProcess.getPreferredSize();
GUIUtil.constrainSize( ggProcess, d.width, d.height );
}
});
pProgress.addCancelListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{
stop();
}
});
setProcessor( proc );
}
public void setProcessor( Processor proc )
{
if( this.proc != null ) {
this.proc.removeProcessorListener( pl );
}
this.proc = proc;
if( proc != null ) {
actionProcess.setEnabled( true );
proc.addProcessorListener( pl );
} else {
actionProcess.setEnabled( false );
}
}
public int getState()
{
return state;
}
public void start()
{
if( (proc != null) && (state == STATE_STOPPED) ) {
setEnabled( false );
// ggProgress.reset();
pProgress.resetProgression();
procThread = new Thread( proc, proc.toString() );
procThread.start(); // ! wenn jemand getState() aufruft, koennte nach
state = STATE_WARMUP; // ! Thread.start() ein STATE_STOPPED fatal sein!!
}
}
public void stop()
{
if( (proc != null) && ((state == STATE_RUNNING) || (state == STATE_PAUSING)) ) {
setEnabled( false );
proc.stop();
if( (procThread != null) && !procThread.isAlive() ) {
// Thread died because of error, unblock GUI
pl.processorStopped( new ProcessorEvent( proc, ProcessorEvent.STOPPED, 0, proc ));
}
}
}
public void pause()
{
if( (proc != null) && (state == STATE_RUNNING) ) {
setEnabled( false );
proc.pause();
if( (procThread != null) && !procThread.isAlive() ) {
// Thread died because of error, unblock GUI
pl.processorStopped( new ProcessorEvent( proc, ProcessorEvent.STOPPED, 0, proc ));
}
}
}
public void resume()
{
if( (proc != null) && (state == STATE_PAUSING) ) {
setEnabled( false );
proc.resume();
}
}
public void addProcessorListener( ProcessorListener li )
{
elm.addListener( li );
}
public void removeProcessorListener( ProcessorListener li )
{
elm.removeListener( li );
}
// ---------- EventManager.Processor interface ----------
public void processEvent( BasicEvent e )
{
ProcessorListener li;
for( int i = 0; i < elm.countListeners(); i++ ) {
li = (ProcessorListener) elm.getListener( i );
switch( e.getID() ) {
// case ProcessorEvent.PROGRESS:
// li.processorProgress( (ProcessorEvent) e );
// break;
case ProcessorEvent.STARTED:
li.processorStarted( (ProcessorEvent) e );
break;
case ProcessorEvent.STOPPED:
li.processorStopped( (ProcessorEvent) e );
break;
case ProcessorEvent.PAUSED:
li.processorPaused( (ProcessorEvent) e );
break;
case ProcessorEvent.RESUMED:
li.processorResumed( (ProcessorEvent) e );
break;
default:
assert false : e.getID();
}
} // for( i = 0; i < elm.countListeners(); i++ )
}
public void setEnabled(boolean state) {
actionProcess.setEnabled(state);
}
public void setPaint(Paint c) {
pProgress.setPaint(c);
}
public void setText(String t) {
pProgress.setProgressionText(t);
}
// -------- internal classes --------
private class ActionProcess
extends AbstractAction {
protected ActionProcess() {
super(txt[state]);
}
protected void updateState() {
}
public void actionPerformed(ActionEvent e) {
if (state == STATE_STOPPED) {
start();
}
}
}
private class ActionStop
extends AbstractAction {
protected ActionStop() {
super();
}
public void actionPerformed(ActionEvent e) {
if (state == STATE_RUNNING || state == STATE_PAUSING) {
stop();
}
}
}
private class ActionClose
extends AbstractAction {
protected ActionClose(String label) {
super(label);
}
public void actionPerformed(ActionEvent e) {
if ((state == STATE_STOPPED) && (win != null)) {
win.dispatchEvent(new WindowEvent(win, WindowEvent.WINDOW_CLOSING));
}
}
}
}