Tuesday, September 2, 2008

Easy drag and drop in JList using MouseMotionListener without using TransferHandlers

Easy way to drag and drop in JList is to use MouseMotionListeners rather than using TransferHandlers.

Following is a demo program displays list box in which the order of the items can be changed by dnd.


public class DragDropDemo extends JPanel {

int index = -1;
String value = "";
JList list;
JScrollPane listScroll;
MouseDragDropListener mouseDragDropListener;
DefaultListModel listModel = new DefaultListModel();

public DragDropDemo() {
listModel.addElement( "0 (list 1)" );
listModel.addElement( "1 (list 1)" );
listModel.addElement( "2 (list 1)" );
listModel.addElement( "3 (list 1)" );
listModel.addElement( "4 (list 1)" );
listModel.addElement( "5 (list 1)" );
listModel.addElement( "6 (list 1)" );
list = new JList( listModel );
list.setSelectionMode(
ListSelectionModel.SINGLE_INTERVAL_SELECTION );
mouseDragDropListener = new MouseDragDropListener();
list.addMouseMotionListener( mouseDragDropListener );
list.addMouseListener( mouseDragDropListener );
listScroll = new JScrollPane( list );
setLayout( new BorderLayout() );
add( listScroll, BorderLayout.CENTER );
setPreferredSize( new Dimension(200,200) );
}

/**
* Create the GUI and show it. For thread safety, this method
* should be invoked from the event-dispatching thread.
*/
private static void createAndShowGUI() {
JFrame.setDefaultLookAndFeelDecorated( true );
JFrame frame = new JFrame( "DragDropDemo" );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
DragDropDemo demo = new DragDropDemo();
frame.setContentPane( demo );
frame.pack();
frame.setVisible( true );
}

public static void main( String[] args ) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater( new Runnable() {
public void run() {
createAndShowGUI();
}
} );
}


/**
* Listener class to implement Drag and Drop functionality
*
*/
private class MouseDragDropListener extends MouseInputAdapter
{

/**
* Invoked when a mouse button is pressed on a component and then
* dragged
*/
public void mouseDragged(MouseEvent dragEvent)
{
if ( dragEvent.getSource() instanceof JList )
{
if ( dragEvent.getY() >= 0 &&
dragEvent.getY() <= list.getHeight()
&& dragEvent.getX() >= 0 &&
dragEvent.getX() <= list.getWidth() )
{
((JComponent) dragEvent.getSource()).
setCursor( new Cursor( Cursor.HAND_CURSOR ) );
drawField( value, dragEvent.getX(),
dragEvent.getY(), 150, 20 );
} else
{
((JComponent) dragEvent.getSource()).
setCursor( Cursor.getDefaultCursor() );
}
listScroll.repaint();
}
}

/**
* Method to draw a drag tooltip box at the given point
*/
private void drawField( String name, int xX, int yY, int width, int height )
{
final int x = xX;
final int y = yY;
final int w = width;
final int h = height;
final String fname = name;
Thread t = new Thread() {
public void run() {
Graphics g = list.getGraphics();
g.drawRect( x, y, w, h );
g.drawString( fname, x + 10, y + 15 );
}
};
t.start();
}

/**
* Invoked when a mouse button is pressed on a component
*
*/
public void mousePressed( MouseEvent pressEvent )
{
if ( pressEvent.getSource() instanceof JList )
{
JList list = (JList) pressEvent.getComponent();
index = list.getSelectedIndex();
if ( index >= 0 )
{
index = list.locationToIndex(
pressEvent.getPoint() );
}
value = list.getSelectedValue().toString();
list.setSelectedIndex( index );
}
}

/**
* Invoked when a mouse button is released on a component
* Passes the curIndex to moveToIndex method to move the
* dragginIndex.
*
*/
public void mouseReleased( MouseEvent releaseEvent )
{
if ( releaseEvent.getSource() instanceof JList )
{
if ( releaseEvent.getModifiers() > 16 )
{
return;
}
((JComponent) releaseEvent.getSource()).
setCursor( new Cursor( Cursor.DEFAULT_CURSOR ) );
int curIndex = list.locationToIndex(
releaseEvent.getPoint() );
if ( curIndex == -1 )
{
if ( list.getModel().getSize()>0 )
{
if ( releaseEvent.getY() >=
list.getHeight() )
{
curIndex =
list.getModel().getSize() - 1;
} else if ( releaseEvent.getY()<=0)
{
curIndex = 0;
} else
{
curIndex = index;
}
} else
{
return;
}
}
if ( curIndex == index )
{
return;
}
moveToIndex( curIndex );
list.repaint();
listScroll.repaint();
}
}

/**
* Move the dragginField to the curIndex position.
* Where curIndex position is set to the index where mouse released.
*
* @param curIndex
*/
public void moveToIndex( int curIndex )
{
int listSize = list.getModel().getSize();
int moveToPos = curIndex + 1;
if ( moveToPos != 0 && value != null )
{
int newPos = moveToPos;
if ( moveToPos > 0 )
{
if ( newPos > listSize )
{
newPos = listSize;
}
newPos--;
} else if ( newPos < 0 )
{
newPos = 0;
}

listModel.remove( index );
listModel.add( newPos, value );
list.setSelectedIndex( newPos );
}
index = 0;
value = null;
}
}
}


Point to note:
When a MouseMotionListener is set for a Jlist component to perform drag and drop manually, then

list.setDragEnabled( true ); - to enable dragging in Jlist

should not be given. Since enabling this will consume the source mouse event in Jlist and that event will be sent to mousedrag event and hence will does nothing.