Package de.sciss.gui

Source Code of de.sciss.gui.PathField

/*
*  PathField.java
*  de.sciss.gui package
*
*  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:
*    20-May-05  created from de.sciss.meloncillo.gui.PathField
*    08-Sep-05  write-protected icon
*    24-Sep-05  changed META shortcuts to work on PC (= Ctrl+Shift)
*    06-Nov-05  extends SpringPanel ; improved checkExists method ; escape key similar to numberfield
*/

package de.sciss.gui;

import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JLabel;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

import de.sciss.app.BasicEvent;
import de.sciss.app.EventManager;

/**
*  This class is an updated (slim) version
*  of FScape's <code>PathField</code> and provides a GUI object
*  that displays a file's or folder's path,
*  optionally with format information, and allows
*  the user to browse the harddisk to change
*  to a different file.
*
@author    Hanns Holger Rutz
@version  0.33, 28-Jun-08
*/
public class PathField
extends SpringPanel
implements ActionListener, PathListener, EventManager.Processor
{
// -------- public Variablen --------
  /**
   *  type flag : the bitmask for all path types
   */
  public static final int TYPE_BASICMASK  = 0x0F;
  /**
   *  type flag : file is used for input / reading
   */
  public static final int TYPE_INPUTFILE  = 0x00;
  /**
   *  type flag : request an extra gadget to view file information
   */
  public static final int TYPE_FORMATFIELD= 0x10;
  /**
   *  type flag : file is used for output / writing
   */
  public static final int TYPE_OUTPUTFILE  = 0x01;
  /**
   *  type flag : path to be chosen is a folder
   */
  public static final int TYPE_FOLDER    = 0x02;

// -------- private Variablen --------

  protected static PathList  userPaths      = null;
  private static final int  USERPATHS_NUM    = 9;    // userPaths capacity
  private static final int  ABBR_LENGTH      = 12;    // constants for abbreviate
//  private static final int  DEFAULT_COLUMN_NUM  = 32;    // constants for IOTextField

  protected final IOTextField  ggPath;
  private final PathButton  ggChoose;
  private final JLabel    lbWarn;
  protected ColouredTextField  ggFormat  = null;
 
  private static final Color  COLOR_ERR   = new Color( 0xFF, 0x00, 0x00, 0x2F );
  private static final Color  COLOR_EXISTS= new Color( 0x00, 0x00, 0xFF, 0x2F );
  protected static final Color COLOR_PROPSET=new Color( 0x00, 0xFF, 0x00, 0x2F );

  private final int    type;
//  private final String  dlgTxt;
  protected String    scheme;
  protected String    protoScheme;
  private PathField    superPaths[];

  private final List        collChildren  = new ArrayList();
  private final EventManager    elm        = new EventManager( this );

  private static final int    MENU_SHORTCUT  = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
  protected static final int    myMeta      = MENU_SHORTCUT == InputEvent.CTRL_MASK ?
    InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK : MENU_SHORTCUT;  // META on Mac, CTRL+SHIFT on PC

  private static Icon        icnAlertStop  = null;
 
  private boolean warnWhenExists      = false;
  private boolean errWhenExistsNot    = false;
  private boolean errWhenWriteProtected  = false;
 
// -------- public Methoden --------

  public PathField()
  {
    this( TYPE_INPUTFILE );
  }
 
  public PathField( int type )
  {
    super();
    this.type = type;
   
    // first instance initialized userPath list
    if( userPaths == null ) {
      userPaths  = new PathList( USERPATHS_NUM, GUIUtil.getUserPrefs(), PathList.KEY_USERPATHS );
      if( userPaths.getPathCount() < USERPATHS_NUM ) {  // prefs not initialized
        File home  = new File( System.getProperty( "user.home" ));
        File[] sub  = home.listFiles();
        userPaths.addPathToHead( home );
        if( sub != null ) {
          for( int i = 0; i < sub.length && userPaths.getPathCount() < USERPATHS_NUM; i++ ) {
            if( sub[i].isDirectory() && !sub[i].isHidden() ) userPaths.addPathToTail( sub[i] );
          }
        }
        while( userPaths.getPathCount() < USERPATHS_NUM ) {
          userPaths.addPathToTail( home );
        }
      }
    }

    lbWarn        = new JLabel();
    gridAdd( lbWarn, 0, 0 );

    ggPath      = new IOTextField();
    ggPath.addActionListener( this );    // High-Level Events: Return-Hit weiterleiten
    ggChoose    = createPathButton( type );
    ggChoose.addPathListener( this );
    gridAdd( ggPath, 1, 0 );

    if( (type & TYPE_FORMATFIELD) != 0 ) {
      ggFormat    = new ColouredTextField();
      ggFormat.setEditable( false );
      ggFormat.setBackground( null );
      gridAdd( ggFormat, 1, 1 );
    }

    gridAdd( ggChoose, 2, 0 );

    makeCompactGrid();

//    deriveFrom( new PathField[0], (ggType != null) ? "$E" : "" );
    deriveFrom( new PathField[0], "" );

    // MacOS X has a bug with
    // the caret position when the
    // font isn't set explicitly for sub containers
    this.addPropertyChangeListener( "font", new PropertyChangeListener() {
      public void propertyChange( PropertyChangeEvent e )
      {
        Font fnt = ((PathField) e.getSource()).getFont();
        ggPath.setFont( fnt );
        if( ggFormat != null ) ggFormat.setFont( fnt );
      }
    });
  }
 
  /**
   *  Create a new <code>PathField</code>
   *
   *  @param  type  one of the types covered by TYPE_BASICMASK
   *          bitwise-OR optional displays like TYPE_FORMATFIELD
   *  @param  dlgTxt  the text string to display in the filechooser or <code>null</code>
   */
  public PathField( int type, String dlgTxt )
  {
    this( type );
    ggChoose.setDialogText( dlgTxt );
    if( (type & TYPE_OUTPUTFILE) != 0 ) {
      warnWhenExists = true;
      errWhenWriteProtected = true;
    } else {
      errWhenExistsNot = true;
    }
  }
 
  /**
   *  Sets the gadget's path. This is path will be
   *  used as default setting when the file chooser is shown
   *
   *  @param  path  the new path for the button
   */
  public void setPath( File path )
  {
    setPathIgnoreScheme( path );
    scheme = createScheme( path.getPath() );
  }
 
  public void setWarnWhenExists( boolean onOff )
  {
    if( warnWhenExists != onOff ) {
      warnWhenExists = onOff;
      updateIconAndColour();
    }
  }

  public void setErrWhenExistsOn( boolean onOff )
  {
    if( errWhenExistsNot != onOff ) {
      errWhenExistsNot = onOff;
      updateIconAndColour();
    }
  }

  public void setErrWhenWriteProtected( boolean onOff )
  {
    if( errWhenWriteProtected != onOff ) {
      errWhenWriteProtected = onOff;
      updateIconAndColour();
    }
  }

  private void setPathIgnoreScheme( File path )
  {
    ggPath.setText( path.getPath() );
    ggChoose.setPath( path );
    for( int i = 0; i < collChildren.size(); i++ ) {
      ((PathField) collChildren.get( i )).motherSpeaks( path );
    }
    updateIconAndColour();
  }
 
  /**
   *  Sets a new path and dispatches a <code>PathEvent</code>
   *  to registered listeners
   *
   *  @param  path  the new path for the gadget and the event
   */
  protected void setPathAndDispatchEvent( File path )
  {
    setPathIgnoreScheme( path );
    elm.dispatchEvent( new PathEvent( this, PathEvent.CHANGED, System.currentTimeMillis(), path ));
  }
 
  /**
   *  Returns the path displayed in the gadget.
   *
   *  @return the <code>File</code> corresponding to the path string in the gadget
   */
  public File getPath()
  {
    return( new File( ggPath.getText() ));
  }

  /**
   *  Change the contents of the format gadget.
   *
   *  @param  txt      Text to be displayed in the format gadget
   *  @param  success    <code>false</code> indicates file format parse error
   *            and will render the format gadget red.
   *  @throws IllegalStateException   if the path field doesn't have a format gadget
   */
  public void setFormat( String txt, boolean success )
  {
    if( ggFormat == null ) throw new IllegalStateException();
   
    ggFormat.setText( txt );
    ggFormat.setPaint( success ? null : COLOR_ERR );
  }
 
  /**
   *  Get the string displayed in the format gadget.
   *
   *  @return    the currently displayed format text or <code>null</code>
   *        if the path field has no format gadget.
   */
  public String getFormat()
  {
    if( ggFormat != null ) {
      return ggFormat.getText();
    } else {
      return null;
    }
  }
 
  /**
   *  Gets the type of the path field.
   *
   *  @return the gadget's type as specified in the constructor
   *      use bitwise-AND with <code>TYPE_BASICMASK</code> to query the
   *      file access type.
   */
  public int getType()
  {
    return type;
  }
 
  /**
   *  Selects the file name portion of the text contents.
   *
   *  @param  extention  whether to include the file name suffix or not
   */
  public void selectFileName( boolean extention )
  {
    final String  s = ggPath.getText();
    final int    i = s.lastIndexOf( File.separatorChar ) + 1;
    final int    j = s.lastIndexOf( '.' );
    ggPath.select( i, j >= i ? j : s.length() );
  }
 
  public boolean requestFocusInWindow()
  {
    return ggPath.requestFocusInWindow();
  }
 
  /**
   *  <code>PathField</code> offers a mechanism to automatically derive
   *  a path name from a "mother" <code>PathField</code>. This applies
   *  usually to output files whose names are derived from
   *  PathFields which represent input paths. The provided
   *  'scheme' String can contain the Tags
   *  <pre>
   *  $Dx = Directory of superPath x; $Fx = Filename; $E = Extension; $Bx = Brief filename
   *  </pre>
   *  where 'x' is the index in the provided array of
   *  mother PathFields. Whenever the mother contents
   *  changes, the child PathField will recalculate its
   *  name. When the user changes the contents of the child
   *  PathField, an algorithm tries to find out which components
   *  are related to the mother's pathname, parts that cannot
   *  be identified will not be automatically changing any more
   *  unless the user completely clears the PathField (i.e.
   *  restores full automation).
   *  <p>
   *  The user can abbreviate or extend filenames by pressing the appropriate
   *  key; in this case the $F and $B tags are exchanged in the scheme.
   *
   *  @param  sp     array of mother path fields to listen to
   *  @param  s    automatic formatting scheme which can incorporate
   *          placeholders for the mother fields' paths.
   */
  public void deriveFrom( PathField[] sp, String s )
  {
    this.superPaths   = sp;
    this.scheme      = s;
    this.protoScheme  = s;

    for( int i = 0; i < sp.length; i++ ) {
      sp[ i ].addChildPathField( this );
    }
  }
 
  private void addChildPathField( PathField child )
  {
    if( !collChildren.contains( child )) collChildren.add( child );
  }
 
  private void motherSpeaks( File superPath )
  {
    setPathAndDispatchEvent( new File( evalScheme( scheme )));
  }

  // --- listener registration ---
 
  /**
   *  Registers a <code>PathListener</code>
   *  which will be informed about changes of
   *  the path (i.e. user selections in the
   *  file chooser or text editing).
   *
   *  @param  listener  the <code>PathListener</code> to register
   *  @see  de.sciss.app.EventManager#addListener( Object )
   */
  public void addPathListener( PathListener listener )
  {
    elm.addListener( listener );
  }

  /**
   *  Unregisters a <code>PathListener</code>
   *  from receiving path change events.
   *
   *  @param  listener  the <code>PathListener</code> to unregister
   *  @see  de.sciss.app.EventManager#removeListener( Object )
   */
  public void removePathListener( PathListener listener )
  {
    elm.removeListener( listener );
  }

  public void processEvent( BasicEvent e )
  {
    PathListener listener;
    int i;
   
    for( i = 0; i < elm.countListeners(); i++ ) {
      listener = (PathListener) elm.getListener( i );
      switch( e.getID() ) {
      case PathEvent.CHANGED:
        listener.pathChanged( (PathEvent) e );
        break;
      default:
        assert false : e.getID();
      }
    } // for( i = 0; i < elm.countListeners(); i++ )
  }

// -------- private Methoden --------

  private static Icon getAlertStopIcon()
  {
    if( icnAlertStop == null ) {
//      icnAlertStop = new ImageIcon( ClassLoader.getSystemClassLoader().getResource( "alertstop.png" ));
      icnAlertStop = new ImageIcon( PathField.class.getResource( "alertstop.png" ));
    }
    return icnAlertStop;
  }

  private void updateIconAndColour()
  {
    final File    path    = getPath();
    final File    parent    = path.getParentFile();
    final boolean  folder    = (type & TYPE_FOLDER) != 0;
    boolean      parentExists= false;
    boolean      exists    = false;
    boolean      wp      = false;
    final Color    c;
    final Icon    icn;
    final String  tt;
   
    if( warnWhenExists || errWhenExistsNot || errWhenWriteProtected ) {
    try {
      parentExists  = (parent != null) && parent.isDirectory();
      exists      = folder? path.isDirectory() : path.isFile();
      if( errWhenWriteProtected ) {
        wp      = parentExists &&
          ((exists && !path.canWrite()) || (!exists && !parent.canWrite()));
      }
    } catch( SecurityException e ) { /* ignore */ }

    if( errWhenWriteProtected && (wp || !parentExists) ) {
      c  = COLOR_ERR;
      icn  = GUIUtil.getNoWriteIcon();
      tt  = GUIUtil.getResourceString( folder ? "ttWarnFolderWriteProtected" : "ttWarnFileWriteProtected" );
    } else if( errWhenExistsNot && !exists ) {
      c  = COLOR_ERR;
      icn = getAlertStopIcon();
      tt  = GUIUtil.getResourceString( folder ? "ttWarnFolderExistsNot" : "ttWarnFileExistsNot" );
    } else if( warnWhenExists && exists ) {
      c  = COLOR_EXISTS;
      icn = getAlertStopIcon();
      tt  = GUIUtil.getResourceString( folder ? "ttWarnFolderExists" : "ttWarnFileExists" );
    } else {
      c  = null;
      icn  = null;
      tt  = null;
    }
    if( c != ggPath.getPaint() ) {
      ggPath.setPaint( c );
    }
    if( lbWarn.getIcon() != icn ) {
      lbWarn.setIcon( icn );
    }
    if( lbWarn.getToolTipText() != tt )
      lbWarn.setToolTipText( tt );
    }
  }

  /*
   *  Tags: $Dx = Directory of superPath x; $Fx = Filename; $E = Extension; $Bx = Brief filename
   */
  protected String evalScheme( String s )
  {
    String  txt2;
    int    i, j, k;

    for( i = s.indexOf( "$D" ); (i >= 0) && (i < s.length() - 2); i = s.indexOf( "$D", i )) {
      j    = s.charAt( i + 2 ) - 48;
      try {
        txt2 = superPaths[ j ].getPath().getPath();
      } catch( ArrayIndexOutOfBoundsException e1 ) {
        txt2 = "";
      }
      // sucky java 1.1 stringbuffer is impotent
      s  = s.substring( 0, i ) + txt2.substring( 0, txt2.lastIndexOf( File.separatorChar ) + 1 ) +
            s.substring( i + 3 );
    }
    for( i = s.indexOf( "$F" ); (i >= 0) && (i < s.length() - 2); i = s.indexOf( "$F", i )) {
      j    = s.charAt( i + 2 ) - 48;
      try {
        txt2 = superPaths[ j ].getPath().getPath();
      } catch( ArrayIndexOutOfBoundsException e1 ) {
        txt2 = "";
      }
      txt2  = txt2.substring( txt2.lastIndexOf( File.separatorChar ) + 1 );
      k    = txt2.lastIndexOf( '.' );
      s  = s.substring( 0, i ) + ((k > 0) ? txt2.substring( 0, k ) : txt2 ) +
            s.substring( i + 3 );
    }
    for( i = s.indexOf( "$X" ); (i >= 0) && (i < s.length() - 2); i = s.indexOf( "$X", i )) {
      j    = s.charAt( i + 2 ) - 48;
      try {
        txt2 = superPaths[ j ].getPath().getPath();
      } catch( ArrayIndexOutOfBoundsException e1 ) {
        txt2 = "";
      }
      txt2  = txt2.substring( txt2.lastIndexOf( File.separatorChar ) + 1 );
      k    = txt2.lastIndexOf( '.' );
      s  = s.substring( 0, i ) + ((k > 0) ? txt2.substring( k ) : "" ) +
            s.substring( i + 3 );
    }
    for( i = s.indexOf( "$B" ); (i >= 0) && (i < s.length() - 2); i = s.indexOf( "$B", i )) {
      j    = s.charAt( i + 2 ) - 48;
      try {
        txt2 = superPaths[ j ].getPath().getPath();
      } catch( ArrayIndexOutOfBoundsException e1 ) {
        txt2 = "";
      }
      txt2  = txt2.substring( txt2.lastIndexOf( File.separatorChar ) + 1 );
      k    = txt2.lastIndexOf( '.' );
      txt2  = abbreviate( (k > 0) ? txt2.substring( 0, k ) : txt2 );
      s   = s.substring( 0, i ) + txt2 + s.substring( i + 3 );
    }
// XXXX
//    for( i = scheme.indexOf( "$E" ); i >= 0; i = scheme.indexOf( "$E", i )) {
//      j    = getType();
//      scheme  = scheme.substring( 0, i ) + GenericFile.getExtStr( j ) + scheme.substring( i + 2 );
//    }

    return s;
  }

  /*
   *  A filename will be abbreviated. This is not so
   *  critical on MacOS X any more because filenames
   *  can be virtually as long as possible; on some
   *  systems however when filenames are combinations
   *  of more than one mother file this can be
   *  crucial to keep the total filename length within
   *  the file system's allowed bounds.
   */
  private static String abbreviate( String longStr )
  {
    StringBuffer  shortStr;
    int        i, j;
    char      c;
 
    j = longStr.length();
    if( j <= ABBR_LENGTH ) return longStr;

    shortStr = new StringBuffer( j );
    for( i = 0; (i < j) && (shortStr.length() + j - i > ABBR_LENGTH); i++ ) {
      c = longStr.charAt( i );
      if( Character.isLetterOrDigit( c )) {
        shortStr.append( c );
      }
    }
    shortStr.append( longStr.substring( i ));
    longStr  = shortStr.toString();
    j    = longStr.length();
    if( j <= ABBR_LENGTH ) return longStr;
   
    shortStr = new StringBuffer( j );
    shortStr.append( longStr.charAt( 0 ));
    for( i = 1; (i < j - 1) && (shortStr.length() + j - i > ABBR_LENGTH); i++ ) {
      c = longStr.charAt( i );
      if( "aeiou√§√∂√º".indexOf( c ) < 0 ) {
        shortStr.append( c );
      }
    }
    shortStr.append( longStr.substring( i ));
    longStr  = shortStr.toString();
    j    = longStr.length();
    if( j <= ABBR_LENGTH ) return longStr;
   
    i = (ABBR_LENGTH >> 1) - 1;
   
    return( longStr.substring( 0, i ) + '\'' + longStr.substring( longStr.length() - i ));
  }

  /*
   *  Try to analyse a concrete pathname
   *  with respect to mother pathnames
   *  to find some sort of scheme behind it.
   */
  private String createScheme( String applied )
  {
    String  txt2;
    int    i = 0;
    int    k = 0;
    int    m;
    int    checkedAbbrev;
    boolean  checkedFull;

    if( applied.length() == 0 ) return protoScheme;

    for( i = 0; i < superPaths.length; i++ ) {
      txt2 = superPaths[ i ].getPath().getPath();
      txt2 = txt2.substring( 0, txt2.lastIndexOf( File.separatorChar ) + 1 );
      if( applied.startsWith( txt2 )) {
        applied  = "$D" + (char) (i + 48) + applied.substring( txt2.length() );
        k    = 3;
        break;
      }
    }
    k = Math.max( k, applied.lastIndexOf( File.separatorChar ) + 1 );
    for( i = 0, checkedAbbrev = -1; i < superPaths.length; i++ ) {
      txt2  = superPaths[ i ].getPath().getPath();
      txt2  = txt2.substring( txt2.lastIndexOf( File.separatorChar ) + 1 );
      m    = txt2.lastIndexOf( '.' );
      txt2  = (m > 0) ? txt2.substring( 0, m ) : txt2;
      if( (protoScheme.indexOf( "$B" + (char) (i + 48) ) < 0) || (checkedAbbrev == i) ) {
        m  = applied.indexOf( txt2, k );
        if( m >= 0 ) {
          applied = applied.substring( 0, m ) + "$F" + (char) (i + 48) + applied.substring( m + txt2.length() );
          k    = m + 3;
          continue;
        }
        checkedFull  = true;
      } else {
        checkedFull = false;
      }
      if( checkedAbbrev == i ) continue;
      txt2 = abbreviate( txt2 );
      m   = applied.indexOf( txt2, k );
      if( m >= 0 ) {
        applied = applied.substring( 0, m ) + "$B" + (char) (i + 48) + applied.substring( m + txt2.length() );
        k    = m + 3;
      } else if( !checkedFull ) {
        checkedAbbrev = i;
        i--;        // retry non-abbreviated
      }
    }
// XXX
//    txt2 = GenericFile.getExtStr( getType() );
//    if( applied.endsWith( txt2 )) {
//      applied = applied.substring( 0, applied.length() - txt2.length() ) + "$E";
//    }

    return applied;
  }

  protected String abbrScheme( String orig )
  {
    int i = orig.lastIndexOf( "$F" );
    if( i >= 0 ) {
      return( orig.substring( 0, i ) + "$B" + orig.substring( i + 2 ));
    } else {
      return orig;
    }
  }

  protected String expandScheme( String orig )
  {
    int i = orig.indexOf( "$B" );
    if( i >= 0 ) {
      return( orig.substring( 0, i ) + "$F" + orig.substring( i + 2 ));
    } else {
      return orig;
    }
  }

  protected String udirScheme( String orig, int idx )
  {
    int    i;
    File  udir = userPaths.getPath( idx );
 
    if( udir == null ) return orig;
 
    if( orig.startsWith( "$D" )) {
      i = 3;
    } else {
      i = orig.lastIndexOf( File.separatorChar ) + 1;
    }

    return( new File( udir, orig.substring( i )).getPath() );
  }
 
  protected PathButton createPathButton( int buttonType )
  {
    return new PathButton( buttonType );
  }
 
// -------- PathListener interface --------
// we're listening to ggChoose

  public void pathChanged( PathEvent e )
  {
    File path = e.getPath();
    scheme = createScheme( path.getPath() );
    setPathAndDispatchEvent( path );
  }

// -------- ActionListener interface --------
// we're listening to ggPath

  public void actionPerformed( ActionEvent e )
  {
    String str = ggPath.getText();
    if( ggPath.getText().length() == 0 ) {        // automatic generation
      str = evalScheme( scheme );
    } else {
      scheme = createScheme( str );
    }
    setPathAndDispatchEvent( new File( str ));
  }

// -------- internal IOTextfeld class --------

  private class IOTextField
  extends ColouredTextField
  {
    protected IOTextField()
    {
      super( 32 );
     
      final InputMap    inputMap  = getInputMap();
      final ActionMap    actionMap   = getActionMap();
      String        s;
     
      inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_LEFT, myMeta + InputEvent.ALT_MASK ), "abbr" );
      actionMap.put( "abbr", new AbstractAction() {
        public void actionPerformed( ActionEvent e )
        {
          scheme = abbrScheme( scheme );
          setPathAndDispatchEvent( new File( evalScheme( scheme )));
        }
      });
      inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_RIGHT, myMeta + InputEvent.ALT_MASK ), "expd" );
      actionMap.put( "expd", new AbstractAction() {
        public void actionPerformed( ActionEvent e )
        {
          scheme = expandScheme( scheme );
          setPathAndDispatchEvent( new File( evalScheme( scheme )));
        }
      });
      inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, myMeta ), "auto" );
      actionMap.put( "auto", new AbstractAction() {
        public void actionPerformed( ActionEvent e )
        {
          scheme = protoScheme;
          setPathAndDispatchEvent( new File( evalScheme( scheme )));
        }
      });
      inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), "lost" );
      actionMap.put( "lost", new AbstractAction() {
        public void actionPerformed( ActionEvent e )
        {
          final JRootPane rp = SwingUtilities.getRootPane( IOTextField.this );
          if( rp != null ) rp.requestFocus();
        }
      });
      for( int i = 0; i < USERPATHS_NUM; i++ ) {
        s = "sudir" + i;
        inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_NUMPAD1 + i, myMeta + InputEvent.ALT_MASK ), s );
        actionMap.put( s, new SetUserDirAction( i ));
        s = "rudir" + i;
        inputMap.put( KeyStroke.getKeyStroke( KeyEvent.VK_NUMPAD1 + i, myMeta ), s );
        actionMap.put( s, new RecallUserDirAction( i ));
      }
    }

    private class SetUserDirAction
    extends AbstractAction
    {
      private int idx;
      private javax.swing.Timer visualFeedback;
      private Paint oldPaint = null;
   
      protected SetUserDirAction( int idx )
      {
        this.idx    = idx;
        visualFeedback  = new javax.swing.Timer( 250, this );
        visualFeedback.setRepeats( false );
      }
     
      public void actionPerformed( ActionEvent e )
      {
        if( e.getSource() == visualFeedback ) {
          ggPath.setPaint( oldPaint );
        } else {
          File dir = getPath().getParentFile();
          if( dir != null ) {
            userPaths.setPath( idx, dir );
            if( visualFeedback.isRunning() ) {
              visualFeedback.restart();
            } else {
              oldPaint = ggPath.getPaint();
              ggPath.setPaint( COLOR_PROPSET );
              visualFeedback.start();
            }
          }
        }
      }
    }

    private class RecallUserDirAction
    extends AbstractAction
    {
      private int idx;
   
      protected RecallUserDirAction( int idx )
      {
        this.idx = idx;
      }
     
      public void actionPerformed( ActionEvent e )
      {
        scheme = udirScheme( scheme, idx );
        setPathAndDispatchEvent( new File( evalScheme( scheme )));
      }
    }
  } // class IOTextField
} // class PathField
TOP

Related Classes of de.sciss.gui.PathField

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.