Moved table UI classes into their own package
This commit is contained in:
parent
ccff62d261
commit
6ca3fad77b
6 changed files with 161 additions and 98 deletions
|
@ -0,0 +1,117 @@
|
|||
package net.vhati.modmanager.ui.table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import net.vhati.modmanager.core.ModInfo;
|
||||
import net.vhati.modmanager.ui.table.Reorderable;
|
||||
|
||||
|
||||
public class ChecklistTableModel<T> extends AbstractTableModel implements Reorderable {
|
||||
|
||||
private static final int COLUMN_CHECK = 0;
|
||||
private static final int COLUMN_PAYLOAD = 1;
|
||||
|
||||
private static final int DATA_CHECK = 0;
|
||||
private static final int DATA_PAYLOAD = 1;
|
||||
|
||||
private String[] columnNames = new String[] {"?", "Name"};
|
||||
private Class[] columnTypes = new Class[] {Boolean.class, String.class};
|
||||
|
||||
private List<List<Object>> rowsList = new ArrayList<List<Object>>();
|
||||
|
||||
|
||||
public void addItem( T o ) {
|
||||
insertItem( rowsList.size(), false, o );
|
||||
}
|
||||
|
||||
public void insertItem( int row, boolean selected, T o ) {
|
||||
int newRowIndex = rowsList.size();
|
||||
|
||||
List<Object> rowData = new ArrayList<Object>();
|
||||
rowData.add( new Boolean(selected) );
|
||||
rowData.add( o );
|
||||
rowsList.add( row, rowData );
|
||||
|
||||
fireTableRowsInserted( row, row );
|
||||
}
|
||||
|
||||
public void removeItem( int row ) {
|
||||
rowsList.remove( row );
|
||||
fireTableRowsDeleted( row, row );
|
||||
}
|
||||
|
||||
public void removeAllItems() {
|
||||
rowsList.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T getItem( int row ) {
|
||||
return (T)rowsList.get(row).get(DATA_PAYLOAD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reorder( int fromRow, int toRow ) {
|
||||
if ( toRow > fromRow ) toRow--;
|
||||
List<Object> rowData = rowsList.get( fromRow );
|
||||
rowsList.remove( fromRow );
|
||||
fireTableRowsDeleted( fromRow, fromRow );
|
||||
rowsList.add( toRow, rowData );
|
||||
fireTableRowsInserted( toRow, toRow );
|
||||
}
|
||||
|
||||
public void setSelected( int row, boolean b ) {
|
||||
rowsList.get(row).set( DATA_CHECK, new Boolean(b) );
|
||||
fireTableRowsUpdated( row, row );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isSelected( int row ) {
|
||||
return ((Boolean)rowsList.get(row).get(DATA_CHECK)).booleanValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return columnNames.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return rowsList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt( int row, int column ) {
|
||||
if ( column == COLUMN_CHECK ) {
|
||||
return rowsList.get(row).get(DATA_CHECK);
|
||||
}
|
||||
else if ( column == COLUMN_PAYLOAD ) {
|
||||
Object o = rowsList.get(row).get(DATA_PAYLOAD);
|
||||
return o.toString();
|
||||
}
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setValueAt( Object o, int row, int column ) {
|
||||
if ( column == COLUMN_CHECK ) {
|
||||
Boolean bool = (Boolean)o;
|
||||
rowsList.get(row).set( DATA_CHECK, bool );
|
||||
fireTableRowsUpdated( row, row );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable( int row, int column ) {
|
||||
if ( column == COLUMN_CHECK ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getColumnClass( int column ) {
|
||||
return columnTypes[column];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package net.vhati.modmanager.ui.table;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.DropMode;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
|
||||
import net.vhati.modmanager.core.ModFileInfo;
|
||||
import net.vhati.modmanager.ui.table.ChecklistTableModel;
|
||||
import net.vhati.modmanager.ui.table.TableRowTransferHandler;
|
||||
|
||||
|
||||
public class ChecklistTablePanel<T> extends JPanel {
|
||||
|
||||
protected ChecklistTableModel<T>tableModel;
|
||||
protected JTable table;
|
||||
|
||||
|
||||
public ChecklistTablePanel() {
|
||||
super( new BorderLayout() );
|
||||
|
||||
tableModel = new ChecklistTableModel<T>();
|
||||
|
||||
table = new JTable( tableModel );
|
||||
table.setFillsViewportHeight( true );
|
||||
table.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
|
||||
table.setTableHeader( null );
|
||||
table.getColumnModel().getColumn(0).setMinWidth(30);
|
||||
table.getColumnModel().getColumn(0).setMaxWidth(30);
|
||||
table.getColumnModel().getColumn(0).setPreferredWidth(30);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane( null, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
|
||||
scrollPane.setViewportView( table );
|
||||
//scrollPane.setColumnHeaderView( null ); // Counterpart to setTableHeader().
|
||||
scrollPane.setPreferredSize( new Dimension(Integer.MIN_VALUE, Integer.MIN_VALUE) );
|
||||
this.add( scrollPane, BorderLayout.CENTER );
|
||||
|
||||
|
||||
// Double-click toggles checkboxes.
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
int prevRow = -1;
|
||||
int streak = 0;
|
||||
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e ) {
|
||||
if ( e.getSource() != table ) return;
|
||||
int thisRow = table.rowAtPoint( e.getPoint() );
|
||||
|
||||
// Reset on first click and when no longer on that row.
|
||||
if ( e.getClickCount() == 1 ) prevRow = -1;
|
||||
if ( thisRow != prevRow || thisRow == -1 ) {
|
||||
streak = 1;
|
||||
prevRow = thisRow;
|
||||
return;
|
||||
} else {
|
||||
streak++;
|
||||
}
|
||||
if ( streak % 2 != 0 ) return; // Respond only to click pairs.
|
||||
|
||||
// Don't further toggle a multi-clicked checkbox.
|
||||
int viewCol = table.columnAtPoint( e.getPoint() );
|
||||
int modelCol = table.getColumnModel().getColumn(viewCol).getModelIndex();
|
||||
if ( modelCol == 0 ) return;
|
||||
|
||||
int selRow = table.getSelectedRow();
|
||||
if ( selRow != -1 ) {
|
||||
boolean selected = tableModel.isSelected( selRow );
|
||||
tableModel.setSelected( selRow, !selected );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
table.setTransferHandler( new TableRowTransferHandler( table ) );
|
||||
table.setDropMode( DropMode.INSERT ); // Drop between rows, not on them.
|
||||
table.setDragEnabled( true );
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
tableModel.removeAllItems();
|
||||
}
|
||||
|
||||
|
||||
public List<T> getAllItems() {
|
||||
List<T> results = new ArrayList<T>();
|
||||
|
||||
for ( int i=0; i < tableModel.getRowCount(); i++ ) {
|
||||
results.add( tableModel.getItem(i) );
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<T> getSelectedItems() {
|
||||
List<T> results = new ArrayList<T>();
|
||||
|
||||
for ( int i=0; i < tableModel.getRowCount(); i++ ) {
|
||||
if ( tableModel.isSelected(i) ) {
|
||||
results.add( tableModel.getItem(i) );
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
public void toggleAllItemSelection() {
|
||||
int selectedCount = 0;
|
||||
for ( int i = tableModel.getRowCount()-1; i >= 0; i-- ) {
|
||||
if ( tableModel.isSelected(i) ) selectedCount++;
|
||||
}
|
||||
boolean b = ( selectedCount != tableModel.getRowCount() );
|
||||
|
||||
for ( int i = tableModel.getRowCount()-1; i >= 0; i-- ) {
|
||||
tableModel.setSelected( i, b );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ChecklistTableModel<T> getTableModel() {
|
||||
return tableModel;
|
||||
}
|
||||
|
||||
public JTable getTable() {
|
||||
return table;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package net.vhati.modmanager.ui.table;
|
||||
|
||||
|
||||
public interface Reorderable {
|
||||
/**
|
||||
* Moves an element at fromIndex to toIndex.
|
||||
*/
|
||||
public void reorder( int fromIndex, int toIndex );
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package net.vhati.modmanager.ui.table;
|
||||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.DragSource;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.TransferHandler;
|
||||
|
||||
import net.vhati.modmanager.ui.table.Reorderable;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Allows drag and drop reordering of JTable rows.
|
||||
*
|
||||
* Its TableModel must implement the Reorderable interface.
|
||||
*/
|
||||
public class TableRowTransferHandler extends TransferHandler {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(TableRowTransferHandler.class);
|
||||
|
||||
private DataFlavor localIntegerFlavor = null;
|
||||
|
||||
private JTable table = null;
|
||||
|
||||
|
||||
public TableRowTransferHandler( JTable table ) {
|
||||
super();
|
||||
if ( table.getModel() instanceof Reorderable == false ) {
|
||||
throw new IllegalArgumentException( "The tableModel doesn't implement Reorderable." );
|
||||
}
|
||||
this.table = table;
|
||||
|
||||
try {
|
||||
localIntegerFlavor = new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType + ";class=\""+ Integer.class.getName() +"\"" );
|
||||
}
|
||||
catch ( ClassNotFoundException e ) {
|
||||
log.error( e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transferable createTransferable( JComponent c ) {
|
||||
assert ( c == table );
|
||||
int row = table.getSelectedRow();
|
||||
return new IntegerTransferrable( new Integer(row) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canImport( TransferHandler.TransferSupport ts ) {
|
||||
boolean b = ( ts.getComponent() == table && ts.isDrop() && ts.isDataFlavorSupported(localIntegerFlavor) );
|
||||
table.setCursor( b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop );
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceActions( JComponent comp ) {
|
||||
return TransferHandler.MOVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("Unchecked")
|
||||
public boolean importData( TransferHandler.TransferSupport ts ) {
|
||||
if ( !canImport(ts) ) return false;
|
||||
|
||||
JTable target = (JTable)ts.getComponent();
|
||||
JTable.DropLocation dl = (JTable.DropLocation)ts.getDropLocation();
|
||||
int dropRow = dl.getRow();
|
||||
int rowCount = table.getModel().getRowCount();
|
||||
if ( dropRow < 0 || dropRow > rowCount ) dropRow = rowCount;
|
||||
|
||||
target.setCursor( Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR) );
|
||||
try {
|
||||
Integer draggedRow = (Integer)ts.getTransferable().getTransferData(localIntegerFlavor);
|
||||
if ( draggedRow != -1 && draggedRow != dropRow ) {
|
||||
((Reorderable)table.getModel()).reorder( draggedRow, dropRow );
|
||||
if ( dropRow > draggedRow ) dropRow--;
|
||||
target.getSelectionModel().addSelectionInterval( dropRow, dropRow );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
log.error( e );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void exportDone( JComponent source, Transferable data, int action ) {
|
||||
if ( action == TransferHandler.MOVE || action == TransferHandler.NONE ) {
|
||||
table.setCursor( Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Drag and drop Integer data, constructed with a raw object
|
||||
* from a drag source, to be transformed into a flavor
|
||||
* suitable for the drop target.
|
||||
*/
|
||||
private class IntegerTransferrable implements Transferable {
|
||||
private Integer data;
|
||||
|
||||
public IntegerTransferrable( Integer data ) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransferData( DataFlavor flavor ) {
|
||||
if ( flavor.equals( localIntegerFlavor ) ) {
|
||||
return data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return new DataFlavor[] {localIntegerFlavor};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataFlavorSupported( DataFlavor flavor ) {
|
||||
return flavor.equals( localIntegerFlavor );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue