/*
* PropertyGUI.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
*/
package de.sciss.fscape.gui;
import de.sciss.fscape.prop.PropertyArray;
import de.sciss.fscape.util.Constants;
import de.sciss.fscape.util.Param;
import de.sciss.fscape.util.ParamSpace;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.util.Hashtable;
import java.util.StringTokenizer;
/**
* Another idea to simplify GUI creation which kind
* of misses the point of getting the GUI description
* out of the source code.
*/
public class PropertyGUI
extends GUISupport
implements ItemListener {
// -------- public variables --------
// toplevel codes; separator ';' oder '\n' fuer neue Zeile
public static final int objGroupLabel = ('g' << 8) + 'l'; // <Name>
public static final int objLabel = ('l' << 8) + 'b'; // <Name>
public static final int objJCheckBox = ('c' << 8) + 'b'; // <Name>
public static final int objJComboBox = ('c' << 8) + 'h';
public static final int objParamField = ('p' << 8) + 'f'; // [<spaceID>[|<spaceID>...]]
// public static final int objFontField = (int) 'f' << 8 + (int) 'f'; // <Type>
public static final int objPathField = ('i' << 8) + 'o'; // <Type>|<Requester-Txt>
// public static final int objColorJComboBox = (int) 'c' << 8 + (int) 'c'; // <Flags>[|<Image-Filename>]
public static final int objEditEnv = ('e' << 8) + 'n';
public static final int objSkip = ('s' << 8) + 'k'; // Platzhalter
// generic; separator ','
public static final int attrCols = ('w' << 8) + 'i'; // <Spaltenbreite>
public static final int attrID = ('i' << 8) + 'd'; // <Array-Index>
public static final int attrPropName = ('p' << 8) + 'r'; // <Properties-Key>
public static final int attrAction = ('a' << 8) + 'c'; // <condition>|<target obj ID>|<Command>[|<target 2>|<cmd 2>...]
/**
* generic action commands
* mehrere Befehle einfach durch mehrere <ac> Befehle!
*/
public static final int actionEnable = ('e' << 8) + 'n';
public static final int actionDisable = ('d' << 8) + 'i';
/**
* choice
* action-conditions: <itemID> ==> Action wird ausgefuehrt,
* wenn diese Item selectiert wurde
* z.B. <ac1|5|en> ==> wenn Item#1 selektiert wurde,
* soll die Componente mit ID5 enabled werden
*/
public static final int chItem = ('i' << 8) + 't'; // JComboBox-Item <Name>
public static final int chSelect = ('s' << 8) + 'e'; // <ItemID>
// paramfield
public static final int pfSpaces = ('s' << 8) + 'p'; // <ParamUnit>[|<ParamUnit>...]
public static final int pfReference = ('r' << 8) + 'e'; // <value>|<unit> oder <obj ID>
// label
public static final int lbText = ('t' << 8) + 'x'; // <new name>
// checkbox
public static final int cbState = ('s' << 8) + 't'; // <true> bzw. <false>
public static final int MAX_ID = 0xFFFF; // maximale User-Object-ID
// -------- private variables --------
protected PropertyArray pr;
protected Hashtable<Integer, PropertyComponent> hID; // key = ID, value = PropertyComponent
protected Hashtable<Object, PropertyComponent> hObj; // key = Component
protected Hashtable<String, PropertyComponent> hPropName; // key = Properties-Keyname
// -------- public methods --------
// public void fillPropertyArray( PropertyArray pr );
// public void fillGUI( PropertyArray pr );
/* LANGFRISTIG
GUISupport entsprechend anpassen und einiges von hier rausschmeissen
und dort implementieren: die komplexeren Hashtables + PropComp-Class!
*/
/**
* @param descr Beschreibung der Oberflaeche. Zeilen werden mit '\n'
* abgeschlossen, Objekte mit ';' getrennt. Jedes Objekt
* beginnt mit einem 2-Buchstaben-Namen (siehe constants
* 'obj...').
*
* Es folgt der Name und alle Attribute durch Kommata
* getrennt. Attribut: 2-Buchstaben-ID gefolgt von Auspraegung,
* z.B. 'wi2' = Breite zwei Spalten.
*/
public PropertyGUI( String descr )
{
super( -1 );
final Component parent = SwingUtilities.windowForComponent(this);
hObj = new Hashtable<Object, PropertyComponent>();
hID = new Hashtable<Integer, PropertyComponent>();
hPropName = new Hashtable<String, PropertyComponent>();
StringTokenizer linTok; // Zeilen
StringTokenizer objTok; // Objekte ("Components")
StringTokenizer attrTok; // Parameter eines Objekts
StringTokenizer microTok; // unterste Ebene, "|" Separator
String token;
String objName;
String attrName;
String objPropName; // null, wenn kein zu registrierendes Property
String objActionCmd; // ggf. null; mehrere Cmd durch Kommata getrennt
int objCode; // type
int attrCode;
int objID; // fuer Hash; -1 wenn es nicht registriert werden muss
int autoID; // automatisch registrieren wenn Listener installiert wird
int toLineEnd;
ParamSpace[] spaces; // fuer ParamField
Component cmp = null;
PropertyComponent prCmp;
// setBackground( Color.white );
con.anchor = GridBagConstraints.WEST;
con.insets = new Insets( 0, 8, 0, 0 );
con.fill = GridBagConstraints.BOTH;
autoID = MAX_ID + 1;
linTok = new StringTokenizer( descr, "\n" );
while( linTok.hasMoreTokens() ) {
// Neue Gadget-Zeile
objTok = new StringTokenizer( linTok.nextToken(), ";" );
toLineEnd = objTok.countTokens();
for( int col = 0; objTok.hasMoreTokens(); toLineEnd--, col++ ) {
objPropName = null;
objActionCmd = null;
objID = -1;
if( toLineEnd == 1 ) {
con.weightx = 0.9;
} else {
con.gridwidth = 1;
con.weightx = 0.0;
}
attrTok = new StringTokenizer( objTok.nextToken(), "," );
token = attrTok.nextToken();
objCode = (token.charAt( 0 ) << 8) + token.charAt( 1 );
objName = token.substring( 2 );
// Componente erzeugen
switch( objCode ) {
case objParamField:
if( objName.length() == 0 ) {
cmp = new ParamField();
} else {
microTok = new StringTokenizer( objName, "|" );
spaces = new ParamSpace[ microTok.countTokens() ];
for( int i = 0; i < spaces.length; i++ ) {
spaces[ i ] = Constants.spaces[ Integer.parseInt( microTok.nextToken() )];
}
cmp = new ParamField( spaces );
}
con.weightx = 0.5;
con.gridwidth = 3;
break;
case objEditEnv:
cmp = new EnvIcon( parent );
con.weightx = 0.0;
break;
case objJCheckBox:
cmp = new JCheckBox( objName );
break;
case objJComboBox:
cmp = new JComboBox();
break;
case objLabel:
cmp = new JLabel( objName, col == 0 ? SwingConstants.RIGHT : SwingConstants.LEFT );
break;
case objGroupLabel:
cmp = new GroupLabel( objName, GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE );
break;
case objPathField:
microTok = new StringTokenizer( objName, "|" );
cmp = new PathField( Integer.parseInt( microTok.nextToken()),
microTok.nextToken() );
con.weightx = 0.5;
con.gridwidth = 2;
break;
// case objFontField:
// cmp = new FontField( Integer.parseInt( objName ));
// con.weightx = 0.5;
// con.gridwidth = 2;
// break;
// case objColorJComboBox:
// microTok = new StringTokenizer( objName, "|" );
// if( microTok.countTokens() > 1 ) { // Flags + Image file
// cmp = new ColorChoice( Integer.parseInt( microTok.nextToken() ),
// microTok.nextToken() );
// } else { // just Flags
// cmp = new ColorChoice( Integer.parseInt( objName ));
// }
// break;
case objSkip:
cmp = new JLabel();
break;
default: // ERROR
System.out.println( "PropertyGUI: Unknown Objectcode " + objCode );
return;
}
// Attribute uebersetzen
while( attrTok.hasMoreTokens() ) {
token = attrTok.nextToken();
attrCode = (token.charAt( 0 ) << 8) + token.charAt( 1 );
attrName = token.substring( 2 );
// generic
switch( attrCode ) {
case attrID:
objID = Integer.parseInt( attrName );
break;
case attrPropName:
objPropName = attrName;
if( objID == -1 ) {
objID = autoID; // wichtig, damit die fill-Routine uns findet
}
break;
case attrAction:
if( objActionCmd == null ) {
objActionCmd = attrName;
} else {
objActionCmd += ',' + attrName;
}
if( objID == -1 ) {
objID = autoID; // wichtig, damit der Listener uns findet
}
break;
case attrCols:
con.gridwidth = Integer.parseInt( attrName );
break;
default:
// Component-spezifisch
switch( objCode ) {
case objParamField:
switch( attrCode ) {
case pfReference:
int i = attrName.indexOf( '|' );
if( i > 0 ) { // Param-Reference
((ParamField) cmp).setReference( new Param( Double.valueOf(
attrName.substring( 0, i )).doubleValue(),
Integer.parseInt( attrName.substring( i + 1 ))));
} else { // ParamField-Reference
PropertyComponent targetPrCmp = hID.get(
Integer.valueOf( attrName ));
if( targetPrCmp != null ) {
((ParamField) cmp).setReference( (ParamField) targetPrCmp.cmp );
}
}
break;
default:
break;
}
break;
case objJComboBox:
switch( attrCode ) {
case chItem:
((JComboBox) cmp).addItem( attrName );
break;
default:
break;
}
default:
break;
}
break; // Component-spezifisch Ende
}
} // attrTok
if( toLineEnd == 1 ) {
con.gridwidth = GridBagConstraints.REMAINDER;
}
// register Component in da hash
if( objID != -1 ) {
prCmp = new PropertyComponent();
prCmp.cmp = cmp;
prCmp.ID = objID;
prCmp.type = objCode;
prCmp.actionCmd = objActionCmd;
hID.put( new Integer( objID ), prCmp );
hObj.put( cmp, prCmp );
if( objPropName != null ) {
hPropName.put( objPropName, prCmp );
}
// Listener installieren
if( objActionCmd != null ) {
switch( objCode ) {
case objJCheckBox:
((JCheckBox) cmp).addItemListener( this );
break;
case objJComboBox:
((JComboBox) cmp).addItemListener( this );
break;
default:
break;
}
}
}
// add Component to this panel
lay.setConstraints( cmp, con );
add( cmp );
autoID++;
} // objTok
con.fill = GridBagConstraints.HORIZONTAL;
}
}
/**
* Das PropertyArray wird mit Werten aus den Gadgets gefuellt;
* NOTE: Nur mit PropertyName (boolName etc.) versehene Werte
* werden beruecksichtigt
*/
public void fillPropertyArray( PropertyArray pa )
{
fillPropertyArray( pa, pa.boolName );
fillPropertyArray( pa, pa.intgName );
fillPropertyArray( pa, pa.textName );
fillPropertyArray( pa, pa.paraName );
fillPropertyArray( pa, pa.envlName );
// fillPropertyArray( pa, pa.fontName );
// fillPropertyArray( pa, pa.colrName );
}
/**
* Die Gadgets mit Werten aus einem PropertyArray fuellen;
* ggf. werden ActionCommands ausgefuehrt.
* NOTE: Nur mit PropertyName (boolName etc.) versehene Werte
* werden beruecksichtigt
*/
public void fillGUI( PropertyArray pa )
{
fillGUI( pa, pa.boolName );
fillGUI( pa, pa.intgName );
fillGUI( pa, pa.textName );
fillGUI( pa, pa.paraName );
fillGUI( pa, pa.envlName );
// fillGUI( pa, pa.fontName );
// fillGUI( pa, pa.colrName );
}
// -------- Item methods (JCheckBox + JComboBox) --------
public void itemStateChanged( ItemEvent e )
{
PropertyComponent prCmp;
prCmp = hObj.get( e.getSource() );
if( prCmp != null ) {
processActionCommand( prCmp );
}
}
// -------- protected methods --------
/*
* Fuellt die PropertyArray-Variablen mit den
* Werten der korrespondierenden Gadgets
*
* @param prNames zu bearbeitender Feldtyp, z.B. pr.boolName
*/
protected void fillPropertyArray( PropertyArray pa, String prNames[] )
{
PropertyComponent prCmp;
Component cmp;
for( int i = 0; i < prNames.length; i++ ) {
if( prNames[ i ] != null ) {
prCmp = hPropName.get( prNames[ i ]);
if( prCmp != null ) {
cmp = prCmp.cmp;
switch( prCmp.type ) {
case objParamField:
pa.para[ i ] = ((ParamField) cmp).getParam();
break;
case objEditEnv:
pa.envl[ i ] = ((EnvIcon) cmp).getEnv();
break;
case objJCheckBox:
pa.bool[ i ] = ((JCheckBox) cmp).isSelected();
break;
case objJComboBox:
pa.intg[ i ] = ((JComboBox) cmp).getSelectedIndex();
break;
case objPathField:
pa.text[ i ] = ((PathField) cmp).getPath().getPath();
break;
// case objFontField:
// pr.font[ i ] = ((FontField) cmp).getFontSC();
// break;
// case objColorJComboBox:
// pr.colr[ i ] = ((ColorChoice) cmp).getColor();
// break;
default:
return;
}
}
}
}
}
/*
* Fuellt die Gadgets mit den Werten der
* korrespondierenden PropertyArray-Variablen;
* ggf. werden ActionCommands ausgefuehrt
*
* @param prNames zu bearbeitender Feldtyp, z.B. pr.boolName
*/
protected void fillGUI( PropertyArray pa, String prNames[] )
{
PropertyComponent prCmp;
Component cmp;
for( int i = 0; i < prNames.length; i++ ) {
if( prNames[ i ] != null ) {
prCmp = hPropName.get( prNames[ i ]);
if( prCmp != null ) {
cmp = prCmp.cmp;
switch( prCmp.type ) {
case objParamField:
((ParamField) cmp).setParam( pa.para[ i ]);
break;
case objEditEnv:
((EnvIcon) cmp).setEnv( pa.envl[ i ]);
break;
case objJCheckBox:
((JCheckBox) cmp).setSelected( pa.bool[ i ]);
break;
case objJComboBox:
((JComboBox) cmp).setSelectedIndex( pa.intg[ i ]);
break;
case objPathField:
((PathField) cmp).setPath( new File( pa.text[ i ]));
break;
// case objFontField:
// ((FontField) cmp).setFont( pr.font[ i ]);
// break;
// case objColorJComboBox:
// ((ColorChoice) cmp).setColor( pr.colr[ i ]);
// break;
default:
return;
}
if( prCmp.actionCmd != null ) {
processActionCommand( prCmp );
}
}
}
}
}
/**
* Fuehrt die Befehle einer PropertyComponente aus
*/
protected void processActionCommand( PropertyComponent srcPrCmp )
{
StringTokenizer actionTok;
StringTokenizer cmdTok;
String condition;
String target;
String cmd;
int cmdCode;
PropertyComponent targetPrCmp;
boolean conditionFulfilled = false;
actionTok = new StringTokenizer( srcPrCmp.actionCmd, "," );
while( actionTok.hasMoreTokens() ) {
cmdTok = new StringTokenizer( actionTok.nextToken(), "|" );
condition = cmdTok.nextToken();
while( cmdTok.hasMoreTokens() ) {
target = cmdTok.nextToken();
cmd = cmdTok.nextToken();
targetPrCmp = hID.get( Integer.valueOf( target ));
if( targetPrCmp != null ) {
switch( srcPrCmp.type ) {
case objJCheckBox:
conditionFulfilled = ((JCheckBox) srcPrCmp.cmp).isSelected() ==
Boolean.valueOf( condition ).booleanValue();
break;
case objJComboBox:
conditionFulfilled = ((JComboBox) srcPrCmp.cmp).getSelectedIndex() ==
Integer.parseInt( condition );
break;
default:
conditionFulfilled = false;
break;
}
if( conditionFulfilled ) { // Bedingung erfuellt, Cmd ausfuehren
cmdCode = (cmd.charAt( 0 ) << 8) + cmd.charAt( 1 );
// generic
switch( cmdCode ) {
case actionEnable:
targetPrCmp.cmp.setEnabled( true );
break;
case actionDisable:
targetPrCmp.cmp.setEnabled( false );
break;
default:
// Component-type specific
// XXX NOT YET
switch( targetPrCmp.type ) {
default:
break;
}
}
}
}
} // alle Befehle zu einer Condition
} // naechste Befehlskette
}
// -------- internal class zur Verwaltung der Components --------
class PropertyComponent {
Component cmp;
int ID;
int type; // e.g. objJCheckBox
String actionCmd; // <condition>|<targetID>|<cmd>[|<targetID2>|<cmd2>...]
// [,<condition>|<targetID>...]
}
}