/*
* SelectPathButton.java
* (FScape)
*
* Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU General Public License v3+
*
*
* For further information, please contact Hanns Holger Rutz at
* contact@sciss.de
*/
package de.sciss.fscape.gui;
import de.sciss.app.BasicEvent;
import de.sciss.app.EventManager;
import de.sciss.gui.PathEvent;
import de.sciss.gui.PathListener;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// import net.roydesign.ui.FolderDialog;
/**
* This class is a rewritten version
* of FScape's <code>PathIcon</code> and provides
* a simple ToolIcon like button to
* allow the user to select a file
* from the harddisk. Besides, the user
* can drag files from the Finder onto
* the button's icon to set the button's
* path.
*
* @see java.awt.FileDialog
*/
public class SelectPathButton
extends JButton
implements EventManager.Processor
{
private File path = null;
private final int type;
private String dlgTxt;
private final EventManager elm = new EventManager( this );
private FilenameFilter filter = null;
protected static final DataFlavor[] supportedFlavors = {
DataFlavor.javaFileListFlavor, DataFlavor.stringFlavor
};
public SelectPathButton()
{
this(PathField.TYPE_INPUTFILE);
}
public SelectPathButton(int type) {
super("…"); // SHAPE_LIST);
this.type = type;
// Path2D p = new GeneralPath();
// Shapes.Search(p);
// // Shapes.ListView(p);
// // Shapes.Picker(p);
// final Shape icnShp = p.createTransformedShape(AffineTransform.getScaleInstance(0.5, 0.5));
//
// setIcon(new Icon() {
// @Override
// public void paintIcon(Component c, Graphics g, int x, int y) {
// Graphics2D g2 = (Graphics2D) g;
// g2.setColor(isEnabled() ? Color.black : Color.gray);
// g2.translate(x, y);
// g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
// g2.fill(icnShp);
// g2.translate(-x, -y);
// }
//
// @Override
// public int getIconWidth() {
// return 16;
// }
//
// @Override
// public int getIconHeight() {
// return 16;
// }
// });
setToolTipText("Choose File...");
setTransferHandler(new PathTransferHandler());
final MouseInputAdapter mia = new MouseInputAdapter() {
private MouseEvent dndInit = null;
private boolean dndStarted = false;
public void mousePressed(MouseEvent e) {
dndInit = e;
dndStarted = false;
}
public void mouseReleased(MouseEvent e) {
if (!dndStarted && contains(e.getPoint())) showFileChooser();
dndInit = null;
dndStarted = false;
}
public void mouseDragged( MouseEvent e )
{
if( !dndStarted && (dndInit != null) &&
((Math.abs( e.getX() - dndInit.getX() ) > 5) ||
(Math.abs( e.getY() - dndInit.getY() ) > 5))) {
JComponent c = (JComponent) e.getSource();
c.getTransferHandler().exportAsDrag( c, e, TransferHandler.COPY );
dndStarted = true;
}
}
};
addMouseListener (mia);
addMouseMotionListener(mia);
}
/**
* Constructs a new <code>PathButton</code> for
* given type of file chooser and optional dialog text
*
* @param type the type of file chooser to display. the values
* are those from <code>PathField</code>, e.g.
* <code>PathField.TYPE_INPUT</code>
* @param dlgTxt text to display in the file chooser dialog or <code>null</code>
*/
public SelectPathButton(int type, String dlgTxt) {
this(type);
setDialogText(dlgTxt);
}
public void setDialogText(String dlgTxt) {
this.dlgTxt = dlgTxt;
}
/**
* Sets the button'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) {
this.path = path;
}
/*
* Sets a new path and dispatches a <code>PathEvent</code>
* to registered listeners
*
* @param path the new path for the button and the event
*/
protected void setPathAndDispatchEvent(File path) {
setPath(path);
elm.dispatchEvent(new PathEvent(this, PathEvent.CHANGED, System.currentTimeMillis(), path));
}
/**
* Returns the path set for the button
* or chosen by the user after a file chooser
* has been shown.
*
* @return the button's path or <code>null</code>
* if no path was set or the file chooser was cancelled
*/
public File getPath() {
return path;
}
/**
* Sets the filter to use for enabling or disabling items
* in the file dialog.
*
* @param filter the new filter or null to remove an existing filter
*/
public void setFilter(FilenameFilter filter) {
this.filter = filter;
}
/**
* Queries the current item filter for the file dialog.
*
* @return the current filter or null if no filter is installed
*/
public FilenameFilter getFilter() {
return filter;
}
// --- listener registration ---
/**
* Register a <code>PathListener</code>
* which will be informed about changes of
* the path (i.e. user selections in the
* file chooser).
*
* @param listener the <code>PathListener</code> to register
* @see de.sciss.app.EventManager#addListener( Object )
*/
public void addPathListener(PathListener listener) {
elm.addListener(listener);
}
/**
* Unregister 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++ )
}
protected void showDialog(Dialog dlg) {
dlg.setVisible(true);
}
protected void showFileChooser() {
File p;
FileDialog fDlg;
String fDir, fFile; // , fPath;
// int i;
Component win;
for (win = this; !(win instanceof Frame); ) {
win = SwingUtilities.getWindowAncestor(win);
if (win == null) return;
}
p = getPath();
switch( type & PathField.TYPE_BASICMASK ) {
case PathField.TYPE_INPUTFILE:
fDlg = new FileDialog( (Frame) win, dlgTxt, FileDialog.LOAD );
break;
case PathField.TYPE_OUTPUTFILE:
fDlg = new FileDialog( (Frame) win, dlgTxt, FileDialog.SAVE );
break;
case PathField.TYPE_FOLDER:
fDlg = new FileDialog( (Frame) win, dlgTxt, FileDialog.SAVE );
// fDlg = new FolderDialog( (Frame) win, dlgTxt );
break;
default:
fDlg = null;
assert false : (type & PathField.TYPE_BASICMASK);
break;
}
if( p != null ) {
fDlg.setFile( p.getName() );
fDlg.setDirectory( p.getParent() );
}
if( filter != null ) {
fDlg.setFilenameFilter( filter );
}
showDialog( fDlg );
fDir = fDlg.getDirectory();
fFile = fDlg.getFile();
if( ((type & PathField.TYPE_BASICMASK) != PathField.TYPE_FOLDER) && (fDir == null) ) {
fDir = "";
}
if( (fFile != null) && (fDir != null) ) {
if( (type & PathField.TYPE_BASICMASK) == PathField.TYPE_FOLDER ) {
p = new File( fDir );
} else {
p = new File( fDir + fFile );
}
setPathAndDispatchEvent( p );
}
fDlg.dispose();
}
// ----------- interner TransferHandler -----------
private class PathTransferHandler
extends TransferHandler
{
protected PathTransferHandler() { /* empty */ }
/**
* Overridden to import a Pathname (Fileliste or String) if it is available.
*/
public boolean importData( JComponent c, Transferable t )
{
Object o;
List fileList;
File newPath = null;
try {
if( t.isDataFlavorSupported( DataFlavor.javaFileListFlavor )) {
o = t.getTransferData( DataFlavor.javaFileListFlavor );
if( o instanceof List ) {
fileList = (List) o;
if( !fileList.isEmpty() ) {
o = fileList.get( 0 );
if( o instanceof File ) {
newPath = (File) o;
} else {
newPath = new File( o.toString() );
}
}
}
} else if( t.isDataFlavorSupported( DataFlavor.stringFlavor )) {
newPath = new File( (String) t.getTransferData( DataFlavor.stringFlavor ));
}
if( newPath != null ) {
setPathAndDispatchEvent( newPath );
return true;
}
}
catch( UnsupportedFlavorException e1 ) { e1.printStackTrace(); }
catch( IOException e2 ) { e2.printStackTrace(); }
return false;
}
public int getSourceActions( JComponent c )
{
return COPY;
}
protected Transferable createTransferable( JComponent c )
{
// System.err.println( "createTransferable" );
return new PathTransferable( getPath() );
}
protected void exportDone( JComponent source, Transferable data, int action )
{
// System.err.println( "exportDone. Action == "+action );
}
public boolean canImport( JComponent c, DataFlavor[] flavors )
{
// System.err.println( "canImport" );
for( int i = 0; i < flavors.length; i++ ) {
for( int j = 0; j < supportedFlavors.length; j++ ) {
if( flavors[i].equals( supportedFlavors[j] )) return true;
}
}
return false;
}
// public Icon getVisualRepresentation( Transferable t )
// {
//System.err.println( "getVisualRepresentation" );
// return FileSystemView.getFileSystemView().getSystemIcon( new File( System.getProperty( "user.home" )));
// }
} // class PathTransferHandler
private static class PathTransferable
implements Transferable
{
private final File f;
protected PathTransferable( File f )
{
this.f = f;
}
public DataFlavor[] getTransferDataFlavors()
{
return supportedFlavors;
}
public boolean isDataFlavorSupported( DataFlavor flavor )
{
for( int i = 0; i < supportedFlavors.length; i++ ) {
if( supportedFlavors[ i ].equals( flavor )) return true;
}
return false;
}
public Object getTransferData( DataFlavor flavor )
throws UnsupportedFlavorException, IOException
{
if( f == null ) throw new IOException();
if( flavor.equals( DataFlavor.javaFileListFlavor )) {
final List<File> coll = new ArrayList<File>( 1 );
coll.add( f );
return coll;
} else if( flavor.equals( DataFlavor.stringFlavor )) {
return f.getAbsolutePath();
}
throw new UnsupportedFlavorException( flavor );
}
}
}