package de.unisiegen.gtitool.ui.swing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;
import de.unisiegen.gtitool.ui.swing.dnd.JGTIListModelRows;
import de.unisiegen.gtitool.ui.swing.dnd.JGTIListModelRowsTransferable;
/**
* Special {@link JList}.
*
* @author Christian Fehler
* @version $Id$
*/
public final class JGTIList extends JList implements DropTargetListener
{
/**
* The serial version uid.
*/
private static final long serialVersionUID = 6892137428208392217L;
/**
* The into drag and drop mode.
*
* @see #getDndMode()
* @see #setDndMode(int)
*/
public static final int DROP_INTO = 0;
/**
* The between drag and drop mode.
*
* @see #getDndMode()
* @see #setDndMode(int)
*/
public static final int DROP_BETWEEN = 1;
/**
* The drop point.
*/
private Point dropPoint = null;
/**
* The drop mode used for this {@link JGTIList}.
*
* @see #getDndMode()
* @see #setDndMode(int)
*/
private int dndMode = DROP_INTO;
/**
* The allowed drag and drop sources.
*/
private ArrayList < JComponent > allowedDndSources;
/**
* Flag that indicates that the drag and drop is allowed.
*/
protected boolean dragAndDropAllowed = true;
/**
* Allocates a new {@link JGTIList}.
*/
public JGTIList ()
{
super ();
init ();
}
/**
* Allocates a new {@link JGTIList}.
*
* @param dataModel The {@link ListModel}.
*/
public JGTIList ( ListModel dataModel )
{
super ( dataModel );
init ();
}
/**
* Allocates a new {@link JGTIList}.
*
* @param listData The list data.
*/
public JGTIList ( final Object [] listData )
{
super ( listData );
init ();
}
/**
* Allocates a new {@link JGTIList}.
*
* @param listData The list data.
*/
public JGTIList ( final Vector < ? > listData )
{
super ( listData );
init ();
}
/**
* Adds the given {@link JComponent} to the allowed drag and drop sources.
*
* @param jComponent The {@link JComponent} to add.
*/
public final void addAllowedDndSource ( JComponent jComponent )
{
if ( !this.allowedDndSources.contains ( jComponent ) )
{
this.allowedDndSources.add ( jComponent );
}
}
/**
* Clears the allowed drag and drop sources.
*/
public final void clearAllowedDndSources ()
{
this.allowedDndSources.clear ();
}
/**
* {@inheritDoc}
*
* @see DropTargetListener#dragEnter(DropTargetDragEvent)
*/
public final void dragEnter ( DropTargetDragEvent event )
{
event.acceptDrag ( event.getDropAction () );
this.dropPoint = event.getLocation ();
repaint ();
}
/**
* {@inheritDoc}
*
* @see DropTargetListener#dragExit(DropTargetEvent)
*/
public final void dragExit (
@SuppressWarnings ( "unused" ) DropTargetEvent event )
{
this.dropPoint = null;
repaint ();
}
/**
* {@inheritDoc}
*
* @see DropTargetListener#dragOver(DropTargetDragEvent)
*/
public final void dragOver ( DropTargetDragEvent event )
{
try
{
JGTIListModelRows rows = ( JGTIListModelRows ) event.getTransferable ()
.getTransferData ( JGTIListModelRowsTransferable.dataFlavor );
if ( !this.allowedDndSources.contains ( rows.getSource () ) )
{
event.rejectDrag ();
this.dropPoint = null;
repaint ();
return;
}
}
catch ( UnsupportedFlavorException exc )
{
event.rejectDrag ();
this.dropPoint = null;
repaint ();
return;
}
catch ( IOException exc )
{
event.rejectDrag ();
this.dropPoint = null;
repaint ();
return;
}
event.acceptDrag ( event.getDropAction () );
this.dropPoint = event.getLocation ();
repaint ();
}
/**
* {@inheritDoc}
*
* @see DropTargetListener#drop(DropTargetDropEvent)
*/
public final void drop ( DropTargetDropEvent event )
{
event.acceptDrop ( event.getDropAction () );
try
{
Transferable transferable = event.getTransferable ();
event.dropComplete ( getTransferHandler ().importData ( this,
transferable ) );
}
catch ( RuntimeException exc )
{
event.dropComplete ( false );
}
this.dropPoint = null;
repaint ();
}
/**
* {@inheritDoc}
*
* @see DropTargetListener#dropActionChanged(DropTargetDragEvent)
*/
public final void dropActionChanged ( DropTargetDragEvent event )
{
event.acceptDrag ( event.getDropAction () );
this.dropPoint = event.getLocation ();
repaint ();
}
/**
* Returns the drag and drop mode of this {@link JGTIList}.
*
* @return The drag and drop mode of this {@link JGTIList}.
* @see #setDndMode(int)
*/
public final int getDndMode ()
{
return this.dndMode;
}
/**
* Returns the drop point.
*
* @return The drop point.
*/
public final Point getDropPoint ()
{
return this.dropPoint;
}
/**
* Initializes this {@link JComponent}.
*/
private final void init ()
{
setSelectionMode ( ListSelectionModel.SINGLE_SELECTION );
this.allowedDndSources = new ArrayList < JComponent > ();
// must be removed because of problems with the drag and drop
for ( MouseMotionListener current : getMouseMotionListeners () )
{
removeMouseMotionListener ( current );
}
// store the drag and drop allowed state
addMouseListener ( new MouseAdapter ()
{
@Override
public void mousePressed ( MouseEvent event )
{
int rowIndex = locationToIndex ( event.getPoint () );
Rectangle rect = getCellBounds ( rowIndex, rowIndex );
if ( ( rect == null )
|| ( event.getPoint ().getY () > rect.y + rect.height ) )
{
JGTIList.this.dragAndDropAllowed = false;
}
else
{
JGTIList.this.dragAndDropAllowed = true;
}
}
} );
// swing bugfix
addMouseMotionListener ( new MouseMotionAdapter ()
{
/**
* {@inheritDoc}
*
* @see MouseMotionAdapter#mouseDragged(MouseEvent)
*/
@Override
public void mouseDragged ( MouseEvent event )
{
if ( getDragEnabled ()
&& ( ( event.getModifiers () & InputEvent.BUTTON1_MASK ) != 0 )
&& JGTIList.this.dragAndDropAllowed )
{
TransferHandler transferHandler = getTransferHandler ();
transferHandler.exportAsDrag ( JGTIList.this, event, transferHandler
.getSourceActions ( JGTIList.this ) );
event.consume ();
}
}
} );
setDropTarget ( new DropTarget ( this, this ) );
// disable cut, copy and paste
getActionMap ().put ( "cut", new AbstractAction () { //$NON-NLS-1$
/**
* The serial version uid.
*/
private static final long serialVersionUID = -4319963661932864508L;
public void actionPerformed (
@SuppressWarnings ( "unused" ) ActionEvent e )
{
// do nothing
}
} );
getActionMap ().put ( "copy", new AbstractAction () { //$NON-NLS-1$
/**
* The serial version uid.
*/
private static final long serialVersionUID = 2449576847404790643L;
public void actionPerformed (
@SuppressWarnings ( "unused" ) ActionEvent e )
{
// do nothing
}
} );
getActionMap ().put ( "paste", new AbstractAction () { //$NON-NLS-1$
/**
* The serial version uid.
*/
private static final long serialVersionUID = -7404714019259587536L;
public void actionPerformed (
@SuppressWarnings ( "unused" ) ActionEvent e )
{
// do nothing
}
} );
}
/**
* {@inheritDoc}
*
* @see javax.swing.JList#locationToIndex(java.awt.Point)
*/
@Override
public int locationToIndex ( Point p )
{
if ( getModel ().getSize () == 0 )
{
return super.locationToIndex ( p );
}
Rectangle r = getCellBounds ( 0, getModel ().getSize () - 1 );
if ( r.contains ( p ) )
{
return super.locationToIndex ( p );
}
return getModel ().getSize ();
}
/**
* {@inheritDoc}
*
* @see JComponent#paintComponent(Graphics)
*/
@Override
protected final void paintComponent ( Graphics graphics )
{
super.paintComponent ( graphics );
if ( this.dropPoint != null )
{
if ( this.dndMode == DROP_INTO )
{
Rectangle rect = getVisibleRect ();
graphics.setColor ( Color.BLACK );
graphics.drawRect ( 0, rect.y, rect.width - 1, rect.height - 1 );
}
else if ( this.dndMode == DROP_BETWEEN )
{
int rowIndex = locationToIndex ( this.dropPoint );
Rectangle rect = getCellBounds ( rowIndex, rowIndex );
if ( ( rect == null )
|| ( this.dropPoint.getY () > rect.y + rect.height ) )
{
rowIndex = getModel ().getSize ();
}
int y = 0;
for ( int i = 0 ; i < rowIndex ; i++ )
{
y += getCellBounds ( i, i ).height; // getRowHeight(rowIndex);
}
if ( y > 0 )
{
y -= 1;
}
int width = getWidth () - 1;
int size = 3;
// Color
graphics.setColor ( Color.BLACK );
// Line
graphics.drawLine ( size, y, width - size, y );
graphics.drawLine ( size, y + 1, width - size, y + 1 );
// Left upper
graphics.drawLine ( size, y, 0, y - size );
graphics.drawLine ( size, y + 1, 0, y - size + 1 );
// Left lower
graphics.drawLine ( size, y, 0, y + size );
graphics.drawLine ( size, y + 1, 0, y + size + 1 );
// Right upper
graphics.drawLine ( width - size, y, width, y - size );
graphics.drawLine ( width - size, y + 1, width, y - size + 1 );
// Right lower
graphics.drawLine ( width - size, y, width, y + size );
graphics.drawLine ( width - size, y + 1, width, y + size + 1 );
}
}
}
/**
* Removes the given {@link JComponent} from the allowed drag and drop
* sources.
*
* @param jComponent The {@link JComponent} to remove.
*/
public final void removeAllowedDndSource ( JComponent jComponent )
{
this.allowedDndSources.remove ( jComponent );
}
/**
* Sets the drag and drop mode of this {@link JGTIList}.
*
* @param dndMode The new drag and drop mode.
*/
public final void setDndMode ( int dndMode )
{
if ( ( dndMode != DROP_INTO ) && ( dndMode != DROP_BETWEEN ) )
{
throw new IllegalArgumentException ( "dnd mode is invalid" ); //$NON-NLS-1$
}
this.dndMode = dndMode;
}
}