/* * SpectPatch.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: * 24-Jun-06 renamed from Document to SpectPatch */ package de.sciss.fscape.session; import de.sciss.fscape.gui.OpConnector; import de.sciss.fscape.gui.OpIcon; import de.sciss.fscape.gui.OpPanel; import de.sciss.fscape.gui.SpectPatchDlg; import de.sciss.fscape.op.Operator; import de.sciss.fscape.op.SlotAlreadyConnectedException; import de.sciss.fscape.prop.BasicProperties; import de.sciss.fscape.prop.Presets; import de.sciss.fscape.prop.PropertyArray; import de.sciss.fscape.spect.SpectStreamSlot; import de.sciss.fscape.util.Slots; import java.awt.*; import java.io.IOException; import java.io.SyncFailedException; import java.util.Enumeration; import java.util.NoSuchElementException; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; /** * Contains the data of a spectral patch. * This is historic and should be completely rewritten * or deleted. */ public class SpectPatch { // -------- private variables -------- private SpectPatchDlg win; private String name = null; // private String pathName = null; // private boolean modified = false; public boolean running = false; public boolean pausing = false; private Vector<Operator> ops; // Operatoren private ThreadGroup opThreadGroup = null; // deren Thread-Gruppe private static final String FILE_OP = "Op"; // Erkennungs-Strings private static final String FILE_OPHEAD = "Head"; private static final String FILE_OPPROP = "Prop"; private static final String LINK_DEST = "Dest"; // Value = <ID>,<SlotName> private static final String LINK_LOC = "Loc"; // wenn vorhanden ==> con ist sichtbar private static final String HEAD_CLASS = "Class"; private static final String HEAD_NAME = "Name"; private static final String HEAD_LOC = "Loc"; private static final String HEAD_ALIAS = "Alias"; // Value = <FirstAlias>,<SecondAlias> etc. private static final String HEAD_ORIGINAL= "Orig"; // Value = <OriginalID> // private static final String ERR_ILLEGALFILE = "Wrong file format"; // -------- public methods -------- /** * Create empty SpectPatch */ public SpectPatch(SpectPatchDlg win) { ops = new Vector<Operator>(); this.win = win; // win.setVisible( true ); } /** * Replaces contents of this SpectPatch * * @return `false` if error occurs */ public boolean load(Properties file) { Operator op, op2; Point opLoc, conLoc; OpPanel opPanel; OpConnector con; String val; StringTokenizer valTok; Properties head; Properties opProp; Properties link; PropertyArray pa; boolean success = true; Vector<Operator> tempOps = new Vector<Operator>(); // Index dort = ID aus dem file! Enumeration slots; SpectStreamSlot slot1, slot2; String slotName; int k; int op2ID; try { // Datei geladen, wir koennen das Dokument leeren if (!clear()) { return false; } opPanel = win.getOpPanel(); // always do this *after* clear() // opPanel.setVisible( false ); for( int i = 0; (val = file.getProperty( FILE_OP + i + FILE_OPHEAD )) != null; i++ ) { head = Presets.valueToProperties( val ); val = file.getProperty( FILE_OP + i + FILE_OPPROP ); opProp = Presets.valueToProperties( val ); val = head.getProperty( HEAD_CLASS ); opLoc = BasicProperties.getPointProperty( head, HEAD_LOC ); if( opLoc == null ) { opLoc = new Point(); } if( val != null ) { val = Operator.PACKAGE + '.' + val; // voller Klassenname try { op = (Operator) Class.forName( val ).newInstance(); tempOps.addElement( op ); // um ihn bei der Linksuche wiederzufinden if( opProp != null ) { pa = op.getPropertyArray(); pa.fromProperties( true, opProp ); // Parameter laden } opPanel.addOperator( op, opLoc.x, opLoc.y ); // ---- Links ---- slots = op.getSlots( Slots.SLOTS_FREE ).elements(); while( slots.hasMoreElements() ) { slot1 = (SpectStreamSlot) slots.nextElement(); val = file.getProperty( FILE_OP + i + slot1.toString() ); link = Presets.valueToProperties( val ); if( link != null ) { val = link.getProperty( LINK_DEST ); if( (val == null) || (val.length() == 0) ) continue; try { k = val.indexOf( ',' ); slotName = val.substring( k + 1 ); op2ID = Integer.parseInt( val.substring( 0, k )); op2 = tempOps.elementAt( op2ID ); slot2 = op2.getSlot( slotName ); if( slot2 != null ) { opPanel.linkOperators( slot1, slot2 ); con = opPanel.getConnector( slot1 ); conLoc = BasicProperties.getPointProperty( link, LINK_LOC ); if( con != null ) { if( conLoc != null ) { // means it is visible! con.setVisible( true ); opPanel.moveConnector( con, conLoc.x, conLoc.y ); } else { con.setVisible( false ); } } } } catch( SlotAlreadyConnectedException e ) { success = false; // noch nichts genaueres XXX } catch( NoSuchElementException e ) { success = false; // noch nichts genaueres XXX } catch( NumberFormatException e ) { // kommt von Integer.parseInt() success = false; // noch nichts genaueres XXX } catch( ArrayIndexOutOfBoundsException e ) { // kommt von tempOps.elementAt() // macht nichts; wir sind nur noch nicht bis zu diesem // Element vorgedrungen } } } // ---- Name ---- val = head.getProperty( HEAD_NAME ); if( val != null ) { opPanel.renameOperator( op, val ); } // ---- Alia ---- val = head.getProperty( HEAD_ALIAS ); if( val != null ) { valTok = new StringTokenizer( val, "," ); while( valTok.hasMoreTokens() ) { try { op2ID = Integer.parseInt( valTok.nextToken() ); op2 = tempOps.elementAt( op2ID ); op2.turnIntoAlias( op ); } catch( SlotAlreadyConnectedException e ) { success = false; // noch nichts genaueres XXX } catch( SyncFailedException e ) { success = false; // noch nichts genaueres XXX } catch( NumberFormatException e ) { // kommt von Integer.parseInt() success = false; // noch nichts genaueres XXX } catch( ArrayIndexOutOfBoundsException e ) { // kommt von tempOps.elementAt() // macht nichts; wir sind nur noch nicht bis zu diesem // Element vorgedrungen } } } val = head.getProperty( HEAD_ORIGINAL ); if( val != null ) { try { op2ID = Integer.parseInt( val ); op2 = tempOps.elementAt( op2ID ); op.turnIntoAlias( op2 ); } catch( SlotAlreadyConnectedException e ) { success = false; // noch nichts genaueres XXX } catch( SyncFailedException e ) { success = false; // noch nichts genaueres XXX } catch( NumberFormatException e ) { // kommt von Integer.parseInt() success = false; // noch nichts genaueres XXX } catch( ArrayIndexOutOfBoundsException e ) { // kommt von tempOps.elementAt() // macht nichts; wir sind nur noch nicht bis zu diesem // Element vorgedrungen } } } // if( HEAD_CLASS exists ) catch( InstantiationException e1 ) { success = false; // noch nichts genaueres XXX } catch( IllegalAccessException e2 ) { success = false; // noch nichts genaueres XXX } catch( ClassNotFoundException e3 ) { success = false; // noch nichts genaueres XXX } } else { success = false; // kein Klassenname gefunden } } } catch( IOException e ) { success = false; } // if( success ) { // setFileName( pathName, name ); // setModified( false ); // } // opPanel = win.getOpPanel(); // opPanel.setVisible( true ); return success; } /* * Dieses SpectPatch unter bisherigem Namen speichern * * @return false bei Fehler */ public Properties save() { Operator op, op2; OpIcon opIcon; OpConnector con; OpPanel opPanel = win.getOpPanel(); String value; StringBuffer valBuf; Properties file = new Properties(); Properties head = new Properties(); Properties link = new Properties(); PropertyArray pa; Enumeration slots, alia; SpectStreamSlot slot1, slot2; int op2ID; synchronized( ops ) { for( int i = 0; i < ops.size(); i++ ) { op = ops.elementAt( i ); opIcon = (OpIcon) op.getIcon(); head.clear(); link.clear(); // ---- Head ---- value = op.getClass().getName(); value = value.substring( value.lastIndexOf( '.' ) + 1 ); head.put( HEAD_CLASS, value ); // Operator-Klasse head.put( HEAD_NAME, opIcon.getName() ); // Icon-Name BasicProperties.setPointProperty( head, HEAD_LOC, opIcon.getLocation() ); // Icon-Position op2 = op.getOriginal(); if( op2 != null ) { op2ID = ops.indexOf( op2 ); if( op2ID >= 0 ) { head.put( HEAD_ORIGINAL, String.valueOf( op2ID )); } else { // nothing yet XXX } } alia = op.getAliases(); if( alia.hasMoreElements() ) { valBuf = new StringBuffer(); while( alia.hasMoreElements() ) { op2 = (Operator) alia.nextElement(); op2ID = ops.indexOf( op2 ); if( op2ID >= 0 ) { valBuf.append( op2ID ); if( alia.hasMoreElements() ) valBuf.append( ',' ); } else { // nothing yet XXX } } head.put( HEAD_ALIAS, valBuf.toString() ); } value = Presets.propertiesToValue( head ); // in String verwandeln file.put( FILE_OP + i + FILE_OPHEAD, value ); // ...und schreiben // ---- Properties ---- pa = op.getPropertyArray(); value = Presets.propertiesToValue( pa.toProperties( true )); // Op-Einstellungen file.put( FILE_OP + i + FILE_OPPROP, value ); // ---- Links ---- slots = op.getSlots( Slots.SLOTS_LINKED ).elements(); while( slots.hasMoreElements() ) { slot1 = (SpectStreamSlot) slots.nextElement(); slot2 = slot1.getLinked(); if( slot2 != null ) { op2 = slot2.getOwner(); op2ID = ops.indexOf( op2 ); if( op2ID >= 0 ) { // Aufbau: <destOpID>,<destSlot> link.put( LINK_DEST, "" + op2ID + ',' + slot2.toString() ); con = opPanel.getConnector( slot1 ); if( (con != null) && con.isVisible() ) { BasicProperties.setPointProperty( link, LINK_LOC, con.getLocation() ); } } else { // nothing yet XXX } } value = Presets.propertiesToValue( link ); file.put( FILE_OP + i + slot1.toString(), value ); } } } return file; } /** * Dieses SpectPatch leeren * * @return false bei Fehler */ public boolean clear() { synchronized (ops) { win.clear(); ops.removeAllElements(); // setFileName( null, null ); // setModified( false ); } return true; } /** * Operatoren starten * TEST STADIUM */ public void start() { Operator op; Thread thread; synchronized (ops) { if (!running) { pausing = false; opThreadGroup = new ThreadGroup("Operators"); for (int i = 0; i < ops.size(); i++) { op = ops.elementAt(i); if (op.threadDead) { thread = new Thread(opThreadGroup, op, op.getIcon().getName()); op.threadPaused = false; op.threadDead = false; op.owner = this; // thread.setPriority( Thread.NORM_PRIORITY + 1 ); thread.start(); } } running = true; // Thread.currentThread().setPriority( Thread.NORM_PRIORITY - 1 ); } } } /** * Operatoren stoppen * TEST STADIUM */ public void stop() { Operator op; Enumeration slots; SpectStreamSlot slot; synchronized (ops) { if (running) { pausing = false; for (int i = 0; i < ops.size(); i++) { op = ops.elementAt(i); if (!op.threadDead) { op.runStop(); slots = op.getSlots(Slots.SLOTS_LINKED).elements(); while (slots.hasMoreElements()) { slot = (SpectStreamSlot) slots.nextElement(); synchronized (slot) { if (slot.state == SpectStreamSlot.STATE_WAITING) { // wartende Ops informieren slot.getOwnerThread().interrupt(); } } } } } while (opThreadGroup.activeCount() > 0) { try { Thread.sleep(500); // Operatoren schicken notify() beim Beenden! } catch (InterruptedException ignored) {} } running = false; } } // thread.setPriority( Thread.NORM_PRIORITY ); win.stop(); // moeglicherweise wurde gar kein Thread gestartet, der win.stop() } // aufrufen wuerde /** * Operatoren pausieren / Pause beenden * TEST STADIUM * * @param state true = Pause einlegen; false = Pause beenden */ public synchronized void pause(boolean state) { Operator op; Enumeration slots; SpectStreamSlot slot; boolean allPausing; synchronized (ops) { if (running && (state != pausing)) { for (int i = 0; i < ops.size(); i++) { op = ops.elementAt(i); if (!op.threadDead) { op.runPause(state); slots = op.getSlots(Slots.SLOTS_LINKED).elements(); while (slots.hasMoreElements()) { slot = (SpectStreamSlot) slots.nextElement(); synchronized (slot) { if (slot.state == SpectStreamSlot.STATE_WAITING) { // wartende Ops informieren slot.getOwnerThread().interrupt(); } } } } } if (state) { do { try { Thread.sleep(500); } catch (InterruptedException ignored) {} allPausing = true; for (int i = 0; i < ops.size(); i++) { op = ops.elementAt(i); if (!op.threadDead && !op.threadPausing) { allPausing = false; } } } while (!allPausing); } pausing = state; } } } /** * Muss von Operatoren aufgerufen werden, * wenn sie die run() Methode verlassen */ public synchronized void operatorTerminated(Operator op) { // notify(); if (Thread.activeCount() == 1) { // wenn ich der letzte bin running = false; win.stop(); // ...dann die Gadgets wieder freigeben } } /** * Muss von Operatoren aufgerufen werden, * wenn sie in den Pause-Modus wechseln */ public synchronized void operatorPaused(Operator op) { // notify(); } /** * Neuen Operator hinzufuegen */ public void addOperator(Operator op) { synchronized (ops) { ops.addElement(op); } // setModified( true ); } /** * Operator loeschen * RUFT Operator.dispose() AUF! */ public void removeOperator(Operator op) { synchronized (ops) { ops.removeElement(op); } op.dispose(); } public ModulePanel getModule() { return win; } // public AppWindow getWindow() // { // return win; // } /** * (Datei)namen des Dokuments ermitteln * * @return moeglicherweise null! */ public String getName() { return name; } /** * Liste aller Operatoren besorgen */ public Enumeration getOperators() { synchronized (ops) { return ops.elements(); } } }