Package de.sciss.meloncillo.session

Source Code of de.sciss.meloncillo.session.SessionObjectTable$Renderer

/*
*  SessionObjectTable.java
*  Meloncillo
*
*  Copyright (c) 2004-2008 Hanns Holger Rutz. All rights reserved.
*
*  This software is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either
*  version 2, june 1991 of the License, or (at your option) any later version.
*
*  This software is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License (gpl.txt) along with this software; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*
*  For further information, please contact Hanns Holger Rutz at
*  contact@sciss.de
*
*
*  Changelog:
*    03-Feb-05  created
*    27-Mar-05  adds support for dynamic plug-in created contexts and
*          boolean and string type values
*    08-Apr-05  bugfix : edits are undoable now
*    27-Apr-05  bugfix : cancels editing when keys are updated
*/

package de.sciss.meloncillo.session;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import de.sciss.app.AbstractApplication;
import de.sciss.app.AbstractCompoundEdit;
import de.sciss.app.Document;
import de.sciss.app.DynamicAncestorAdapter;
import de.sciss.app.DynamicListening;
import de.sciss.gui.AbstractWindowHandler;
import de.sciss.gui.NumberEvent;
import de.sciss.gui.NumberField;
import de.sciss.gui.NumberListener;
import de.sciss.gui.PathEvent;
import de.sciss.gui.PathField;
import de.sciss.gui.PathListener;
import de.sciss.gui.StringItem;
import de.sciss.meloncillo.edit.BasicCompoundEdit;
import de.sciss.meloncillo.edit.EditPutMapValue;
import de.sciss.meloncillo.plugin.PlugInManager;
import de.sciss.meloncillo.util.MapManager;
import de.sciss.util.NumberSpace;

