Monday, June 1, 2009

Adding a JPanel in JTable's cell

Following sample code displays a JTable with 2 rows and a sincle column.
Each cell containing a JPanel with two JButtons and each of it is linked to its own action.

PS : Setting tool tips for the buttons inside JPanel in each cell is not possible directly. It may be achieved by calculating the static position of each buttin and compare it with the mouse position and show a tooltip.


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class JButtonTableExample extends JFrame {

public JButtonTableExample() {
super( "JButtonTable Example" );

DefaultTableModel dm = new DefaultTableModel();
dm.setDataVector( new Object[][] { {"1" }, {"2" } }, new Object[] {"Buttons" } );

JTable table = new JTable( dm );
table.getColumn( "Buttons" ).setCellRenderer( new PanelRenderer() );
table.getColumn( "Buttons" ).setCellEditor( new PanelEditor( new JCheckBox() ) );
JScrollPane scroll = new JScrollPane( table );
table.setRowHeight( 30 );
getContentPane().add( scroll );
setSize( 400, 150 );
setVisible( true );
}

public static void main( String[] args ) {
JButtonTableExample frame = new JButtonTableExample();
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
System.exit( 0 );
}
} );
}

class PanelRenderer extends JPanel implements TableCellRenderer {

public PanelRenderer() {
setOpaque( true );
init();
}

private void init() {
setLayout( new FlowLayout( FlowLayout.CENTER, 5, 0 ) );
add( new JButton( "A" ) );
add( new JButton( "B" ) );
}

public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) {
if ( isSelected )
{
setForeground( table.getSelectionForeground() );
setBackground( table.getSelectionBackground() );
} else
{
setForeground( table.getForeground() );
setBackground( UIManager.getColor( "Button.background" ) );
}
return this;
}
}

class PanelEditor extends DefaultCellEditor {
protected JPanel panel;

protected JButton button1;

protected JButton button2;

public PanelEditor( JCheckBox checkBox ) {
super( checkBox );
panel = new JPanel( new FlowLayout( FlowLayout.CENTER, 5, 0 ) );
button1 = new JButton( "1" );
button1.setOpaque( true );
button1.setActionCommand( "Action1" );
button1.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e ) {
JOptionPane.showMessageDialog( button1, button1.getText()
+ ": Ouch!" );
fireEditingStopped();
}
} );
button2 = new JButton( "2" );
button2.setOpaque( true );
button2.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e ) {
JOptionPane.showMessageDialog( button2, button2.getText()
+ ": Ouch!" );
fireEditingStopped();
}
} );
panel.add( button1 );
panel.add( button2 );
}

public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column ) {
if ( isSelected )
{
button1.setForeground( table.getSelectionForeground() );
button1.setBackground( table.getSelectionBackground() );
} else
{
button1.setForeground( table.getForeground() );
button1.setBackground( table.getBackground() );
}
return panel;
}
}
}

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.