Package de.sciss.meloncillo.gui

Source Code of de.sciss.meloncillo.gui.MenuFactory$ActionSelectionForward

/*
*  MenuFactory.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:
*    15-Jun-04   Filter Trajectories
*    31-Jul-04   commented and cleaned up.
*    03-Aug-04   small changes due to modified SyncCompoundEdit.
*          uses MRJAdpater to set file type and creator.
*    08-Aug-04   View Menu->Surface Shows added
*    02-Feb-05  support for warning messages when loading sessions
*    15-Mar-05  added support for different receiver types
*    26-Mar-05  bugfix in remove-session-objects
*    07-Apr-05  help menu
*/

package de.sciss.meloncillo.gui;

import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.prefs.Preferences;

import javax.swing.Action;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SpringLayout;

import de.sciss.util.Flag;
import de.sciss.util.NumberSpace;

import de.sciss.app.AbstractApplication;
import de.sciss.app.AbstractCompoundEdit;
import de.sciss.common.AppWindow;
import de.sciss.common.BasicMenuFactory;
import de.sciss.common.BasicWindowHandler;
import de.sciss.common.ProcessingThread;
import de.sciss.gui.BooleanPrefsMenuAction;
import de.sciss.gui.GUIUtil;
import de.sciss.gui.MenuAction;
import de.sciss.gui.MenuCheckItem;
import de.sciss.gui.MenuGroup;
import de.sciss.gui.MenuItem;
import de.sciss.gui.MenuSeparator;
import de.sciss.gui.NumberField;
import de.sciss.gui.ProgressComponent;
import de.sciss.gui.StringItem;
import de.sciss.io.Span;
import de.sciss.meloncillo.Main;
import de.sciss.meloncillo.debug.HRIRPrepareDialog;
import de.sciss.meloncillo.edit.BasicCompoundEdit;
import de.sciss.meloncillo.edit.EditAddSessionObjects;
import de.sciss.meloncillo.edit.EditRemoveSessionObjects;
import de.sciss.meloncillo.edit.TimelineVisualEdit;
import de.sciss.meloncillo.io.AudioStake;
import de.sciss.meloncillo.io.AudioTrail;
import de.sciss.meloncillo.io.XMLRepresentation;
import de.sciss.meloncillo.lisp.JathaDiddler;
import de.sciss.meloncillo.receiver.Receiver;
import de.sciss.meloncillo.render.BounceDialog;
import de.sciss.meloncillo.render.FilterDialog;
import de.sciss.meloncillo.session.BasicSessionCollection;
import de.sciss.meloncillo.session.BasicSessionGroup;
import de.sciss.meloncillo.session.DocumentFrame;
import de.sciss.meloncillo.session.GroupableSessionObject;
import de.sciss.meloncillo.session.MutableSessionCollection;
import de.sciss.meloncillo.session.Session;
import de.sciss.meloncillo.session.SessionCollection;
import de.sciss.meloncillo.session.SessionGroup;
import de.sciss.meloncillo.transmitter.Transmitter;
import de.sciss.meloncillo.util.MapManager;
import de.sciss.meloncillo.util.PrefsUtil;