/**
*  This class is used to display a session object's
*  map entries in the observer palette. it is a <code>JTable</code>
*  with two columns for the key name and the corresponding value.
*  When plug-ins are activated or deactivated or when the
*  map changes, the table is automatically updated. The
*  map manager's contexts are scanned for items that should be
*  displayed in the palette, and for plug-in specific entries are
*  matched with the currently active plug-ins.
*
@author    Hanns Holger Rutz
@version  0.75, 10-Jun-08
*/
public class SessionObjectTable
extends JTable
implements DynamicListening
{
  private Document          doc      = null;
  private final List          keys    = new ArrayList();
  private final Map          contexts  = new HashMap();
  private final List          collObjects  = new ArrayList();
  private final AbstractTableModel  model;
  private final TableCellEditor    editor;
 
  private final MapManager.Listener  soListener, plugListener;
 
  private static final String[] columnNames = new String[] { "key", "value" }// not used
 
  public SessionObjectTable()
  {
    super();
//
//    this.doc  = doc;

//System.out.println( "doc = " + doc );
//   
    model    = new Model();
   
    setModel( model );
    setRowSelectionAllowed( false );
    TableColumn  tc;
    tc    = getColumnModel().getColumn( 0 );
    tc.setPreferredWidth( 92 );
    tc.setMaxWidth( 92 ); // XXX
    tc    = getColumnModel().getColumn( 1 );
    tc.setCellRenderer( new Renderer() );
    editor  = new Renderer();
    tc.setCellEditor( editor );
    setRowHeight( 24 )// XXX
    setBackground( null );
    setShowGrid( false );

    // ------- listeners -------
    soListener = new MapManager.Listener() {
      public void mapChanged( MapManager.Event e )
      {
        if( e.getSource() == SessionObjectTable.this ) return;
     
        Iterator  iter;
        int      row;
       
        iter = e.getPropertyNames().iterator();
         
        while( iter.hasNext() ) {
          row = keys.indexOf( iter.next() );
          if( row >= 0 ) {
            model.fireTableCellUpdated( row, 1 );
          }
        }
      }
       
      public void mapOwnerModified( MapManager.Event e ) {}
    };
   
    plugListener = new MapManager.Listener() {
      public void mapChanged( MapManager.Event e )
      {
        unregisterAll();
        updateKeysAndContexts()// calls model.fireTableDataChanged();
        registerAll();
      }
       
      public void mapOwnerModified( MapManager.Event e ) {}
    };

        new DynamicAncestorAdapter( this ).addTo( this );
  }
 
  public void setDocument( Document doc )
  {
    this.doc = doc;
  }
 
// ---------------- DynamicListening interface ----------------

    public void startListening()
    {
      PlugInManager.getInstance().addListener( plugListener );
 
      updateKeysAndContexts(); // calls model.fireTableDataChanged();
      registerAll();
    }

    public void stopListening()
    {
      unregisterAll();
    }
 
  // sync: to be called with sync on doors and sync
  private void registerAll()
  {
    SessionObject so;
   
    for( int i = 0; i < collObjects.size(); i++ ) {
      so = (SessionObject) collObjects.get( i );
      so.getMap().addListener( soListener );
    }
  }

  // sync: to be called with sync on doors and sync
  private void unregisterAll()
  {
    SessionObject so;
   
    for( int i = 0; i < collObjects.size(); i++ ) {
      so = (SessionObject) collObjects.get( i );
      so.getMap().removeListener( soListener );
    }
  }

  public void setObjects( List collObjects )
  {
    unregisterAll();
    this.collObjects.clear();
    this.collObjects.addAll( collObjects );
    updateKeysAndContexts()// calls model.fireTableDataChanged();
    registerAll();
  }
 
  // sync: to be called with sync on 'sync' and on doors
  // this method invokes model.fireTableDataChanged()
  private void updateKeysAndContexts()
  {
    MapManager      map;
    Object        o;
    MapManager.Context  c;
    SessionObject    so;

    if( isEditing() ) editor.cancelCellEditing();

    keys.clear();
    contexts.clear();
    if( !collObjects.isEmpty() ) {
      so  = (SessionObject) collObjects.get( 0 );
      map  = so.getMap();
      keys.addAll( map.keySet( MapManager.Context.FLAG_OBSERVER_DISPLAY,
                   MapManager.Context.NONE_EXCLUSIVE ));

      for( int i = keys.size() - 1; i >= 0 ; i-- ) {
        o  = keys.get( i );
        c  = map.getContext( o.toString() );
        contexts.put( o, c );
        // remove fields that belong to inactive plug-ins
        if( (c.dynamic != null) &&
          (PlugInManager.getInstance().getValue( c.dynamic.toString() ) == null) ) {
          keys.remove( o );
        }
      }
    }
    for( int i = 1; i < collObjects.size(); i++ ) {
      so  = (SessionObject) collObjects.get( i )// only common fields are displayed
      map  = so.getMap();
      keys.retainAll( map.keySet( MapManager.Context.FLAG_OBSERVER_DISPLAY,
                    MapManager.Context.NONE_EXCLUSIVE ));
    }
    model.fireTableDataChanged();
  }

  private class Model
  extends AbstractTableModel
  {
    public String getColumnName( int col )
    {
      return columnNames[ col ];
    }
   
    public int getRowCount()
    {
      return keys.size();
    }
   
    public int getColumnCount()
    {
      return 2;
    }
   
    public Object getValueAt( int row, int col )
    {
      if( row >= keys.size() ) return null;

      switch( col ) {
      case 0:
        String resKey = ((MapManager.Context) contexts.get( keys.get( row ))).label;
        if( resKey != null ) {
          // plug-ins will not store resKeys but human readable text
          return( AbstractApplication.getApplication().getResourceString( resKey, resKey ));
        }
        return( keys.get( row ).toString() );
       
      case 1:
        return getCommonValue( row );
       
      default:
        return null;
      }
    }

      public Class getColumnClass( int col )
    {
      switch( col ) {
      case 0:
        return String.class;
      case 1:
        return MapManager.Context.class;
      default:
        return Object.class;
      }
    }
 
    public boolean isCellEditable( int row, int col )
    {
      return( col == 1 );
    }
   
    // sync: caller must sync on door and sync
    private Object getCommonValue( int row )
    {
      Object val, val2;
      int i;
   
      val = ((SessionObject) collObjects.get( 0 )).getMap().getValue( keys.get( row ).toString() );
      if( val == null ) return null;
      if( collObjects.size() > 1 ) {
        for( i = 1; i < collObjects.size(); i++ ) {
          val2 = ((SessionObject) collObjects.get( i )).getMap().getValue( keys.get( row ).toString() );
          if( val2 == null || !val2.equals( val )) {
            return null;
          }
        }
      }
      return val;
    }

    public void setValueAt( Object value, int row, int col )
    {
      if( (doc == null) || (col != 1) || (value == null) ) return;
   
      if( row >= keys.size() ) return;

      SessionObject      so;
      MapManager        map;
      final String      key    = keys.get( row ).toString();
      boolean          addEdit  = false;
      AbstractCompoundEdit  edit  = new BasicCompoundEdit();

      for( int i = 0; i < collObjects.size(); i++ ) {
        so  = (SessionObject) collObjects.get( i );
        map = so.getMap();
        if( map.containsKey( key )) {
          edit.addPerform( new EditPutMapValue( SessionObjectTable.this, map, key, value ));
          addEdit = true;
        }
      }
      if( addEdit ) {
        edit.perform();
        edit.end();
        doc.getUndoManager().addEdit( edit );
      }
    }
  } // class Model
 
  private class Renderer
  extends AbstractCellEditor
  implements TableCellRenderer, TableCellEditor, NumberListener, PathListener, ActionListener
  {
    private NumberField  ggNumberField  = null;
    private JTextField  ggTextField    = null;
    private Map      mapPathFields  = new HashMap();
    private JLabel    ggLabel      = null;
    private JCheckBox  ggCheckBox    = null;
    private JComboBox  ggComboBox    = null;
    // they are placed on a panel to avoid that there size is
    // maximized and looks ugly
    private JPanel    panelComboBox, panelTextField;
 
    private Object    editorValue    = null;
 
    private Renderer()
    {
      super();
    }

    public Object getCellEditorValue()
    {
      return editorValue;
    }

    private void prepareNumberField()
    {
      if( ggNumberField == null ) {
        ggNumberField = new NumberField( NumberSpace.genericDoubleSpace );
        ggNumberField.addListener( this );
//        GUIUtil.setDeepFont( ggNumberField, GraphicsUtil.smallGUIFont );
        AbstractWindowHandler.setDeepFont( ggNumberField );
      }
    }
   
    public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int col )
    {
      editorValue = value;
      return getComponent( value, row, col, true );
    }

    public Component getTableCellRendererComponent( JTable table, Object value,
                            boolean isSelected, boolean hasFocus,
                            int row, int col )
    {
      Component c = getComponent( value, row, col, false );
//      if( c != null ) {
//        Dimension d = c.getPreferredSize();
//        if( d.height != table.getRowHeight( row )) {
//          table.setRowHeight( row, d.height );
//        }
//      }
      return c;
    }
   
    private Component getComponent( Object value, int row, int col, boolean isEditor )
    {
      NumberSpace ns;

      if( row >= keys.size() || col != 1 ) return null;

      MapManager.Context c = (MapManager.Context) contexts.get( keys.get( row ));
      switch( c.type ) {
      case MapManager.Context.TYPE_INTEGER:
      case MapManager.Context.TYPE_LONG:
      case MapManager.Context.TYPE_FLOAT:
      case MapManager.Context.TYPE_DOUBLE:
        prepareNumberField();
        if( (c.typeConstraints != null) && (c.typeConstraints instanceof NumberSpace) ) {
          ns = (NumberSpace) c.typeConstraints;
        } else {
          ns = (c.type == MapManager.Context.TYPE_INTEGER) || (c.type == MapManager.Context.TYPE_LONG) ?
             NumberSpace.genericIntSpace : NumberSpace.genericDoubleSpace;
        }
        if( !ns.equals( ggNumberField.getSpace() )) ggNumberField.setSpace( ns );

        ggNumberField.setNumber( (value == null) || !(value instanceof Number) ?
                     new Double( Double.NaN ) : (Number) value );
        return ggNumberField;

      case MapManager.Context.TYPE_BOOLEAN:
        if( ggCheckBox == null ) {
          ggCheckBox = new JCheckBox();
          ggCheckBox.setFocusable( false );
//              GUIUtil.setDeepFont( ggCheckBox, GraphicsUtil.smallGUIFont );
          AbstractWindowHandler.setDeepFont( ggCheckBox );
        } else {
          ggCheckBox.removeActionListener( this );
        }
        ggCheckBox.setSelected( (value == null) || !(value instanceof Boolean) ?
                      false : ((Boolean) value).booleanValue() );
        ggCheckBox.addActionListener( this );
        return ggCheckBox;

      case MapManager.Context.TYPE_STRING:
        if( c.typeConstraints != null && (c.typeConstraints instanceof StringItem[]) ) {
          if( ggComboBox == null ) {
            ggComboBox    = new JComboBox();
//                GUIUtil.setDeepFont( ggComboBox, GraphicsUtil.smallGUIFont );
            AbstractWindowHandler.setDeepFont( ggComboBox );
            panelComboBox  = new JPanel( new BorderLayout() );
            panelComboBox.add( ggComboBox, BorderLayout.WEST );
            ggComboBox.setFocusable( false )// because on Aqua it looks truncated
          } else {
            ggComboBox.removeActionListener( this );
            ggComboBox.removeAllItems();
          }
          StringItem[] items = (StringItem[]) c.typeConstraints;
          int idx = -1;
          for( int i = 0; i < items.length; i++ ) {
            ggComboBox.addItem( items[ i ]);
            if( items[ i ].getKey().equals( value )) idx = i;
          }
          ggComboBox.setSelectedIndex( idx );
          ggComboBox.addActionListener( this );
          return panelComboBox;
        } else {
          if( ggTextField == null ) {
            ggTextField    = new JTextField();
            panelTextField  = new JPanel( new BorderLayout() );
            panelTextField.add( ggTextField, BorderLayout.NORTH );
//                GUIUtil.setDeepFont( ggTextField, GraphicsUtil.smallGUIFont );
            AbstractWindowHandler.setDeepFont( ggTextField );
            ggTextField.addActionListener( this );
          }
          ggTextField.setText( (value == null) ? "" : value.toString() );
          return panelTextField;
        }

      case MapManager.Context.TYPE_FILE:
        Integer    type;
        PathField  ggPath;
        if( c.typeConstraints != null && (c.typeConstraints instanceof Integer) ) {
          type = (Integer) c.typeConstraints;
        } else {
          type = new Integer( PathField.TYPE_INPUTFILE );
        }
        ggPath = (PathField) mapPathFields.get( type );
        if( ggPath == null ) {
          ggPath = new PathField( type.intValue(), c.label );
          ggPath.addPathListener( this );
//              GUIUtil.setDeepFont( ggPath, GraphicsUtil.smallGUIFont );
          AbstractWindowHandler.setDeepFont( ggPath );
          mapPathFields.put( type, ggPath );
        }
        ggPath.setPath( (value == null) || !(value instanceof File) ?
                new File( "" ) : (File) value );
        return ggPath;
       
      default:
        if( ggLabel == null ) {
          ggLabel = new JLabel();
//              GUIUtil.setDeepFont( ggLabel, GraphicsUtil.smallGUIFont );
          AbstractWindowHandler.setDeepFont( ggLabel );
        }
        ggLabel.setText( keys.get( row ).toString() );
        return ggLabel;
      }
    }

    // from text fields --> editorValue instanceof String
    // from checkboxes --> editorValue instanceof Boolean
    // from comboboxes --> editorValue instanceof StringItem
    public void actionPerformed( ActionEvent e )
    {
      if( e.getSource() == ggTextField ) {
        editorValue = ggTextField.getText();
//System.err.println( "from text field : "+editorValue );
      } else if( e.getSource() == ggCheckBox ) {
        editorValue = new Boolean( ggCheckBox.isSelected() );
//System.err.println( "from checkbox : "+editorValue );
      } else if( e.getSource() == ggComboBox ) {
        Object o  = ggComboBox.getSelectedItem();
        if( (o != null) && (o instanceof StringItem) ) {
          editorValue = ((StringItem) o).getKey();
        } else {
          editorValue = null;
        }
//System.err.println( "from combo box : "+editorValue );
      }
      fireEditingStopped();
    }

    // from number fields --> editorValue instanceof Number
    public void numberChanged( NumberEvent e )
    {
      editorValue = e.getNumber();
      fireEditingStopped();
    }

    // from path fields --> editorValue instanceof File
    public void pathChanged( PathEvent e )
    {
      editorValue = e.getPath();
      fireEditingStopped();
    }
  } // class Renderer
}
TOP

Related Classes of de.sciss.meloncillo.session.SessionObjectTable$Renderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.