package org.biolegato.tables;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableModel;
import org.biolegato.main.BLMain;
import org.biolegato.main.DataCanvas;
import org.biopcd.parser.PCD;
/*
* TableCanvas.java
*
* Created on November 15, 2010, 4:08 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
/**
* BioLegato's table canvas.
**
* @author Graham Alvare
* @author Brian Fristensky
*/
public class TableCanvas extends DataCanvas {
/**
* The selection modes available for the table canvas. There are currently
* three modes available to the user. The purpose of selection modes is to
* allow the user to click on individual rows or columns, easily, in the
* table canvas. To see this in action, please run the table canvas.
* (The selection modes are easier to explain visually than through text.)
*/
public static enum SelectionMode {
/**
* In this mode, the user selects individual cells in the canvas.
*/
CELL,
/**
* In this mode, the user selects individual columns in the canvas.
*/
COLUMN,
/**
* In this mode, the user selects individual rows in the canvas.
*/
ROW;
};
/**
* Stores whether or not the data within the canvas is editable
* (this is read from BioLegato's properties).
*/
final boolean editable = !"true".equalsIgnoreCase(getProperty("readonly"));
/**
* Stores whether or not the data within the canvas has generic column
* headers (i.e. A, B, C, ...) or named headers (the first row of any data
* imported into the table canvas). True means named headers are used
* instead of generic column headers.
*/
final boolean namedHeaders
= !"true".equalsIgnoreCase(getProperty("table.generic_headers"));
/**
* The table model to store the canvas content.
*/
DefaultTableModel tableModel = new DefaultTableModel() {
/**
* Tests whether a cell within the table model is editable. This method
* is customized to first test whether BioLegato's readonly parameter is
* enabled. If the readonly parameter is set in BioLegato's properties,
* then none of the cells in the table are editable.
**
* @param row the row index of the cell to test.
* @param col the column index of the cell to test.
* @return whether the cell is editable (true = yes, false = no).
*/
@Override
public boolean isCellEditable(int row, int col) {
return editable && super.isCellEditable(row, col);
}
};
/**
* The actual JTable object for displaying the table canvas data.
*/
final JTable tablePane = new JTable(tableModel);
/**
* Stores the current row header model for the table. This is used to
* display row numbers (and row headers in the future).
*/
private TableRowModel rowHeaderModel = new TableRowModel(tableModel);
/**
* The file filter object for reading in CSV files.
*/
private static final FileFilter CSV_FILTER = new CSVFile();
/**
* The file filter object for reading in TSV files.
*/
private static final FileFilter TSV_FILTER = TSVFile.TSV_FILTER;
/**
* An empty two-dimensional string array. This is used as the default
* return value for the function getData.
*/
private static final String[][] EMPTY_SET = new String[0][0];
/**
* The menu item "Select All"
*/
public final AbstractAction selectAllA = new AbstractAction("Select All") {
/**
* Serialization number - required for no warnings
*/
private static final long serialVersionUID = 7526472295622777033L;
/**
* Sets the mnemonic for the event.
*/
{
putValue(MNEMONIC_KEY, new Integer(java.awt.event.KeyEvent.VK_A));
}
/**
* Event handler - select all of the cells in the canvas.
**
* @param evt ignored by this method.
*/
public void actionPerformed(java.awt.event.ActionEvent evt) {
tablePane.selectAll();
}
};
SelectionMode mode = SelectionMode.CELL;
/**
* Creates a new instance of a TableCanvas. This is the most basic
* constructor available.
*/
public TableCanvas() {
super();
// Add the "Select All..." menu item.
addMenuHeading("Edit").add(new JMenuItem(selectAllA));
}
/**
* Creates a new instance of TableCanvas. This constructor will accept
* a Map of values for BioLegato properties.
**
* @param props the BioLegato properties to initialize the canvas with.
* This parameter is ignored if null.
*/
public TableCanvas(Map extends Object, ? extends Object> props) {
super(props);
// Add the "Select All..." menu item.
addMenuHeading("Edit").add(new JMenuItem(selectAllA));
}
/**
* Creates a new instance of TableCanvas, reading results from an SQL query
* into the table, by default.
**
* @param rs the results of an SQL query; these are to be used as the
* initial contents of the table canvas.
*/
protected TableCanvas(ResultSet rs) {
this(null, rs);
}
/**
* Creates a new instance of TableCanvas, reading results from an SQL query
* into the table, by default. This constructor will also accept a Map of
* values for BioLegato properties.
**
* @param props the BioLegato properties to initialize the canvas with.
* This parameter is ignored if null.
* @param rs the results of an SQL query; these are to be used as the
* initial contents of the table canvas.
*/
public TableCanvas(Map extends Object, ? extends Object> props,
ResultSet rs) {
this(props);
// Parse the SQL query.
try {
// Obtain the metadata from the SQL query result set.
ResultSetMetaData meta = rs.getMetaData();
// Determine the number of columns in the result set.
int colmax = meta.getColumnCount();
// Create a new array to cache each row while extracting each row
// from the result set.
String[] record = new String[colmax];
// Add the column headers from the result set to the JTable.
for (int count = 1; count <= colmax; count++) {
tableModel.addColumn(meta.getColumnName(count));
}
// Add each row from the result set to the table.
while (rs.next()) {
for (int count = 0; count < colmax; count++) {
record[count] = rs.getString(count + 1);
}
tableModel.addRow(record);
}
} catch (SQLException sqle) {
sqle.printStackTrace(System.err);
}
}
/**
* Creates a new instance of TableCanvas. This constructor will accept
* a Map of values for BioLegato properties, and a 2D string array
* containing the initial data for the TableCanvas. The first index
* corresponds to the row number, and the second index corresponds to the
* column number. For example, the index dataImport[1][4] corresponds to
* the 2nd row, 5th column. The first row contains the column names/headers
* for the table.
**
* @param props the BioLegato properties to initialize the canvas with.
* This parameter is ignored if null.
* @param dataImport a 2D string array to initialize the canvas with.
*/
public TableCanvas(Map extends Object, ? extends Object> propertiesadd,
String[][] dataImport) {
this(propertiesadd);
// Ensure that the 2D array is not null and not empty.
if (dataImport != null && dataImport.length > 0) {
// Add the columns and headers to the table.
for (int col = 0; col < dataImport[0].length; col++) {
tableModel.addColumn(dataImport[0][col]);
}
// Add the data for each row in the 2D string array to the table.
for (int row = 0; row < dataImport.length; row++) {
tableModel.addRow(dataImport[row]);
}
}
}
/**
* Starts a new instance of BioLegato, using the table canvas.
**
* @param args the command line arguments for BioLegato.
*/
public static void main (String[] args) {
BLMain.main(TableCanvas.class, args);
}
/**
* Returns the current/selected data in the canvas as a 2D string array.
* The first index corresponds to the row number, and the second index
* corresponds to the column number. For example, the index
* dataImport[1][4] corresponds to the 2nd row, 5th column. The first row
* contains the column names/headers for the table. This method is an alias
* for: getData(false).
**
* @return the current data stored in the table canvas
* (as a 2D string array).
*/
public String[][] getData() {
return getData(false);
}
/**
* Returns the current/selected data in the canvas as a 2D string array.
* The first index corresponds to the row number, and the second index
* corresponds to the column number. For example, the index
* dataImport[1][4] corresponds to the 2nd row, 5th column. The first row
* contains the column names/headers for the table.
**
* @param forceall use the entire canvas instead of just only exporting
* whichever sequences are selected.
* @return the current data stored in the table canvas
* (as a 2D string array).
*/
public String[][] getData(boolean forceall) {
// Determine the first row to start writing the data out. If named
// column headers are enabled, then the absolute first row of the output
// 2D string array (row zero) is written from the column headers, and
// the data begins at row one. If, however, named column headers are
// disabled, then the data is written starting at the first row of the
// output 2D string array (row zero).
int firstrow = (namedHeaders ? 1 : 0);
// A list of rows selected (used to limit the output). This will only
// have values set, if the selection mode is either row or cell based.
int[] rows = null;
// A list of columns selected (used to limit the output). This will
// only have values set, if the selection mode is either column or cell
// based.
int[] columns = null;
// The output 2D string array.
String[][] result = EMPTY_SET;
// Correct the value of the forceall variable based on whether anything
// is actually selected. If nothing is selected, then forceall will be
// set to true, regardless of its actual value.
forceall = forceall || (tablePane.getSelectedColumnCount() == 0
&& tablePane.getSelectedRowCount() == 0);
// If forceall is not set, then determine the filtration needed for
// outputting the selected text in the canvas only.
if (!forceall) {
// Handle row or cell based selections, by extracting the rows in
// the table canvas which are selected.
if (mode == SelectionMode.ROW || mode == SelectionMode.CELL) {
rows = tablePane.getSelectedRows();
}
// Handle column or cell based selections, by extracting the columns
// in the table canvas which are selected.
if (mode == SelectionMode.COLUMN || mode == SelectionMode.CELL) {
columns = tablePane.getSelectedColumns();
}
}
// Branch based on whether rows are filtered.
if (forceall || rows == null) {
// If rows are NOT filtered, then we will write all of the rows,
// when we get to the row extraction part of getData(boolean).
// Start by initializing the output 2D string array.
result = new String[firstrow + tablePane.getRowCount()][];
// If we are using named column headers, then start by extracting
// the column headers and writing it as the first row in the 2D
// string array (the first row is row number zero).
if (namedHeaders) {
result[0] = getColumnHeaders(columns);
}
// Write each of the rows in the table canvas to the output 2D
// string array.
for (int y = 0; y < tablePane.getRowCount(); y++) {
result[firstrow + y] = getRowData(columns, y);
}
} else {
// If rows ARE filtered, then we will write only the rows that are
// selected by the user, when we get to the row extraction part of
// the getData(boolean) method. Start by initializing the output
// 2D string array.
result = new String[firstrow + rows.length][];
// If we are using named column headers, then start by extracting
// the column headers and writing it as the first row in the 2D
// string array (the first row is row number zero).
if (namedHeaders) {
result[0] = getColumnHeaders(columns);
}
// Write only the rows, which are currently selected in the table
// canvas, to the output 2D string array.
for (int y = 0; y < rows.length; y++) {
result[firstrow + y] = getRowData(columns, rows[y]);
}
}
// Return the output 2D string array object.
return result;
}
/**
* Retrieves the column headers (names) for a given set of columns.
**
* @param columns an array of column indices (export all columns if null).
* @return a string array of column header (names).
*/
String[] getColumnHeaders(int[] columns) {
// The string array that will store the column headers.
String[] result = null;
// If the columns variable is null, export all columns, otherwise only
// export the columns selected by the array 'columns'.
if (columns == null) {
// Ensure that all column names are exported. Start by creating a
// string array large enough to hold all of the column headers.
result = new String[tableModel.getColumnCount()];
// Iterate through each column and export it to the array.
for (int x = 0; x < tableModel.getColumnCount(); x++) {
result[x] = tableModel.getColumnName(x);
}
} else {
// Ensure that all of the selected column names are exported. Start
// by creating a string array large enough to hold all of the column
// headers that will be exported.
result = new String[columns.length];
// Iterate through each column selected and export it to the array.
for (int x = 0; x < columns.length; x++) {
result[x] = tableModel.getColumnName(columns[x]);
}
}
// Return the column header array.
return result;
}
/**
* Retrieves all of the data for a given set of columns in a given row.
**
* @param columns the list of column indices to limit the output by (if this
* is set to null, then all columns will be used).
* @param row the row indices to export data for.
* @return the array containing all of the data for the given column and row
* indices only.
*/
String[] getRowData (int[] columns, int row) {
// The string array that will store the row data.
String[] result = null;
// If the columns variable is null, export data for all of the columns,
// otherwise only export data for the selected by the array 'columns'.
if (columns == null) {
// Ensure that data for each column is exported. Start by creating
// a string array large enough to hold all of the column data for
// the current row.
result = new String[tableModel.getColumnCount()];
// Iterate through each column in the current row, and export the
// cell data to the output array.
for (int x = 0; x < tableModel.getColumnCount(); x++) {
result[x] = (String) tableModel.getValueAt(row, x);
}
} else {
// Ensure that all of the data for the selected columns is exported.
// Start by creating a string array large enough to hold all of the
// columns data selected for export from the current row.
result = new String[columns.length];
// Iterate through each selected column in the current row, and
// export the cell data to the output array.
for (int x = 0; x < columns.length; x++) {
result[x] = (String) tableModel.getValueAt(row, columns[x]);
}
}
// Return the column header array.
return result;
}
/**
*
Converts a number into a column letter(s). This is used if the first
* line of a file being imported does not contain column headers -- i.e.
* namedHeaders is set to false. The column letter(s) are a base-26
* representation of the number 'columnCount' using alphabet letters.
*
* The only exception is that the value zero cannot be represented, thus
* the letter "A" represents the value zero in the right-most digit, but
* the letter "A" represents the value one for every other digit. This
* is okay, because we are only displaying column letters, and not
* creating a true base-10 to base-26 converter.
*
* For example, the value 27 is equal to "AA" (26 + 1)
* Likewise, the value 3 is equal to "C", and the value 56 is "BC"
**
* @param columnCount the number of the column to convert into a letter
* @return the letter that corresponds to column #columnCount
*/
public static String columnLetter(int columnCount) {
// A string array to hold the resulting alphabetical column name.
String result = "";
// Loop through each base-26 digit (A-Z) within the number. This is
// done in case the number provided for columnCount is greater than 26
// (the number of letters in the English alphabet). In the case where
// the number is greater than 26, more digits/letters are added to the
// left, to ensure the number is accurately and uniquely represented.
//
// The algorithm is as follows:
do {
// 1. Determine the current character to write to the output string.
// This is done by taking columnCount, and applying the modulus
// operator with a value of 26. Then, this value is added to
// 'A', which will convert it to the appropriate letter.
result = ((char)(((int)'A') + ((columnCount - 1) %26))) + result;
// 2. Divide the columnCount value by 26. This moves to the next
// digit. Thus, in the next loop, we will examine the next
// base-26 digit to output.
columnCount = (int) ((columnCount - 1) / 26);
// 3. Loop until there are no more digits to add (columnCount == 0)
} while (columnCount > 0);
// Return the base-26 (alphabet) representation of the number in
// the base-10 integer parameter columnCount.
return result;
}
/**
* Adds a list of column headers to the current table model. These column
* headers are added as new columns.
**
* @param columns the array of column names to use for adding columns.
*/
public void addColumns(String[] columns) {
while (tableModel.getColumnCount() < columns.length) {
tableModel.addColumn(columns[tableModel.getColumnCount()]);
}
}
/**
* Adds a row to the table canvas. If the row contains more columns than
* the table canvas, more (generic) columns are added to allow the row to
* be added in full.
**
* @param row the array of values to use for the new row.
*/
void addRow(String[] row) {
// If there are less columns in the table canvas than in the row array
// we wish to add, then add new columns to the table until there is
// enough room to add all of the row data. The new columns added will
// use generic headers (because there are no column header names
// available for them to use).
while (tableModel.getColumnCount() < row.length) {
tableModel.addColumn(
TableCanvas.columnLetter(tableModel.getColumnCount() + 1));
}
// Add the row data to the table canvas.
tableModel.addRow(row);
}
/**
* Deletes the current row(s) selected. This
* function is only applicable if rows are selected (either using
* the row or cell selection mode).
**
*/
void deleteSelectedRows() {
int[] rowSelection = tablePane.getSelectedRows();
// Sort the list of rows selected (this is important so we can
// iterate backwards through it).
Arrays.sort(rowSelection);
// Iterate backwards through the list of rows and delete each
// row in the list. We iterate backwards, so we start with
// the highest row number, and work our way to the lowest row
// number. The reason for working from highest to lowest is
// that deleting low row numbers can change the indices of
// higher row numbers. For example:
//
// If we have the data
// 1 sssss
// 2 ddddd
// 3 ccccc
// 4 eeeee
// And we delete the indices 2 and 3; if we delete 2 before
// 3, we will get the wrong result:
// 1 sssss
// 3 ccccc
// However, if we delete the larger row first, we will get
// the correct result:
// 1 sssss
// 4 eeeee
for (int count = rowSelection.length - 1; count >= 0; count--) {
tableModel.removeRow(rowSelection[count]);
}
}
/**
* Possible vestigial code!
* This code is used to provide a plugin name for the canvas. The purpose
* of the plugin name system was to allow multiple canvases to be loaded
* (via. BioLegato's plugin system), and then have the properties select
* which canvas to use based on the plugin names. This architecture has
* since been replaced, and so this code may no longer be used.
**
* @return the name to display for the canvas in
* all program text referring to it.
* @deprecated vestigial code from a previous BioLegato framework structure.
*/
@Override
public String getPluginName() {
return "Table";
}
/**
* Reads a data into the canvas. This method only handles CSV (comma-
* separated-values) and TSV (tab-separated-values) spreadsheet table
* formats. If any other formats are passed to this method, this method
* will default to the TSV format. For more information about the exact
* parsing specification of BioLegato's TSV and CSV formats, please see
* the CSVFile and TSVFile JavaCC code and classes.
**
* @param format the file format to use for parsing the file. If the
* string "" is passed, the PCDIO object should auto-
* detect the format of the data.
* @param in the "file" (or stream) to read in from.
* @param overwrite whether to overwrite the currently selected
* data in the current canvas with the data
* being imported by this function/method.
* @throws IOException if an error occurs while reading
* @see org.biolegato.tables.CSVFile
* @see org.biolegato.tables.TSVFile
*/
public void readFile(String format, Reader in, boolean overwrite)
throws IOException {
// TODO: add more formats
if (format.equalsIgnoreCase("csv")) {
CSVFile.readFile(this, in, overwrite);
} else if (format.equalsIgnoreCase("tsv")) {
TSVFile.readFile(this, in, overwrite);
} else {
// TODO fix autodetect!!!
TSVFile.readFile(this, in, overwrite);
}
}
/**
* Writes data out from the canvas to an Appendable object. This method
* only handles CSV (comma-separated-values) and TSV (tab-separated-values)
* spreadsheet table formats. If any other formats are passed to this
* method, this method won't write any data to the Appendable object. For
* more information about the exact parsing specification of BioLegato's TSV
* and CSV formats, please see the CSVFile and TSVFile JavaCC code and
* classes.
**
* @param format the file format to use for writing the file.
* @param out the "file" (or stream) to write out to.
* @param forceall write the entire contents of the canvas
* instead of just the currently selected
* sequences in the canvas.
* @throws IOException if an error occurs while writing
* @see org.biolegato.tables.CSVFile
* @see org.biolegato.tables.TSVFile
*/
public void writeFile(String format, Appendable out, boolean forceall)
throws IOException {
if (format.equalsIgnoreCase("csv")) {
CSVFile.writeFile(out, getData(forceall));
} else if (format.equalsIgnoreCase("tsv")) {
TSVFile.writeFile(out, getData(forceall));
}
}
/**
* Displays the table canvas within a BioLegato window.
**
* @return the Component object containing the canvas display data.
*/
public Component display() {
// The main box panel to display the table canvas in.
Box mainPane = new Box(BoxLayout.PAGE_AXIS);
// A scroll pane to display the table canvas in. (This will allow the
// user to scroll the table horizontally and vertically.)
JScrollPane scrollPane = new JScrollPane(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
// Configure the scroll pane to view the table within the canvas.
scrollPane.setViewportView(tablePane);
// Configure the table.
tablePane.setAutoCreateColumnsFromModel(true);
tablePane.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tablePane.setFont(new Font("Monospaced", Font.PLAIN, 12));
tablePane.doLayout();
// Add the table's scroll pane to the main panel.
mainPane.add(scrollPane);
// Set the table selection model. If the BioLegato property
// "table.selection" is set to "row" or or "column", then limit the user
// to only select by either rows or columns in the table canvas. If the
// property "table.selection" is set to any other value, then let the
// user choose how they want to select data within the table canvas.
// For more information on this property, please see default.properties,
// in BioLegato's Core/src directory.
if ("row".equals(getProperty("table.selection"))) {
// Force the user to only be able to select data in the table canvas
// by row.
mode = SelectionMode.ROW;
tablePane.setCellSelectionEnabled(false);
tablePane.setColumnSelectionAllowed(false);
tablePane.setRowSelectionAllowed(true);
} else if ("column".equals(getProperty("table.selection"))) {
// Force the user to only be able to select data in the table canvas
// by column.
mode = SelectionMode.COLUMN;
tablePane.setCellSelectionEnabled(false);
tablePane.setColumnSelectionAllowed(true);
tablePane.setRowSelectionAllowed(false);
} else {
// Allow the user to choose how they wish to select data within the
// table canvas. The current options for the user are: by row, by
// column, or by cell.
// Create a new box panel to contain the selection modes, as radio
// buttons, for the user to control.
Box selectionPane = new Box(BoxLayout.LINE_AXIS);
// A variable to cache data for the new radio buttons to add.
JRadioButton radio;
// Create a button group to ensure that each selection mode is
// mutually exclusive (i.e. the user can only pick one at a time).
ButtonGroup group = new ButtonGroup();
// Configure the table canvas to use the "by cell" selection mode,
// by default.
tablePane.setCellSelectionEnabled(true);
tablePane.setColumnSelectionAllowed(true);
tablePane.setRowSelectionAllowed(true);
// Add a label header for the selection mode box panel.
selectionPane.add(new JLabel("Selection mode"));
// Create and add the "by cell" selection mode radio button. (This
// mode will be selected by default.)
radio = new JRadioButton(new AbstractAction("by cell") {
/**
* When the user clicks on this radio button, change the table
* canvas's selection mode to "by cell".
**
* @param evt this parameter is not used by this method.
*/
public void actionPerformed(ActionEvent evt) {
tablePane.setCellSelectionEnabled(true);
tablePane.setColumnSelectionAllowed(true);
tablePane.setRowSelectionAllowed(true);
mode = SelectionMode.CELL;
}
});
radio.setSelected(true);
group.add(radio);
selectionPane.add(radio);
// Create and add the "by column" selection mode radio button.
radio = new JRadioButton(new AbstractAction("by column") {
/**
* When the user clicks on this radio button, change the table
* canvas's selection mode to "by column".
**
* @param evt this parameter is not used by this method.
*/
public void actionPerformed(ActionEvent evt) {
tablePane.setCellSelectionEnabled(false);
tablePane.setColumnSelectionAllowed(true);
tablePane.setRowSelectionAllowed(false);
mode = SelectionMode.COLUMN;
}
});
group.add(radio);
selectionPane.add(radio);
// Create and add the "by row" selection mode radio button.
radio = new JRadioButton(new AbstractAction("by row") {
/**
* When the user clicks on this radio button, change the table
* canvas's selection mode to "by row".
**
* @param evt this parameter is not used by this method.
*/
public void actionPerformed(ActionEvent e) {
tablePane.setCellSelectionEnabled(false);
tablePane.setColumnSelectionAllowed(false);
tablePane.setRowSelectionAllowed(true);
mode = SelectionMode.ROW;
}
});
group.add(radio);
selectionPane.add(radio);
// Add the selection mode panel to the main panel.
mainPane.add(selectionPane);
}
// Create a new list for storing the row headers (i.e. row numbers to
// the right of the JTable object).
final JList rowHeader = new JList(rowHeaderModel);
rowHeader.setFixedCellWidth(50);
rowHeader.setFixedCellHeight(tablePane.getRowHeight());
// + tablePane.getRowMargin());
rowHeader.setCellRenderer(new RowHeaderRenderer(tablePane));
scrollPane.setRowHeaderView(rowHeader);
//////////////////////////////////
//******************************//
//* ADD THE DEFAULT MENU ITEMS *//
//******************************//
//////////////////////////////////
///////////////////////////
// Add the "Open" button //
///////////////////////////
addMenuHeading("File").insert(new JMenuItem(
new AbstractAction("Open...") {
/**
* Serialization number - required for no warnings.
*/
private static final long serialVersionUID = 7526472295622776157L;
/**
* Sets the mnemonic for the event.
*/
{
putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
}
/**
* Event handler - displays a JFileChooser so that the user can
* select and open a file. Once the file is selected, the file will
* be read into the current canvas, using whichever FileFilter
* choice the user selected. The two FileFilters in the
* JFileChooser are CSV_FILTER and TSV_FILTER. If CSV_FILTER is
* selected, then the file will be parsed as a CSV file. If
* TSV_FILTER is selected, then the file will be parsed as a TSV
* file.
**
* @param evt ignored by this method.
* @see org.biolegato.tables.CSVFile
* @see org.biolegato.tables.TSVFile
*/
public void actionPerformed(java.awt.event.ActionEvent evt) {
// A variable for storing all of the files the user selects
// inside the JFileChooser window.
File[] openFiles;
// The JFileChooser windwow, from which the user selects the
// file to open.
JFileChooser openDlg = new JFileChooser();
// Configure the JFileChooser object to use PCD's current
// directory as its default directory; enable multiple file
// selection; and to disable the "accept all" file filter
// (which will correspond to file type auto-detection).
openDlg.setCurrentDirectory(PCD.getCurrentPWD());
openDlg.setAcceptAllFileFilterUsed(false);
openDlg.setMultiSelectionEnabled(true);
// Add the CSV and TSV file filters.
openDlg.addChoosableFileFilter(CSV_FILTER);
openDlg.addChoosableFileFilter(TSV_FILTER);
// If the user clicks the OK button, then check to see if at
// least one file is selected, and also update the current PCD
// path (such that any subsequent new file choosers will use the
// directory last browsed as their default directory. If files
// are selected, then read them into the canvas.
if (openDlg.showOpenDialog(getJFrame())
== JFileChooser.APPROVE_OPTION) {
// Extract the files (if any) selected in the JFileChooser.
openFiles = openDlg.getSelectedFiles();
// Prevent parsing a null value for openFiles.
if (openFiles != null) {
// Iterate through all of the files selected in the
// JFileChooser object.
for (File opf : openFiles) {
// Ensure that the file exists and is a file (i.e. skip
// directories and non-existent files).
if (opf.exists() && opf.isFile()) {
try {
// Read the file into the table canvas,
// using the appropriate file filter (csv,
// or tsv).
readFile((openDlg.getFileFilter()
== CSV_FILTER ? "csv" : "tsv"),
new FileReader(opf), false);
} catch (IOException ioe) {
// Print a stack trace if any error occurs.
ioe.printStackTrace(System.err);
}
}
}
}
// Update the PCD working directory.
if (openDlg.getCurrentDirectory() != null) {
PCD.setCurrentPWD(openDlg.getCurrentDirectory());
}
}
}
}), 0);
/////////////////////////////////
// Add the "Properties" button //
/////////////////////////////////
addMenuHeading("File").insert(new JMenuItem(new AbstractAction("Properties...") {
/**
* Serialization number - required for no warnings.
*/
private static final long serialVersionUID = 7526472295622776157L;
/**
* Sets the mnemonic for the event.
*/
{
putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
}
/**
* The "Properties..." menu item action. This action will open a
* window, so the user can change the properties of the table
* canvas. The properties window is controlled (and displayed,
* etc.) by the TableCanvasProperties class.
**
* @param evt ignored by this method.
* @see org.biolegato.sequence.canvas.TableCanvasProperties
*/
public void actionPerformed(java.awt.event.ActionEvent evt) {
new TableCanvasProperties(getJFrame(), tablePane, rowHeader);
}
}), 2);
/////////////////////////////////
// Add the "Delete Row" button //
/////////////////////////////////
if (!"true".equalsIgnoreCase(getProperty("readonly"))) {
addMenuHeading("Edit").insert(new JMenuItem(new AbstractAction("Delete Row(s)") {
/**
* Serialization number - required for no warnings.
*/
private static final long serialVersionUID = 7526472295622776157L;
/**
* Sets the mnemonic for the event.
*/
{
putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
}
/**
* Event handler - deletes the current row(s) selected. This
* function is only applicable if rows are selected (either using
* the row or cell selection mode).
**
* @param evt ignored by this method.
*/
public void actionPerformed(java.awt.event.ActionEvent evt) {
// Extract a list of the rows selected.
int[] rowSelection = tablePane.getSelectedRows();
// Sort the list of rows selected (this is important so we can
// iterate backwards through it).
Arrays.sort(rowSelection);
// Iterate backwards through the list of rows and delete each
// row in the list. We iterate backwards, so we start with
// the highest row number, and work our way to the lowest row
// number. The reason for working from highest to lowest is
// that deleting low row numbers can change the indices of
// higher row numbers. For example:
//
// If we have the data
// 1 sssss
// 2 ddddd
// 3 ccccc
// 4 eeeee
// And we delete the indices 2 and 3; if we delete 2 before
// 3, we will get the wrong result:
// 1 sssss
// 3 ccccc
// However, if we delete the larger row first, we will get
// the correct result:
// 1 sssss
// 4 eeeee
for (int count = rowSelection.length - 1; count >= 0; count--) {
tableModel.removeRow(rowSelection[count]);
}
}
}), 0);
}
return mainPane;
}
}