/**
<code>JMenu</code>s cannot be added to more than
*  one frame. Since on MacOS there's one
*  global menu for all the application windows
*  we need to 'duplicate' a menu prototype.
*  Synchronizing all menus is accomplished
*  by using the same action objects for all
*  menu copies. However when items are added
*  or removed, synchronization needs to be
*  performed manually. That's the point about
*  this class.
<p>
<code>JInternalFrames</code> have been removed
*  because they don't offer a constistent look-and-feel
*  on MacOS and besides the main window would
*  have to occupy most of the visible screen.
*  Unfortunately this means we cannot use
*  'floating' palettes any more.
<p>
*  There can be only one instance of <code>MenuFactory</code>
*  for the application, and that will be created by the
<code>Main</code> class.
*
@author    Hanns Holger Rutz
@version  0.74, 31-May-05
*
@see  de.sciss.meloncillo.Main#menuFactory
*
*  @todo   on operating systems that do not have a
*      global screen menu bar but attach a menubar
*      directly to a windows frame &mdash; this happens
*      on Linux and Windows &mdash; not every frame should
*      display the global menu. Small windows such
*      as the palettes should go without a menubar
*      but nevertheless a way of responding to accelerator
*      keys should be found.
*  @todo   see actionNewReceiversClass.actionPerformed.
*  @todo  some menu accelerators do not work on german keyboard.
*      it's impossible(?) to assign meta+questionmark to the help index
*      item for example.
*/
public class MenuFactory
extends BasicMenuFactory
{
  private final Session   doc;
 
  private ActionOpen    actionOpen;
 
  private Action  actionNewReceivers, actionNewTransmitters, actionNewGroup,
          actionRemoveTransmitters, actionRemoveGroups, actionFilter,
          actionBounce, actionSelectionBackwards,
          actionShowSurface, actionShowTimeline, actionShowTransport,
          actionShowMeter, actionShowRealtime;
 
  private Action  actionDebugDumpUndo,
          actionDebugDumpPrefs, actionJathaDiddler,
          actionDebugDumpListeners, actionHRIRPrepare;
//  private Action  actionDebugDumpTracks, actionDebugViewTrack,
 
  // for custom JOptionPane calls (see actionNewReceiversClass )
  private final String[]  queryOptions;

  // --- publicly accessible actions ---
  /**
   *  Action that removes all
   *  selected receivers.
   */
  public ActionRemoveSessionObject  actionRemoveReceivers;
  /**
   *  Action that advances the
   *  the timeline selection.
   */
  public ActionSelectionForward  actionSelectionForward;
  /**
   *  Action that opens the
   *  preferences frame.
   */
  public ActionPreferences    actionPreferences;
 
  /**
   *  The constructor is called only once by
   *  the <code>Main</code> class and will create a prototype
   *  main menu from which all copies are
   *  derived.
   *
   *  @param  root  application root
   *  @param  doc    session document
   */
  public MenuFactory( Main root, Session doc )
  {
    super( root );
   
//    this.root   = root;
    this.doc  = doc;
   
    final de.sciss.app.Application  app  = AbstractApplication.getApplication();
   
    queryOptions = new String[] {
      app.getResourceString( "buttonCancel" ),
      app.getResourceString( "buttonOk" )
    };

    createActions();
  }
 
  // @todo  this should eventually read the tree from an xml file
  protected void addMenuItems()
  {
    final Preferences    prefs; // = getApplication().getUserPrefs();
    MenuGroup        mg, smg;
    MenuCheckItem      mci;
    BooleanPrefsMenuAction  ba;
    int            i;
   
    // Ctrl on Mac / Ctrl+Alt on PC
//    final int myCtrl = MENU_SHORTCUT == InputEvent.CTRL_MASK ? InputEvent.CTRL_MASK | InputEvent.ALT_MASK : InputEvent.CTRL_MASK;

    // --- file menu ---
    mg  = (MenuGroup) get( "file" );
    mg.add( new MenuItem( "clearSession", getResourceString( "menuClearSession" ), KeyStroke.getKeyStroke( KeyEvent.VK_N, MENU_SHORTCUT )));
    i  = mg.indexOf( "saveAs" );
    smg = new MenuGroup( "insert", getResourceString( "menuInsert" ));
    smg.add( new MenuItem( "newReceivers", actionNewReceivers ));
    smg.add( new MenuItem( "newTransmitters", actionNewTransmitters ));
    smg.add( new MenuItem( "newGroup", actionNewGroup ));
    mg.add( smg, i + 1 );
    smg = new MenuGroup( "remove", getResourceString( "menuRemove" ));
    smg.add( new MenuItem( "removeReceivers", actionRemoveReceivers ));
    smg.add( new MenuItem( "removeTransmitters", actionRemoveTransmitters ));
    smg.add( new MenuItem( "removeGroup", actionRemoveGroups ));
    mg.add( smg, i + 2 );
    mg.add( new MenuSeparator(), i + 3 );
    mg.add( new MenuItem( "bounce", actionBounce ), i + 4 );
   
    // --- timeline menu ---
    i  = indexOf( "edit" );
    mg  = new MenuGroup( "timeline", getResourceString( "menuTimeline" ));
//    mg.add( new MenuItem( "insTimeSpan", actionInsTimeSpan ));
    mg.add( new MenuItem( "trimToSelection", getResourceString( "menuTrimToSelection" )));
    mg.add( new MenuItem( "insertSilence", getResourceString( "menuInsTimeSpan" ),
                KeyStroke.getKeyStroke( KeyEvent.VK_E, MENU_SHORTCUT + InputEvent.SHIFT_MASK )));
    mg.add( new MenuItem( "selectionForward", actionSelectionForward ));
    mg.add( new MenuItem( "selectionBackwards", actionSelectionBackwards ));
    mg.addSeparator();
    mg.add( new MenuItem( "filter", actionFilter ));
    add( mg, i + 1 );

    // --- view menu ---
    mg      = new MenuGroup( "view", getResourceString( "menuView" ));
    prefs    = getApplication().getUserPrefs().node( PrefsUtil.NODE_SHARED );
    ba      = new BooleanPrefsMenuAction( getResourceString( "menuSnapToObjects" ), KeyStroke.getKeyStroke(
                     KeyEvent.VK_LESS, MENU_SHORTCUT + KeyEvent.SHIFT_MASK ));
    mci      = new MenuCheckItem( "snapToObjects", ba );
    ba.setCheckItem( mci );
    ba.setPreferences( prefs, PrefsUtil.KEY_SNAP );
    mg.add( mci );

    smg      = new MenuGroup( "viewSurface", getResourceString( "menuViewSurface" ));
    ba      = new BooleanPrefsMenuAction( getResourceString( "menuViewRcvSense" ), null );
    mci      = new MenuCheckItem( "viewRcvSense", ba );
    ba.setCheckItem( mci );
    ba.setPreferences( prefs, PrefsUtil.KEY_VIEWRCVSENSE );
    smg.add( mci );
    ba      = new BooleanPrefsMenuAction( getResourceString( "menuViewRcvSenseEqP" ), null );
    mci      = new MenuCheckItem( "viewRcvSenseEqP", ba );
    ba.setCheckItem( mci );
    ba.setPreferences( prefs, PrefsUtil.KEY_VIEWEQPRECEIVER );
    smg.add( mci );
    ba      = new BooleanPrefsMenuAction( getResourceString( "menuViewTrnsTraj" ), null );
    mci      = new MenuCheckItem( "viewTrnsTraj", ba );
    ba.setCheckItem( mci );
    ba.setPreferences( prefs, PrefsUtil.KEY_VIEWTRNSTRAJ );
    smg.add( mci );
    ba      = new BooleanPrefsMenuAction( getResourceString( "menuViewUserImages" ), null );
    mci      = new MenuCheckItem( "viewUserImages", ba );
    ba.setCheckItem( mci );
    ba.setPreferences( prefs, PrefsUtil.KEY_VIEWUSERIMAGES );
    smg.add( mci );
    ba      = new BooleanPrefsMenuAction( getResourceString( "menuViewRulers" ), null );
    mci      = new MenuCheckItem( "viewRulers", ba );
    ba.setCheckItem( mci );
    ba.setPreferences( prefs, PrefsUtil.KEY_VIEWRULERS );
    smg.add( mci );
    mg.add( smg );
    add( mg, i + 2 );

    // --- extras menu ---
    mg   = new MenuGroup( "extras", getResourceString( "menuExtras" ));
    mg.add( new MenuItem( "jathaDiddler", actionJathaDiddler ));
    mg.add( new MenuItem( "hrirPrepare", actionHRIRPrepare ));
    add( mg, i + 3 );
   
    // --- window menu ---
    mg  = (MenuGroup) get( "window" );
    mg.add( new MenuItem( "showSurface", actionShowSurface ), 0 );
    mg.add( new MenuItem( "showTimeline", actionShowTimeline ), 1 );
    mg.add( new MenuSeparator(), 2 );
    mg.add( new MenuItem( "showTransport", actionShowTransport ), 3 );
    mg.add( new MenuItem( "observer", new ActionObserver( getResourceString( "paletteObserver" ), KeyStroke.getKeyStroke( KeyEvent.VK_NUMPAD3, MENU_SHORTCUT ))), 4 );
    mg.add( new MenuItem( "showMeter", actionShowMeter ), 5 );
    mg.add( new MenuItem( "showRealtime", actionShowRealtime ), 6 );

    // --- debug menu ---
    mg   = new MenuGroup( "debug", "Debug" );
    mg.add( new MenuItem( "debugDumpUndo", actionDebugDumpUndo ));
//    mg.add( new MenuItem( "debugDumpTracks", actionDebugDumpTracks ));
//    mg.add( new MenuItem( "debugViewTrack", actionDebugViewTrack ));
    mg.add( new MenuItem( "debugDumpPrefs", actionDebugDumpPrefs ));
//    mg.add( new MenuItem( "debugDumpRealtime", actionDebugDumpRealtime ));
    mg.add( new MenuItem( "debugDumpListeners", actionDebugDumpListeners ));
    i  = indexOf( "help" );
    add( mg, i );

  }

  private void createActions()
  {
    final de.sciss.app.Application  app = AbstractApplication.getApplication();
   
    // --- file menu ---
    actionOpen    = new ActionOpenapp.getResourceString( "menuOpen" ),
                        KeyStroke.getKeyStroke( KeyEvent.VK_O, MENU_SHORTCUT ));
    actionNewReceivers = new ActionNewReceiversapp.getResourceString( "menuNewReceivers" ),
              KeyStroke.getKeyStroke( KeyEvent.VK_N, MENU_SHORTCUT + KeyEvent.ALT_MASK ));
    actionNewTransmitters = new ActionNewTransmitters( app.getResourceString( "menuNewTransmitters" ),
              KeyStroke.getKeyStroke( KeyEvent.VK_N, MENU_SHORTCUT + KeyEvent.SHIFT_MASK ));
    actionNewGroup  = new ActionNewGroup( app.getResourceString( "menuNewGroup" ), null );
    actionRemoveReceivers = new ActionRemoveSessionObject( app.getResourceString( "menuRemoveReceivers" ), null,
      doc.getMutableReceivers(), doc.getMutableSelectedReceivers(), doc.getSelectedReceivers() );
    actionRemoveTransmitters = new ActionRemoveSessionObject( app.getResourceString( "menuRemoveTransmitters" ), null,
      doc.getMutableTracks(), doc.getMutableSelectedTracks(), doc.getSelectedTransmitters() );
    actionRemoveGroups  = new ActionRemoveSessionObject( app.getResourceString( "menuRemoveGroups" ), null,
      doc.getMutableGroups(), doc.getMutableSelectedGroups(), doc.getSelectedGroups() );

    actionBounce  = new ActionBounce( app.getResourceString( "menuBounce" ),
                        KeyStroke.getKeyStroke( KeyEvent.VK_R, MENU_SHORTCUT ));
//    actionQuit    = new ActionQuit(  app.getResourceString( "menuQuit" ),
//                        KeyStroke.getKeyStroke( KeyEvent.VK_Q, MENU_SHORTCUT ));

    // --- timeline menu ---
