/*
* org.openmicroscopy.shoola.util.ui.PlateGrid
*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2009 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.util.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
/**
* Displays a grid.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since 3.0-Beta4
*/
public class PlateGrid
extends JTable
{
/** Indicates the row or column is an ascending letter. */
public static final int ASCENDING_LETTER = 0;
/** Indicates the row or column is an ascending number. */
public static final int ASCENDING_NUMBER = 1;
/** The default number of rows. */
public final static int DEFAULT_ROWS = 16;
/** The default number of columns. */
public final static int DEFAULT_COLUMNS = 24;
/** Bound property indicating that a well is selected. */
public static final String WELL_FIELDS_PROPERTY = "wellFields";
/** The background color of a cell. */
public final static Color BACKGROUND_COLOR = Color.WHITE;
/** The color of the grid in the table. */
public final static Color GRID_COLOR = new Color(180, 213, 255);
/** The color of the selected cell. */
public final static Color SELECTED_COLOR = new Color(255, 206, 206);
/** The color of the focused cell. */
public final static Color FOCUS_COLOR = new Color(255, 135, 135);
/** The default size of a cell. */
private final static Dimension CELL_SIZE = new Dimension(10, 10);
/** One of the constants defined by this class. */
private int typeRow;
/** One of the constants defined by this class. */
private int typeColumn;
/** Hosts the valid wells. */
private List<WellGridElement> validValues;
/** Identifies the selected cells. */
private List<Point> selectedCells;
/**
* Returns <code>true</code> if the cell is in the table range,
* <code>false</code> otherwise.
*
* @param row The row identifying the cell.
* @param column The column identifying the cell.
* @return See above.
*/
private boolean isCellInRange(int row, int column)
{
return (!(row < 0 || row >= getModel().getRowCount() ||
column < 0 || column >= getModel().getColumnCount()));
}
/**
* Initializes the component.
*
* @param rows The number of rows.
* @param columns The number of columns.
*/
private void initialize(int rows, int columns)
{
selectedCells = new ArrayList<Point>();
setTableHeader(null);
setModel(new GridModel(rows, columns));
TableColumn col;
int width = CELL_SIZE.width;
for (int i = 0 ; i < getColumnCount(); i++) {
col = getColumnModel().getColumn(i);
col.setMinWidth(width);
col.setMaxWidth(width);
col.setPreferredWidth(width);
col.setResizable(false);
}
setCellSelectionEnabled(true);
setRowHeight(CELL_SIZE.height);
setDefaultRenderer(Object.class, new GridRenderer(this));
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
setGridColor(GRID_COLOR);
addMouseListener(new MouseAdapter() {
/**
* Loads the fields for the selected well.
* @see MouseAdapter#mouseClicked(MouseEvent)
*/
public void mouseClicked(MouseEvent e)
{
Point p = e.getPoint();
int row = rowAtPoint(p);
int column = columnAtPoint(p);
if (row < 0 || column < 0) return;
boolean b = e.isShiftDown() || e.isControlDown();
if (UIUtilities.isMacOS())
b = e.isShiftDown() || e.isMetaDown();
Point selected = findSelectedPoint(row, column);
if (selected != null && selectedCells.size() == 1) return;
firePropertyChange(WELL_FIELDS_PROPERTY, null,
new PlateGridObject(row, column, b));
}
});
}
/**
* Returns the points corresponding to the specified row and column in the
* list of selected nodes.
*
* @param row The row identifying the cell.
* @param column The column identifying the cell.
* @return See above.
*/
private Point findSelectedPoint(int row, int column)
{
if (!isCellInRange(row, column)) return null;
Iterator<Point> i = selectedCells.iterator();
Point p;
while (i.hasNext()) {
p = i.next();
if (p.x == row && p.y == column)
return p;
}
return null;
}
/**
* Creates a default instance.
*
* @param typeRow One of the constants defined by this class.
* @param typeColumn One of the constants defined by this class.
* @param values Host the valid wells.
* @param rows The maximum number of rows.
* @param columns The maximum number of columns.
*/
public PlateGrid(int typeRow, int typeColumn, List<WellGridElement> values,
int rows, int columns)
{
this.typeColumn = typeColumn;
this.typeRow = typeRow;
this.validValues = values;
initialize(DEFAULT_ROWS, DEFAULT_COLUMNS);
}
/**
* Creates a default instance.
*
* @param typeRow One of the constants defined by this class.
* @param typeColumn One of the constants defined by this class.
* @param values Host the valid wells.
*/
public PlateGrid(int typeRow, int typeColumn, List<WellGridElement> values)
{
this(typeRow, typeColumn, values, DEFAULT_ROWS, DEFAULT_COLUMNS);
}
/**
* Creates a default instance, one row and multiple column.
*
* @param columns The number of columns.
*/
public PlateGrid(int columns)
{
validValues = new ArrayList<WellGridElement>();
for (int i = 0; i < columns; i++)
validValues.add(new WellGridElement(0, i));
initialize(1, columns);
}
/**
* Sets the selected cell.
*
* @param row The row identifying the cell.
* @param column The column identifying the cell.
*/
public void selectCell(int row, int column)
{
selectCells(Arrays.asList(new Point(row, column)));
}
/**
* Sets the selected cells.
*
* @param cells The selected cells.
*/
public void selectCells(List<Point> cells)
{
selectedCells.clear();
if (cells == null) return;
Iterator<Point> i = cells.iterator();
Point p;
while (i.hasNext()) {
p = i.next();
if (isCellInRange(p.x, p.y) && isCellValid(p.x, p.y) &&
!isSelectedCell(p.x, p.y))
selectedCells.add(p);
}
repaint();
}
/**
* Returns <code>true</code> if the passed cell contains a valid well,
* <code>false</code> otherwise.
*
* @param row The row of the selected cell.
* @param column The column of the selected cell.
* @return See above.
*/
boolean isCellValid(int row, int column)
{
if (validValues == null) return false;
Iterator<WellGridElement> i = validValues.iterator();
WellGridElement well;
while (i.hasNext()) {
well = i.next();
if (well.getRow() == row && well.getColumn() == column)
return well.isValid();
}
return false;
}
/**
* Returns <code>true</code> if the passed cell is the displayed one,
* <code>false</code> otherwise.
*
* @param row The row identifying the cell.
* @param column The column identifying the cell.
* @return See above.
*/
boolean isSelectedCell(int row, int column)
{
if (!isCellInRange(row, column)) return false;
Iterator<Point> i = selectedCells.iterator();
Point p;
while (i.hasNext()) {
p = i.next();
if (p.x == row && p.y == column)
return true;
}
return false;
}
/**
* Returns the displayed for the tool tip.
*
* @param row The row to handle.
* @param column The column to handle.
* @return See above.
*/
String getCellToolTip(int row, int column)
{
String r = "";
String c = "";
row = row+1;
column = column+1;
if (typeRow == ASCENDING_LETTER)
r = UIUtilities.LETTERS.get(row);
else r = ""+row;
if (typeColumn == ASCENDING_LETTER)
c = UIUtilities.LETTERS.get(row);
else c = ""+column;
return r+"-"+c;
}
/**
* Inner class to render the cell.
*/
class GridRenderer
extends DefaultTableCellRenderer
{
/** Reference to the model. */
private PlateGrid model;
/**
* Creates a new instance.
*
* @param model Reference to the model.
*/
GridRenderer(PlateGrid model)
{
this.model = model;
}
/**
* Overridden to set the color of the selected cell.
*/
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column)
{
setToolTipText(model.getCellToolTip(row, column));
if (model.isCellValid(row, column)) {
if (model.isSelectedCell(row, column))
setBackground(FOCUS_COLOR);
else setBackground(SELECTED_COLOR);
} else {
setBackground(BACKGROUND_COLOR);
}
return this;
}
}
}