//    actionInsTimeSpan   = new ActionInsTimeSpan( app.getResourceString( "menuInsTimeSpan" ),
//                  KeyStroke.getKeyStroke( KeyEvent.VK_E, MENU_SHORTCUT + KeyEvent.SHIFT_MASK ));
    actionSelectionForward = new ActionSelectionForward( app.getResourceString( "menuSelectionForward" ),
                        KeyStroke.getKeyStroke( KeyEvent.VK_CLOSE_BRACKET, MENU_SHORTCUT + KeyEvent.SHIFT_MASK ));
    actionSelectionBackwards = new ActionSelectionBackwards( app.getResourceString( "menuSelectionBackwards" ),
                        KeyStroke.getKeyStroke( KeyEvent.VK_OPEN_BRACKET, MENU_SHORTCUT + KeyEvent.SHIFT_MASK ));
    actionFilter = new ActionFilter( app.getResourceString( "menuFilter" ),
                    KeyStroke.getKeyStroke( KeyEvent.VK_F, MENU_SHORTCUT ));

    // --- view menu ---

    // --- window menu ---
    actionShowSurface    = new ActionShowWindow( app.getResourceString( "frameSurface" ), KeyStroke.getKeyStroke(
                    KeyEvent.VK_MULTIPLY, MENU_SHORTCUT ), Main.COMP_SURFACE );
    actionShowTimeline    = new ActionShowWindow( app.getResourceString( "frameTimeline" ), KeyStroke.getKeyStroke(
                    KeyEvent.VK_EQUALS, MENU_SHORTCUT ), Main.COMP_TIMELINE );
    actionShowTransport    = new ActionShowWindow( app.getResourceString( "paletteTransport" ), KeyStroke.getKeyStroke(
                    KeyEvent.VK_NUMPAD1, MENU_SHORTCUT ), Main.COMP_TRANSPORT );
//    actionShowObserver    = new ActionShowWindow( app.getResourceString( "paletteObserver" ), KeyStroke.getKeyStroke(
//                    KeyEvent.VK_NUMPAD3, MENU_SHORTCUT ), Main.COMP_OBSERVER );
    actionShowMeter      = new ActionShowWindow( app.getResourceString( "frameMeter" ), KeyStroke.getKeyStroke(
                    KeyEvent.VK_NUMPAD4, MENU_SHORTCUT ), Main.COMP_METER );
    actionShowRealtime    = new ActionShowWindow( app.getResourceString( "frameRealtime" ), KeyStroke.getKeyStroke(
                    KeyEvent.VK_NUMPAD2, MENU_SHORTCUT ), Main.COMP_REALTIME );

    // --- extras menu ---
    actionJathaDiddler    = JathaDiddler.getMenuAction();
    actionHRIRPrepare    = HRIRPrepareDialog.getMenuAction( doc );

    // --- debug menu ---
    actionDebugDumpUndo    = doc.getUndoManager().getDebugDumpAction();
    actionDebugDumpPrefs  = PrefsUtil.getDebugDumpAction( doc );
//    actionDebugDumpTracks   = DebugTrackEditor.getDebugDumpAction( doc );
//    actionDebugViewTrack  = DebugTrackEditor.getDebugViewAction( doc );
    actionDebugDumpListeners= new ActionDebugDumpListeners();
  }

  public void showPreferences()
  {
    PrefsFrame prefsFrame = (PrefsFrame) getApplication().getComponent( Main.COMP_PREFS );
 
    if( prefsFrame == null ) {
      prefsFrame = new PrefsFrame( doc );
    }
    prefsFrame.setVisible( true );
    prefsFrame.toFront();
  }
 
  protected Action getOpenAction()
  {
    return actionOpen;
  }

  protected ActionOpenRecent createOpenRecentAction( String name, File path )
  {
    return new ActionOpenRecent( name, path );
  }

  public void openDocument( File f )
  {
    actionOpen.perform( f );
  }

//  /**
//   *  Checks if there are unsaved changes to
//   *  the session. If so, displays a confirmation
//   *  dialog. Invokes Save/Save As depending
//   *  on user selection.
//   * 
//   *  @param  parentComponent the component associated with
//   *              the proposed action, e.g. root
//   *  @param  actionName    name of the action that
//   *              threatens the session
//   *  @return          - true if the action should proceed,
//   *              - false if the action should be aborted
//   */
//  public boolean confirmUnsaved( Component parentComponent, String actionName )
//  {
//    if( !doc.isDirty() ) return true;
//   
//    final de.sciss.app.Application  app    = AbstractApplication.getApplication();
//    final String[]          options  = { app.getResourceString( "buttonSave" ),
//                          app.getResourceString( "buttonCancel" ),
//                          app.getResourceString( "buttonDontSave" ) };
//    int                choice;
//    ProcessingThread        proc;
//    File              f;
//   
//    choice = JOptionPane.showOptionDialog( parentComponent, app.getResourceString( "optionDlgUnsaved" ), actionName,
//                         JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null,
//                         options, options[0] );
//    switch( choice ) {
//    case JOptionPane.CLOSED_OPTION:
//    case 1:
//      return false;
//     
//    case 2:
//      return true;
//     
//    case 0:
//      f = (File) doc.getMap().getValue( Session.MAP_KEY_PATH );
//      if( f == null ) {
//        f = actionSaveAs.queryFile();
//      }
//      if( f != null ) {
//        proc = actionSave.perform( f );
//        if( proc != null ) {
//          return proc.sync();
//        }
//      }
//      return false;
//     
//    default:
//      assert false : choice;
//      return false;
//    }
//  }

// ---------------- Action objects for file (session) operations ----------------

  // action for the Insert-New-Receivers menu item
  private class ActionNewReceivers
  extends MenuAction
  {
    private Number    num    = new Integer( 1 );
    private StringItem  type  = null;

    private ActionNewReceivers( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    // ask the user for a number of receivers
    // to insert. if dialog is confirmed
    // create those receivers and add them to
    // the session.
    //
    // @synchronization waitExclusive on DOOR_RCV + DOOR_GRP
    // @todo  this was copied from Surface.createReceiver(); there should
    //      be a central function that creates Receivers and not multiple
    //      versions!
    // @todo  to improve performance, doc.receiverCollectio.add should
    //      be called only once with the whole new collection; implement
    //      a new related method createUniqueName( Collection theseNot );
    // @todo  should select new receives
    public void actionPerformed( ActionEvent e )
    {
      int        numi, result;
      Receiver    rcv;
      List      collNewRcv, coll2;
      Point2D      anchor;
      double      d1;
      Class      c;

      final List        collTypes  = Main.getReceiverTypes();
      final JPanel      msgPane    = new JPanel( new SpringLayout() );
      final NumberField    ggNum    = new NumberField( NumberSpace.createIntSpace( 1, 0x10000 ));
      final JComboBox      ggType    = new JComboBox();

      for( int i = 0; i < collTypes.size(); i++ ) {
        ggType.addItem( collTypes.get( i ));
      }
     
      ggNum.setNumber( num );
      if( type != null ) ggType.setSelectedItem( type );

      msgPane.add( ggNum );
      msgPane.add( ggType );
      GUIUtil.makeCompactSpringGrid( msgPane, 1, 2, 4, 2, 4, 2 )// #row #col initx inity padx pady
//      HelpGlassPane.setHelp( msgPane, getValue( NAME ).toString() );  // EEE
   
      result = JOptionPane.showOptionDialog( null, msgPane,
        AbstractApplication.getApplication().getResourceString( "inputDlgNewReceivers" ),
        JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
        null, queryOptions, queryOptions[ 1 ]);
       
      if( result != 1 ) return;

      num    = ggNum.getNumber();
      type  = (StringItem) ggType.getSelectedItem();
      numi  = num.intValue();
     
      try {
        doc.bird.waitExclusive( Session.DOOR_RCV | Session.DOOR_GRP );
        // we get a list of known receivers from the main class
        // and create a new instance of the first receiver class
        // in the list; in the future when there are more types
        // apart from SigmaReceiver, we could display a selection
        // dialog or the like...
        c    = Class.forName( type.getKey() );
        collNewRcv  = new ArrayList();
        coll2  = doc.getReceivers().getAll();
        for( int i = 0; i < numi; i++ ) {
          d1  = ((double) i / (double) numi - 0.25) * Math.PI * -2;
//          anchor = new Point2D.Double( 0.25 * (2.0 + Math.cos( d1 )), 0.25 * (2.0 + Math.sin( d1 )));
          anchor = new Point2D.Double( 0.5 * + Math.cos( d1 ), 0.5 * Math.sin( d1 ));
          rcv = (Receiver) c.newInstance();
          rcv.setAnchor( anchor );
//          rcv.setSize( new Dimension2DDouble( 0.5, 0.5 ));
          rcv.setName( BasicSessionCollection.createUniqueName( Session.SO_NAME_PTRN,
            new Object[] { new Integer( 1 ), Session.RCV_NAME_PREFIX, Session.RCV_NAME_SUFFIX },
            coll2 ));
          doc.getReceivers().getMap().copyContexts( this, MapManager.Context.FLAG_DYNAMIC,
                             MapManager.Context.NONE_EXCLUSIVE, rcv.getMap() );
          collNewRcv.add( rcv );
          coll2.add( rcv );
        }
        final BasicCompoundEdit edit = new BasicCompoundEdit( getValue( NAME ).toString() );
        if( !doc.getSelectedGroups().isEmpty() ) {
          final List selectedGroups = doc.getSelectedGroups().getAll();
          for( int i = 0; i < collNewRcv.size(); i++ ) {
            final GroupableSessionObject so = (GroupableSessionObject) collNewRcv.get( i );
            edit.addPerform( new EditAddSessionObjects( this, so.getGroups(), selectedGroups ));
          }
        }
        edit.addPerform( new EditAddSessionObjects( this, doc.getMutableReceivers(), collNewRcv ));
        edit.addPerform( new EditAddSessionObjects( this, doc.getMutableSelectedReceivers(), collNewRcv ));
//        for( int i = 0; i < doc.getSelectedGroups().size(); i++ ) {
//          group  = (SessionGroup) doc.getSelectedGroups().get( i );
//          edit.addPerform( new EditAddSessionObjects( this, group.getReceivers(), coll ));
//        }
        edit.perform();
        edit.end();
        doc.getUndoManager().addEdit( edit );
      }
      catch( InstantiationException e1 ) {
        System.err.println( e1.getLocalizedMessage() );
      }
      catch( IllegalAccessException e2 ) {
        System.err.println( e2.getLocalizedMessage() );
      }
      catch( LinkageError e3 ) {
        System.err.println( e3.getLocalizedMessage() );
      }
      catch( ClassNotFoundException e4 ) {
        System.err.println( e4.getLocalizedMessage() );
      }
      finally {
        doc.bird.releaseExclusive( Session.DOOR_RCV | Session.DOOR_GRP );
      }
    }
  }
 
  // action for the Insert-New-Group menu item
  private class ActionNewGroup
  extends MenuAction
  {
    private ActionNewGroup( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    // ask the user for a number of group name.
    // if dialog is confirmed creates a new group
    // with the currently selected transmitters + receivers
    //
    // @synchronization waitExclusive on DOOR_RCV + DOOR_TRNS, DOOR_GRP
    public void actionPerformed( ActionEvent e )
    {
      String          name;
      boolean          b1;
      int            result;
      List          collSO;
      SessionGroup      group;
      AbstractCompoundEdit  ce;

      try {
        doc.bird.waitShared( Session.DOOR_TRNS | Session.DOOR_RCV );
        b1 = doc.getSelectedReceivers().isEmpty() && doc.getSelectedTransmitters().isEmpty();
      } finally {
        doc.bird.releaseShared( Session.DOOR_TRNS | Session.DOOR_RCV );
      }
      if( b1 ) {  // ask if we should create an empty group
        result = JOptionPane.showConfirmDialog( null,
          AbstractApplication.getApplication().getResourceString( "warnNoObjectsSelected" ),
          getValue( Action.NAME ).toString(), JOptionPane.YES_NO_OPTION );
         
        if( result != JOptionPane.YES_OPTION ) return;
      }

      try {
        doc.bird.waitShared( Session.DOOR_GRP );
        name = BasicSessionCollection.createUniqueName( Session.SO_NAME_PTRN,
          new Object[] { new Integer( 1 ), Session.GRP_NAME_PREFIX, Session.GRP_NAME_SUFFIX },
          doc.getGroups().getAll() );
      } finally {
        doc.bird.releaseShared( Session.DOOR_GRP );
      }
      name = JOptionPane.showInputDialog( null, AbstractApplication.getApplication().getResourceString(
        "inputDlgNewGroup" ), name );
       
      if( name == null ) return;
     
      ce = new BasicCompoundEdit( getValue( NAME ).toString() );
     
      try {
        doc.bird.waitExclusive( Session.DOOR_GRP );
        group  = (SessionGroup) doc.getGroups().findByName( name );
        b1    = group == null;
        if( !b1 ) {
          result = JOptionPane.showConfirmDialog( null,
            AbstractApplication.getApplication().getResourceString(
            "optionDlgOverwriteGroup" ), getValue( Action.NAME ).toString(),
            JOptionPane.YES_NO_OPTION );
           
          if( result != JOptionPane.YES_OPTION ) return;
        } else {
          group = new BasicSessionGroup( doc );
          group.setName( name );
          doc.getGroups().getMap().copyContexts( this, MapManager.Context.FLAG_DYNAMIC,
                            MapManager.Context.NONE_EXCLUSIVE, group.getMap() );
        }
        if( !doc.bird.attemptShared( Session.DOOR_TRNS | Session.DOOR_RCV, 250 )) return;
        try {
          collSO    = doc.getSelectedReceivers().getAll();
          final List collGroup = Collections.singletonList( group );
          for( int i = 0; i < collSO.size(); i++ ) {
            final GroupableSessionObject so = (GroupableSessionObject) collSO.get( i );
            ce.addPerform( new EditAddSessionObjects( this, so.getGroups(), collGroup ));
          }
//          ce.addEdit( new EditAddSessionObjects( this, group.getReceivers(), collSO ));
          collSO    = doc.getSelectedTransmitters().getAll();
          for( int i = 0; i < collSO.size(); i++ ) {
            final GroupableSessionObject so = (GroupableSessionObject) collSO.get( i );
            ce.addPerform( new EditAddSessionObjects( this, so.getGroups(), collGroup ));
          }
//          ce.addEdit( new EditAddSessionObjects( this, group.getTransmitters(), collSO ));

          if( b1 ) {
            ce.addPerform( new EditAddSessionObjects( this, doc.getMutableGroups(), collGroup ));
          }
          ce.perform();
          ce.end();
          doc.getUndoManager().addEdit( ce );
         
        } finally {
          doc.bird.releaseShared( Session.DOOR_TRNS | Session.DOOR_RCV );
        }
      } finally {
        doc.bird.releaseExclusive( Session.DOOR_GRP );
      }
    }
  }
 
  // action for the Open-Session menu item
  private class ActionOpen
  extends MenuAction
  implements ProcessingThread.Listener
  {
    private String text;
 
    private ActionOpen( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
     
      this.text = text;
    }
   
    /*
     *  Open a document. If the current document
     *  contains unsaved changes, the user is prompted
     *  to confirm. A file chooser will pop up for
     *  the user to select the session to open.
     */
    public void actionPerformed( ActionEvent e )
    {
      perform();
    }
   
    protected void perform()
    {
      final Flag        confirmed  = new Flag( false );
      final DocumentFrame    frame    = (DocumentFrame) getApplication().getComponent( Main.COMP_MAIN );
      final ProcessingThread  pt      = frame.confirmUnsaved( text, confirmed );
      if( pt == null ) {
        if( !confirmed.isSet() ) return;
        queryAndPerform();
      } else {
        pt.addListener( new ProcessingThread.Listener() {
          public void processStarted( ProcessingThread.Event e ) {}
          public void processStopped( ProcessingThread.Event e ) {
            if( e.getProcessingThread().getReturnCode() == ProgressComponent.DONE ) {
              queryAndPerform();
            }
          }
        });
        pt.start();
      }
    }
   
    private void queryAndPerform()
    {
      File f = queryFile();
      if( f != null ) {
        perform( f );
      }
    }
   
    private void perform( File f )
    {
      final ProcessingThread pt = doc.initiateLoad( f );
      pt.addListener( this );
      pt.start();
    }

    private File queryFile()
    {
      FileDialog  fDlg;
      String    strFile, strDir;
//      Frame    frame = (Frame) getApplication().getComponent( Main.COMP_MAIN );
      final Frame  frame  = new Frame();

      fDlg  = new FileDialog( frame, AbstractApplication.getApplication().getResourceString(
        "fileDlgOpen" ), FileDialog.LOAD );
      fDlg.setFilenameFilter( doc );
      // fDlg.setDirectory();
      // fDlg.setFile();
      fDlg.show();
      frame.dispose();
      strDir  = fDlg.getDirectory();
      strFile  = fDlg.getFile();
     
      if( strFile == null ) return null;   // means the dialog was cancelled

      return( new File( strDir, strFile ));
    }
   
    public void processStopped( ProcessingThread.Event e )
    {
      final ProcessingThread pt = e.getProcessingThread();
      if( pt.getReturnCode() == ProgressComponent.DONE ) {
        final Map options = (Map) pt.getClientArg( "options" );
        addRecent( (File) options.get( "file" ));
// EEE
//        if( AbstractApplication.getApplication().getUserPrefs().getBoolean(
//          PrefsUtil.KEY_RECALLFRAMES, false )) {
//          BasicFrame.restoreAllFromPrefs();
//        }
        final Object warn = options.get( XMLRepresentation.KEY_WARNING );
        if( warn != null ) {
//          final MainFrame mf = (MainFrame) getApplication().getComponent( Main.COMP_MAIN );
          JOptionPane.showMessageDialog( null, warn, getValue( Action.NAME ).toString(),
                           JOptionPane.WARNING_MESSAGE );
        }
        ((MainFrame) AbstractApplication.getApplication().getComponent( Main.COMP_MAIN )).updateTitle();
      }
    }
   
    public void processStarted( ProcessingThread.Event e ) { /* nada */ }
  }
 
  // action for Bounce-to-Disk menu item
  private class ActionBounce
  extends MenuAction
  {
    private ActionBounce( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }
   
    /**
     *  Opens the bounce-to-disk dialog
     */
    public void actionPerformed( ActionEvent e )
    {
      AppWindow bounceFrame = (AppWindow) getApplication().getComponent( Main.COMP_BOUNCE );
   
      if( bounceFrame == null ) {
        final Main root = (Main) AbstractApplication.getApplication();
        bounceFrame = new BounceDialog( root, doc );
      }
      bounceFrame.setVisible( true );
      bounceFrame.toFront();
    }
  }

//  // action for Application-Quit menu item
//  private class ActionQuit
//  extends MenuAction
//  {
//    private ActionQuit( String text, KeyStroke shortcut )
//    {
//      super( text, shortcut );
//    }
//   
//    public void actionPerformed( ActionEvent e )
//    {
//      getApplication().quit();
//    }
//  }
 
// ---------------- Action objects for edit operations ----------------
 
//  // action for Edit-Cut menu item
//  private class ActionCut
//  extends MenuAction
//  {
//    private ActionCut( String text, KeyStroke shortcut )
//    {
//      super( text, shortcut );
//    }
//
//    /**
//     *  Tries to find the current active window
//     *  and - if this window implements the
//     *  <code>EditMenuListener</code> interface - calls
//     *  <code>editCut</code> on that window.
//     *
//     *  @see  EditMenuListener#editCut( ActionEvent )
//     */
//    public void actionPerformed( ActionEvent e )
//    {
//      JFrame frame = fuckINeedTheWindow( e );
//      if( frame == null || !(frame instanceof EditMenuListener) ) return;
//     
//      ((EditMenuListener) frame).editCut( e );
//    }
//  }
// EEE
 
//  // action for Edit-Copy menu item
//  private class ActionCopy
//  extends MenuAction
//  {
//    private ActionCopy( String text, KeyStroke shortcut )
//    {
//      super( text, shortcut );
//    }
//
//    /**
//     *  Tries to find the current active window
//     *  and - if this window implements the
//     *  <code>EditMenuListener</code> interface - calls
//     *  <code>editCopy</code> on that window.
//     *
//     *  @see  EditMenuListener#editCopy( ActionEvent )
//     */
//    public void actionPerformed( ActionEvent e )
//    {
//      JFrame frame = fuckINeedTheWindow( e );
//      if( frame == null || !(frame instanceof EditMenuListener) ) return;
//     
//      ((EditMenuListener) frame).editCopy( e );
//    }
//  }
// EEE
 
//  // action for Edit-Paste menu item
//  private class ActionPaste
//  extends MenuAction
//  {
//    private ActionPaste( String text, KeyStroke shortcut )
//    {
//      super( text, shortcut );
//    }
//
//    /**
//     *  Tries to find the current active window
//     *  and - if this window implements the
//     *  <code>EditMenuListener</code> interface - calls
//     *  <code>editPaste</code> on that window.
//     *
//     *  @see  EditMenuListener#editPaste( ActionEvent )
//     */
//    public void actionPerformed( ActionEvent e )
//    {
//      JFrame frame = fuckINeedTheWindow( e );
//      if( frame == null || !(frame instanceof EditMenuListener) ) return;
//     
//      ((EditMenuListener) frame).editPaste( e );
//    }
//  }
// EEE
 
//  // action for Edit-Clear/Delete menu item
//  private class ActionClear
//  extends MenuAction
//  {
//    private ActionClear( String text, KeyStroke shortcut )
//    {
//      super( text, shortcut );
//    }
//
//    /**
//     *  Tries to find the current active window
//     *  and - if this window implements the
//     *  <code>EditMenuListener</code> interface - calls
//     *  <code>editClear</code> on that window.
//     *
//     *  @see  EditMenuListener#editClear( ActionEvent )
//     */
//    public void actionPerformed( ActionEvent e )
//    {
//      JFrame frame = fuckINeedTheWindow( e );
//      if( frame == null || !(frame instanceof EditMenuListener) ) return;
//     
//      ((EditMenuListener) frame).editClear( e );
//    }
//  }
// EEE
 
//  // action for Edit-Select-All menu item
//  private class ActionSelectAll
//  extends MenuAction
//  {
//    private ActionSelectAll( String text, KeyStroke shortcut )
//    {
//      super( text, shortcut );
//    }
//
//    /**
//     *  Tries to find the current active window
//     *  and - if this window implements the
//     *  <code>EditMenuListener</code> interface - calls
//     *  <code>editSelectAll</code> on that window.
//     *
//     *  @see  EditMenuListener#editSelectAll( ActionEvent )
//     */
//    public void actionPerformed( ActionEvent e )
//    {
//      JFrame frame = fuckINeedTheWindow( e );
//      if( frame == null || !(frame instanceof EditMenuListener) ) return;
//     
//      ((EditMenuListener) frame).editSelectAll( e );
//    }
//  }
// EEE
 
// ---------------- Action objects for surface operations ----------------
 
// ---------------- Action objects for timeline operations ----------------

  // action for Insert-New-Transmitters menu item
  private class ActionNewTransmitters
  extends MenuAction
  implements ProcessingThread.Client
  {
    private int    defaultValue = 1;
    private String  text;
 
    private ActionNewTransmitters( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
     
      this.text = text;
    }

    /**
     *  Queries the number of transmitters to
     *  create from the user and creates a
     *  new ProcessingThread which will perform
     *  the insertion (and creation of corresponding
     *  trajectory files).
     */
    public void actionPerformed( ActionEvent e )
    {
      String      result;
      int        num = 0;
   
      result  = JOptionPane.showInputDialog( null,
        AbstractApplication.getApplication().getResourceString( "inputDlgInsNewTransmitters" ),
        String.valueOf( defaultValue ));
       
      if( result == null ) return;
      try {
        num = Integer.parseInt( result );
      }
      catch( NumberFormatException e1 ) {
        System.err.println( e1.getLocalizedMessage() );
        return;
      }
     
      if( num < 1 ) return;
      defaultValue = num;

      final List        collTypes    = Main.getTransmitterTypes();
      final List        collMap;
      final Span        span;
      final List        collNewTrns    = new ArrayList( num );
      final List        collAllTrns    = doc.getTransmitters().getAll();
      final BasicCompoundEdit  edit;
      Class    c;
      Transmitter  trns;
      String    s;
      Map      map;
     
      edit = new BasicCompoundEdit( getValue( NAME ).toString() );
     
      collMap = new ArrayList( num );
      for( int i = 0; i < num; i++ ) {
        collMap.add( collTypes.get( 0 ));
      }

      span = new Span( 0, doc.timeline.getLength() );
      try {
        for( int i = 0; i < num; i++ ) {
          map    = (Map) collMap.get( i );
          s    = (String) map.get( Main.KEY_CLASSNAME );
          if( s == null ) continue;

          c    = Class.forName( s );
          trns  = (Transmitter) c.newInstance();
          trns.setName( BasicSessionCollection.createUniqueName( Session.SO_NAME_PTRN,
            new Object[] { new Integer( 1 ), Session.TRNS_NAME_PREFIX, Session.TRNS_NAME_SUFFIX },
            collAllTrns ));
          doc.getTransmitters().getMap().copyContexts( this, MapManager.Context.FLAG_DYNAMIC,
                              MapManager.Context.NONE_EXCLUSIVE, trns.getMap() );
          collNewTrns.add( trns );
          collAllTrns.add( trns );
         
//          trns.getAudioTrail().editBegin( edit );
        }
      }
      catch( InstantiationException e1 ) {
        BasicWindowHandler.showErrorDialog( null, e1, getValue( NAME ).toString() );
      }
      catch( IllegalAccessException e1 ) {
        BasicWindowHandler.showErrorDialog( null, e1, getValue( NAME ).toString() );
      }
      catch( LinkageError e1 ) {
        BasicWindowHandler.showErrorDialog( null, e1, getValue( NAME ).toString() );
      }
      catch( ClassNotFoundException e1 ) {
        BasicWindowHandler.showErrorDialog( null, e1, getValue( NAME ).toString() );
      }

      final Main root = (Main) AbstractApplication.getApplication();
//      new ProcessingThread( this, root, root, doc, text, coll, Session.DOOR_TIMETRNSMTE | Session.DOOR_GRP );
      final ProcessingThread pt;
      pt = new ProcessingThread( this, root, text );
      pt.putClientArg( "trns", collNewTrns );
      pt.putClientArg( "span", span );
      pt.putClientArg( "edit", edit );
      pt.start();
    }

    /**
     *  @synchronization  waitExclusive on DOOR_TIMETRNSMTE + DOOR_GRP
     */
    public int processRun( ProcessingThread context )
    throws IOException
    {
      final float[][]      buf      = new float[ 2 ][ 4096 ];
      final BasicCompoundEdit  edit    = (BasicCompoundEdit) context.getClientArg( "edit" );
      final List        collNewTrns  = (List) context.getClientArg( "trns" );
      final int        num      = collNewTrns.size();
      Transmitter        trns;
      AudioTrail        at;
      long          progress  = 0;
      double          d1;
      Span          span    = (Span) context.getClientArg( "span" );
//      TrackSpan        ts;
      float          f1, f2;
      AudioStake        as;
      int            chunkLen;
     
      if( span.isEmpty() ) return DONE;
     
      try {
        for( int i = 0; i < num; i++ ) {
          d1    = ((double) i / (double) num - 0.25) * Math.PI * -2;
//          f1    = (float) (0.25 * (2.0 + Math.cos( d1 )));
//          f2    = (float) (0.25 * (2.0 + Math.sin( d1 )));
          f1    = (float) (0.5 * Math.cos( d1 ));
          f2    = (float) (0.5 * Math.sin( d1 ));
          for( int j = 0; j < 4096; j++ ) {
            buf[0][j] = f1;
            buf[1][j] = f2;
          }
          trns  = (Transmitter) collNewTrns.get( i );
          at    = trns.getAudioTrail();
//          ts    = at.beginInsert( span, edit );
          as    = at.alloc( span );
          for( long framesWritten = 0, frames = span.getLength(); framesWritten < frames; ) {
            chunkLen = (int) Math.min( 4096, frames - framesWritten );
//            at.continueWrite( ts, buf, 0, j );
            as.writeFrames( buf, 0, new Span( span.start + framesWritten, span.start + framesWritten + chunkLen ));
            framesWritten += chunkLen;
          }
//          stakes.add( as );
          at.editBegin( edit );
          try {
            at.editAdd( this, as, edit ); // EEE should undy the stake alloc!!!
          } finally {
            at.editEnd( edit );
          }
//          at.finishWrite( ts, edit );
          progress++;
          context.setProgression( (float) progress / (float) num );
        }

        return DONE;
      }
      catch( IOException e1 ) {
        context.setException( e1 );
        return FAILED;
      }
    } // run()
   
    public void processFinished( ProcessingThread context )
    {
      final BasicCompoundEdit edit = (BasicCompoundEdit) context.getClientArg( "edit" );

      if( context.getReturnCode() == ProgressComponent.DONE ) {
        final List collNewTrns  = (List) context.getClientArg( "trns" );
//        final List stakes    = (List) context.getClientArg( "stakes" );
//        for( int i = 0; i < collNewTrns.size(); i++ ) {
//          final AudioTrail at = ((Transmitter) collNewTrns.get( i )).getAudioTrail();
//          .editAdd(
//              this, (AudioStake) stakes.get( i ), edit );
//        }
        final List selectedGroups = doc.getSelectedGroups().getAll();
        if( !selectedGroups.isEmpty() ) {
          for( int i = 0; i < collNewTrns.size(); i++ ) {
            final GroupableSessionObject so = (GroupableSessionObject) collNewTrns.get( i );
            edit.addPerform( new EditAddSessionObjects( this, so.getGroups(), selectedGroups ));
          }
        }
        edit.addPerform( new EditAddSessionObjects( this, doc.getMutableTracks(), collNewTrns ));
        edit.addPerform( new EditAddSessionObjects( this, doc.getMutableSelectedTracks(), collNewTrns ));
//        for( int i = 0; i < doc.getSelectedGroups().size(); i++ ) {
//          final SessionGroup group = (SessionGroup) doc.getSelectedGroups().get( i );
//          edit.addPerform( new EditAddSessionObjects( this, group.getTransmitters(), collNewTrns ));
//        }
        edit.perform();
        edit.end();
        doc.getUndoManager().addEdit( edit );
      } else {
        // EEE should undo the stake alloc!!!
        edit.cancel();
      }
    }
   
    public void processCancel( ProcessingThread context ) {}
  }
 
  // action for Remove-Selected-Transmitters/Groups menu item
  public class ActionRemoveSessionObject
  extends MenuAction
  {
    private final MutableSessionCollection  scAll;
    private final MutableSessionCollection  scSelAll;
    private final SessionCollection      scSel;
//    private final SessionCollection      scGroups;
 
    private ActionRemoveSessionObject( String text, KeyStroke shortcut,
                       MutableSessionCollection scAll,
                       MutableSessionCollection scSelAll,
                       SessionCollection scSel )
    {
      super( text, shortcut );
     
//      this.doors    = doors;
      this.scAll    = scAll;
      this.scSelAll  = scSelAll;
      this.scSel    = scSel;
//      this.scGroups  = scGroups;
    }

    /**
     *  @synchronization  waitExclusive on doors
     */
    public void actionPerformed( ActionEvent e )
    {
      perform();
    }
   
    /**
     *  Removes all current selected receivers
     *  from the session.
     */
    public void perform()
    {
      List          collSelection;
      AbstractCompoundEdit  edit;
//      SessionGroup      g;

//      try {
//        doc.bird.waitExclusive( doors );
        collSelection    = scSel.getAll();
        edit        = new BasicCompoundEdit( getValue( NAME ).toString() );
//        if( scGroups != null ) {
//          for( int i = 0; i < scGroups.size(); i++ ) {
//            g      = (SessionGroup) scGroups.get( i );
//            collInGroup  = g.getTransmitters().getAll();
//            collInGroup.retainAll( collSelection );
//            if( !collInGroup.isEmpty() ) {
//              edit.addPerform( new EditRemoveSessionObjects( this, doc, g.getTransmitters(), collInGroup,
//                                                             doors | Session.DOOR_GRP ));
//            } else {
//              collInGroup  = g.getReceivers().getAll();
//              collInGroup.retainAll( collSelection );
//              if( !collInGroup.isEmpty() ) {
//                edit.addPerform( new EditRemoveSessionObjects( this, doc, g.getReceivers(), collInGroup,
//                                                               doors | Session.DOOR_GRP ));
////              } else {
////                collInGroup  = g.groups.getAll().retainAll( collSelection );
////                if( !collInGroup.isEmpty() ) {
////                  edit.addEdit( new EditRemoveSessionObjects( this, doc, g.transmitters, collInGroup,
////                                        doors | Session.DOOR_GRP ));
////                }
//              }
//            }
//          }
//        }
        edit.addPerform( new EditRemoveSessionObjects( this, scSelAll, collSelection ));
        edit.addPerform( new EditRemoveSessionObjects( this, scAll, collSelection ));
        edit.perform();
        edit.end();
        doc.getUndoManager().addEdit( edit );
//      }
//      finally {
//        doc.bird.releaseExclusive( doors );
//      }
    }
  }

//  // action for Insert-Time-Span menu item
//  private class ActionInsTimeSpan
//  extends MenuAction
//  implements ProcessingThread.Client
//  {
//    private double defaultValue = 1.0;
//    private String text;
// 
//    private ActionInsTimeSpan( String text, KeyStroke shortcut )
//    {
//      super( text, shortcut );
//     
//      this.text = text;
//    }
//
//    /**
//     *  Insert time span into all tracks.
//     *  Queries the amount of time from the user
//     *  and starts a ProcessingThread that will
//     *  update the trajectory files.
//     */
//    public void actionPerformed( ActionEvent e )
//    {
//      String  result;
//      double  length    = 0.0;
//      long  start, stop;
//
//      result  = JOptionPane.showInputDialog( null,
//        AbstractApplication.getApplication().getResourceString( "inputDlgInsTimeSpan" ),
//        String.valueOf( defaultValue ));  // XXX localized number?
//       
//      if( result == null ) return;
//      try {
//        length = Double.parseDouble( result );
//      }
//      catch( NumberFormatException e1 ) {
//        System.err.println( e1.getLocalizedMessage() );
//      }
//     
//      try {
//        doc.bird.waitShared( Session.DOOR_TIME );
//        start   = doc.timeline.getPosition();
//        stop    = start + (long) (doc.timeline.getRate() * length + 0.5);
//        if( stop <= start ) return;
//        defaultValue = length;
//      }
//      finally {
//        doc.bird.releaseShared( Session.DOOR_TIME );
//      }
//
//      final Main root = (Main) AbstractApplication.getApplication();
////      new ProcessingThread( this, root, root, doc, text, new Span( start, stop ), Session.DOOR_TIMETRNSMTE );
//      final ProcessingThread pt;
//      pt = new ProcessingThread( this, root, text );
//      pt.putClientArg( "span", new Span( start, stop ));
//      pt.start();
//    }
//
//    /**
//     *  Insert new data into the trajectory files.
//     *  If the timeline was empty, the tractories of
//     *  the selected transmitters are placed in a circle.
//     *  Otherwise the preceeding and succeeding frame
//     *  is linearly interpolated.
//     * 
//     *  @synchronization  waitExclusive on DOOR_TIMETRNSMTE
//     */
//    public int processRun( ProcessingThread context )
//    throws IOException
//    {
//      Transmitter            trns;
//      AudioTrail            at;
//      float[][]            frameBuf  = new float[2][4096];
//      float[][]            interpBuf   = null;
//      float[]              chBuf;
//      int                i, j, ch, len, interpType;
//      float              f1, f2, interpWeight;
//      double              d1;
//      Span              visibleSpan, interpSpan;
//      Span              span    = (Span) context.getClientArg( "span" );
////      TrackSpan            ts;
//      long              start, interpOff, interpLen;
//      long              progress  = 0;
//      long              progressLen;
//      boolean              success    = false;
//      CompoundSessionObjEdit      edit;
//      AudioStake            as;
//
//      if( span.getStart() > doc.timeline.getLength() ) return DONE;
//     
//      interpWeight= 1.0f / (float) (span.getLength() + 1); // +1 because the linear interpolation excludes the neighbouring samples
//      visibleSpan = doc.timeline.getVisibleSpan();
//      // try to linearly interpolate between sample just before the timeline position
//      // and the old sample at the timeline position; if there are no samples for
//      // interpolation (this happens at the session start or session en), then
//      // just repeat the nearest neighbour.
//      interpSpan  = new Span( Math.max( 0, span.getStart() - 1 ),
//                  Math.min( doc.timeline.getLength(), span.getStart() + 1 ));
//      interpLen   = span.getLength();
//      interpType  = (int) Math.min( 2, interpSpan.getLength() );
//      if( interpType > 1 ) {
//        interpBuf   = new float[2][(int) Math.min( interpLen, 4096 )];
//      }
//      progressLen = Math.max( 1, interpLen ) * doc.getTransmitters().size();
//
//      edit = new CompoundSessionObjEdit( this, doc.getTransmitters().getAll(), Transmitter.OWNER_TRAJ,
//                         null, null, getValue( NAME ).toString() );
//      try {
//        for( i = 0; i < doc.getTransmitters().size(); i++ ) {
//          trns  = (Transmitter) doc.getTransmitters().get( i );
//          at    = trns.getAudioTrail();
//
//          switch( interpType ) {
//          case 1: // only one neighbouring sample -> repeat it
//          case 0: // session is empty -> fill with angular positions
//            if( interpType == 0 ) {
//              d1    = ((double) i / (double) doc.getTransmitters().size() - 0.25) * Math.PI * 2;
//              f1    = (float) (0.25 * (2.0 + Math.cos( d1 )));
//              f2    = (float) (0.25 * (2.0 + Math.sin( d1 )));
//            } else {
//              at.readFrames( frameBuf, 0, interpSpan );
////              at.read( interpSpan, frameBuf, 0 );
//              f1    = frameBuf[0][0];
//              f2    = frameBuf[1][0];
//            }
//            for( j = 0; j < 4096; j++ ) {
//              frameBuf[0][j] = f1;
//              frameBuf[1][j] = f2;
//            }
//            at      = trns.getAudioTrail();
////            at.copyRangeFrom( srcTrail, copySpan, insertPos, mode, source, ce, trackMap, bcPre, bcPost )
////            ts      = at.beginInsert( span, edit );
//            as      = at.alloc( span );
//            for( start = span.getStart(); start < span.getStop(); start += len ) {
//              len    = (int) Math.min( 4096, span.getStop() - start );
////              at.continueWrite( ts, frameBuf, 0, len );
//              as.writeFrames( frameBuf, 0, new Span( start, start + len ));
//            }
////            at.finishWrite( ts, edit );
//            at.editInsert( this, span, edit );
//            at.editAdd( this, as, edit );
//            progress++;
//            context.setProgression( (float) progress / (float) progressLen );
//            break;
//
//          case 2:  // two neighbouring samples -> interpolation
////            at.read( interpSpan, frameBuf, 0 );
//            at.readFrames( frameBuf, 0, interpSpan );
////            ts = at.beginInsert( span, edit );
//            as = at.alloc( span );
//            for( start = span.getStart(), interpOff = 1; start < span.getStop();
//               start += len, interpOff += len ) {
//              
//              len = (int) Math.min( 4096, span.getStop() - start );
//              for( ch = 0; ch < 2; ch++ ) {
//                f1    = frameBuf[ch][0];
//                f2    = (frameBuf[ch][1] - f1) * interpWeight;
//                chBuf   = interpBuf[ch];
//                for( j = 0; j < len; j++ ) {
//                  chBuf[j] = (float) (interpOff + j) * f2 + f1;
//                }
//              }
////              at.continueWrite( ts, interpBuf, 0, len );
//              as.writeFrames( interpBuf, 0, new Span( start, start + len ));
//              progress += len;
//              context.setProgression( (float) progress / (float) progressLen );
//            }
////            at.finishWrite( ts, edit );
//            at.editInsert( this, span, edit );
//            at.editAdd( this, as, edit );
//            break;
//         
//          default:
//            assert false : interpType;
//          } // switch( interpType )
//        } // for( i = 0; i < doc.transmitterCollection.size(); )
//
//        edit.addPerform( new EditInsertTimeSpan( this, doc, span ));
//        if( visibleSpan.isEmpty() ) {
//          edit.addPerform( TimelineVisualEdit.scroll( this, doc, span ));
//        } else if( visibleSpan.contains( span.getStart() )) {
//          edit.addPerform( TimelineVisualEdit.scroll( this, doc,
//            new Span( visibleSpan.getStart(), visibleSpan.getStop() + span.getLength() )));
//        }
//       
//        edit.perform();
//        edit.end(); // fires doc.tc.modified()
//        doc.getUndoManager().addEdit( edit );
//        success = true;
//      }
//      catch( IOException e1 ) {
//        edit.cancel();
//        context.setException( e1 );
//      }
//     
//      return success ? DONE : FAILED;
//    } // run()
//
//    public void processFinished( ProcessingThread context ) {}
//    public void processCancel( ProcessingThread context ) {}
//  } // class actionInsTimeSpanClass

  /**
   *  Action to be attached to
   *  the Selection-Move-Forward item of the Timeline menu.
   */
  public class ActionSelectionForward
  extends MenuAction
  {
    private ActionSelectionForward( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    public void actionPerformed( ActionEvent e )
    {
      perform();
    }
   
    /**
     *  Advance the timeline selection
     *  by the length of the selection.
     *  Does nothing if the selection is
     *  empty or ends at the timeline's end.
     *
     *  @synchronization  waitExclusive on DOOR_TIME
     */
    public void perform()
    {
      Span          span;
      AbstractCompoundEdit  edit;
   
      try {
        doc.bird.waitExclusive( Session.DOOR_TIME );
        span  = doc.timeline.getSelectionSpan();
        if( span.isEmpty() || span.getStop() == doc.timeline.getLength() ) return;
        span  = new Span( span.getStop() - 1, Math.min( doc.timeline.getLength(),
                  span.getStop() - 1 + span.getLength() ));

        edit  = new BasicCompoundEdit( getValue( NAME ).toString() );
        edit.addPerform( TimelineVisualEdit.select( this, doc, span ));
        edit.addPerform( TimelineVisualEdit.position( this, doc, span.getStart() ));
        edit.perform();
        edit.end();
        doc.getUndoManager().addEdit( edit );
      }
      finally {
        doc.bird.releaseExclusive( Session.DOOR_TIME );
      }
    }
  }

  private class ActionSelectionBackwards extends MenuAction
  {
    private ActionSelectionBackwards( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    /**
     *  Move the timeline selection backwards
     *  by the length of the selection.
     *  Does nothing if the selection is
     *  empty or starts at the beginning of the timeline.
     *
     *  @synchronization  waitExclusive on DOOR_TIME
     */
    public void actionPerformed( ActionEvent e )
    {
      Span          span;
      AbstractCompoundEdit  edit;
   
      try {
        doc.bird.waitExclusive( Session.DOOR_TIME );
        span  = doc.timeline.getSelectionSpan();
        if( span.isEmpty() || span.getStart() == 0 ) return;
        span  = new Span( Math.max( 0, span.getStart() + 1 - span.getLength() ), span.getStart() + 1 );

        edit  = new BasicCompoundEdit( getValue( NAME ).toString() );
        edit.addPerform( TimelineVisualEdit.select( this, doc, span ));
        edit.addPerform( TimelineVisualEdit.position( this, doc, span.getStart() ));
        edit.perform();
        edit.end();
        doc.getUndoManager().addEdit( edit );
      }
      finally {
        doc.bird.releaseExclusive( Session.DOOR_TIME );
      }
    }
  }

  // action for the Filter-Trajectories menu item
  private class ActionFilter
  extends MenuAction
  {
    private ActionFilter( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    /**
     *  Brings up the Filter-Trajectories dialog
     */
    public void actionPerformed( ActionEvent e )
    {
      AppWindow filterFrame = (AppWindow) getApplication().getComponent( Main.COMP_FILTER );
   
      if( filterFrame == null ) {
        final Main root = (Main) AbstractApplication.getApplication();
        filterFrame = new FilterDialog( root, doc );
      }
      filterFrame.setVisible( true );
      filterFrame.toFront();
    }
  }
 
// ---------------- Action objects for window operations ----------------

  private class ActionDebugDumpListeners
  extends MenuAction
  {
    private ActionDebugDumpListeners()
    {
      super( "Dump Listeners" );
    }

    public void actionPerformed( ActionEvent e )
    {
      System.err.println( "---------- all transmitters ----------" );
      doc.getTransmitters().debugDump();
      System.err.println( "---------- all receivers ----------" );
      doc.getReceivers().debugDump();
      System.err.println( "---------- all groups ----------" );
      doc.getGroups().debugDump();
      System.err.println( "---------- active transmitters ----------" );
      doc.getActiveTransmitters().debugDump();
      System.err.println( "---------- active receivers ----------" );
      doc.getActiveReceivers().debugDump();
      for( int i = 0; i < doc.getGroups().size(); i++ ) {
        SessionGroup g = (SessionGroup) doc.getGroups().get( i );
        System.err.println( "............ group : "+g.getName() );
        System.err.println( "............ group transmitters ............" );
        g.getTransmitters().debugDump();
        System.err.println( "............ group receivers ............" );
        g.getReceivers().debugDump();
      }
    }
  }
 
  // action for the Observer menu item
  private class ActionObserver
  extends MenuAction
  {
    protected ActionObserver( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    /**
     *  Brings up the IOSetup
     */
    public void actionPerformed( ActionEvent e )
    {
      ObserverPalette f = (ObserverPalette) getApplication().getComponent( Main.COMP_OBSERVER );
   
      if( f == null ) {
        f = new ObserverPalette()// automatically adds component
      }
      f.setVisible( true );
      f.toFront();
    }
  }
}
TOP

Related Classes of de.sciss.meloncillo.gui.MenuFactory$ActionSelectionForward

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.