/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package photoSpreadTable;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellEditor;
import photoSpread.PhotoSpread;
import photoSpreadObjects.photoSpreadComponents.ObjectsPanel;
import photoSpreadObjects.photoSpreadComponents.Workspace;
import photoSpreadTable.DnDSupport.StillLabel;
import photoSpreadTable.photoSpreadFormulaEditor.PhotoSpreadFormulaEditor;
import photoSpreadUtilities.CellCoordinates;
import photoSpreadUtilities.Const;
import photoSpreadUtilities.Misc;
import photoSpreadUtilities.Misc.ShowHelpAction;
/**
*
* @author skandel
*/
public class PhotoSpreadTable extends JTable {
private static final long serialVersionUID = 1L;
private PhotoSpreadFormulaEditor _formulaEditor;
private PhotoSpreadTableModel _tableModel;
private Workspace _workspace;
// Place to remember which/whether we hover-highlighted
// any cell during a drag/drop operation. This way we
// can be sure to unHoverHighlight the cell when the
// drag/drop is done:
public PhotoSpreadCellHandler _mostRecentDropTarget = null;
private DnDSupport _dndViz = null;
/****************************************************
* Constructor(s)
*****************************************************/
public PhotoSpreadTable(PhotoSpreadTableModel model, JFrame enclosingWindow) {
super(model);
_tableModel = model;
_tableModel.setTable(this);
_formulaEditor = new PhotoSpreadFormulaEditor(this);
_workspace = new Workspace(enclosingWindow);
PhotoSpread.setCurrentWorkspaceWindow(_workspace);
_workspace.setMinimumSize(new Dimension(500,550));
// Put the workspace next to this sheet window:
_workspace.setLocationRelativeTo(null);
Point wsLocation = _workspace.getLocation();
_workspace.setLocation(wsLocation.x, wsLocation.y - 50);
_workspace.setVisible(true);
// Inits for drag/drop support:
_dndViz = (StillLabel) DnDSupport.TableDnDVizFactory(
enclosingWindow,
this,
DnDSupport.DnDVizStyle.STILL_LABEL);
this.setRowHeight(PhotoSpread.photoSpreadPrefs.getInt(PhotoSpread.sheetRowHeightMinKey));
this.setIntercellSpacing(new Dimension(Const.SPACE_BETWEEN_TABLE_CELLS_HOR, Const.SPACE_BETWEEN_TABLE_CELLS_VER));
this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
this.setSelectionBackground(Const.activeCellBackgroundColor);
this.setBackground(Const.inactiveCellBackgroundColor);
this.setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS);
//this.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// Ask to be notified of changes in the model:
_tableModel.addTableModelListener(new PhotoSpreadTableModelListener(this));
// When starting to type, give focus to the
// cell that has the cursor over it. User doesn't
// have to click the cell (default is false):
// this.setSurrendersFocusOnKeystroke(true);
setDefaultRenderer(
Object.class, // All columns whose content type is Object (the default)
// will use this following class for rendering
new PhotoSpreadCellEditorAndRenderer());
setDefaultEditor(
Object.class, // All columns whose content type is Object (the default)
// will use this following class for cell editing
new PhotoSpreadCellEditorAndRenderer());
RowHeightRegulationListener mouseHandler = new RowHeightRegulationListener(this);
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
// Request a cell editor for the
// upper left cell to initialize
// selection- and dnd machinery:
getDefaultEditor().getTableCellEditorComponent(
this,
getCell(0, 1),
Const.IS_SELECTED,
0, // row
1); // column
Misc.bindKey(this, "F1", new ShowHelpAction(
"To do in Sheet Window",
"HelpFiles/sheetHelp.html",
PhotoSpread.getCurrentSheetWindow()));
}
/****************************************************
* Listeners
*****************************************************/
private class RowHeightRegulationListener extends MouseInputAdapter {
PhotoSpreadTable _table;
int _rowBeingResized = -1;
int _cursorYWas = 0;
private Cursor _resizeCursor = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
private Cursor _savedCursor = null;
public RowHeightRegulationListener (PhotoSpreadTable table) {
_table = table;
}
public void mousePressed (MouseEvent e) {
CellCoordinates cellCoords = getCellAddressUnderCursor();
if (cellCoords.column() == 0) {
_rowBeingResized = cellCoords.row();
_cursorYWas = e.getYOnScreen();
// If we keep dragging enabled the system gets confused:
_table.setDragEnabled(false);
if (_savedCursor == null)
_savedCursor = _table.getCursor();
_table.setCursor(_resizeCursor);
}
}
public void mouseReleased (MouseEvent e) {
_rowBeingResized = -1;
_table.setDragEnabled(true);
_table.setCursor(_savedCursor);
}
public void mouseClicked (MouseEvent e) {
if (e.getClickCount() == 2) {
// Double clicked on column 0? If yes,
// reset current row to default height:
CellCoordinates cellCoords = getCellAddressUnderCursor();
if (cellCoords.column() == 0) {
int defaultRowHeight = PhotoSpread.photoSpreadPrefs.getInt(PhotoSpread.sheetRowHeightMinKey);
setRowHeight(cellCoords.row(), defaultRowHeight);
}
}
}
public void mouseDragged (MouseEvent e) {
if (_rowBeingResized >= 0) {
adjustRowHeight(
_rowBeingResized,
_cursorYWas,
e.getYOnScreen(),
Const.motionSensitivity);
_cursorYWas = e.getYOnScreen();
}
}
}
private class PhotoSpreadTableModelListener implements TableModelListener {
PhotoSpreadTable _table = null;
public PhotoSpreadTableModelListener (PhotoSpreadTable table) {
_table = table;
}
/* (non-Javadoc)
* @see javax.swing.event.TableModelListener#tableChanged(javax.swing.event.TableModelEvent)
*/
@Override
public void tableChanged(TableModelEvent changeEvent) {
int affectedCol = changeEvent.getColumn();
int firstAffectedRow = changeEvent.getFirstRow();
int lastAffectedRow = changeEvent.getLastRow();
int changeType = changeEvent.getType();
PhotoSpreadCell currCell = _table.getSelectedCell();
if ((affectedCol < 0) ||
(firstAffectedRow < 0) ||
(lastAffectedRow < 0))
return;
PhotoSpreadCellEditorAndRenderer cellEditorFinder = _table.getDefaultEditor();
PhotoSpreadCellHandler cellHandler = null;
switch (changeType) {
case TableModelEvent.UPDATE:
// One or more rows in a single column were updated.
// Cause each of them to repaint:
for (int row = firstAffectedRow; row <= lastAffectedRow; row++) {
// If the modifification was in the currently selected cell,
// then we need to call the 'big' version of getTableCellEditorComponent().
// That version (re)initializes the formula bar and the Workspace:
if ((row == currCell.getRow()) && (affectedCol == currCell.getColumn()))
cellHandler = (PhotoSpreadCellHandler) cellEditorFinder.getTableCellEditorComponent(
_table,
null, // no initial value to show. Cell has own value
Const.IS_SELECTED,
row,
affectedCol);
else
cellHandler = cellEditorFinder.getTableCellEditorComponent(_table, row, affectedCol);
cellHandler.stopCellEditing();
}
break;
case TableModelEvent.INSERT:
// Inserting rows in the middle of a sheet isn't implemented yet:
if (firstAffectedRow < _table.getRowCount())
// throw new PhotoSpreadException.NotImplementedException("Inserting rows in middle of PhotoSpread sheet is not yet implemented.");
System.out.println("Inserting rows in middle of PhotoSpread sheet is not yet implemented.");
// NOTE: NOT SURE THIS IS RIGHT. NO TIME TO THINK RIGHT NOW:
// Append rows is manageable: just ask for the cell editor
// in each of the new rows.
for (int row = firstAffectedRow; row < lastAffectedRow; row++) {
for (int col = 0; col < _table.getColumnCount(); col++)
cellHandler = cellEditorFinder.getTableCellEditorComponent(_table, row, col);
}
break;
case TableModelEvent.DELETE:
// throw new PhotoSpreadException.NotImplementedException("Deletion in PhotoSpread sheet is not yet implemented.");
System.out.println("Deletion in PhotoSpread sheet is not yet implemented.");
break;
default:
// ignore any other change types (there shouldn't be any).
}
}
}
/****************************************************
* Getter/Setter(s)
*****************************************************/
public PhotoSpreadFormulaEditor getFormulaEditor() {
return _formulaEditor;
}
public Workspace getWorkspace () {
return _workspace;
}
public PhotoSpreadTableModel getTableModel() {
return _tableModel;
}
public DnDSupport getDndViz() {
return _dndViz;
}
public PhotoSpreadTableModel getPhotoSpreadModel(){
return this._tableModel;
}
public void setCellFormula(int row, int col , String formula){
PhotoSpreadCell cell = (PhotoSpreadCell) _tableModel.getValueAt(row, col);
cell.setFormula(formula, Const.DO_EVAL, Const.DO_REDRAW);
}
public void setSelectedCellFormula(String formula){
int row = this.getSelectedRow();
int col = this.getSelectedColumn();
setCellFormula(row, col, formula);
}
/**
* Find PhotoSpreadCell object at (row, column)
* @param row
* @param col
* @return PhotoSpreadCell object held in given table cell.
*/
public PhotoSpreadCell getCell(int row, int col) {
return (PhotoSpreadCell) _tableModel.getValueAt(row, col);
}
/**
* Find PhotoSpreadCell object by coordinates
* @param coords
* @return PhotoSpreadCell object held in given table cell.
*/
public PhotoSpreadCell getCell(CellCoordinates coords) {
return (PhotoSpreadCell) _tableModel.getValueAt(coords.row(), coords.column());
}
/**
* Identify this table's cell coordinates (row, column) under the current cursor location.
* @return CellCoordinate object. Returns -1 for row and/or column,
* if cursor is not located over a row/column.
*/
public CellCoordinates getCellAddressUnderCursor () {
return new CellCoordinates (getRowUnderCursor(), getColumnUnderCursor());
}
/**
* Find the number of the column under the current cursor position.
* @return This table's column under the cursor, or -1 if cursor is not
* over a column
*/
public int getColumnUnderCursor() {
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mouseLoc, this);
return this.columnAtPoint(mouseLoc);
}
/**
* Find the number of the row under the current cursor position.
* @return This table's row under the cursor, or -1 if cursor is not
* over a row.
*/
public int getRowUnderCursor() {
Point mouseLoc = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mouseLoc, this);
return this.rowAtPoint(mouseLoc);
}
/**
* Return cell coordinates given a Point in table coordinates.
* @param loc Location in table coordinates
* @return Coordinates of cell underneath that location. Like (1,5).
*/
public CellCoordinates getCellAddressAt (Point loc) {
return new CellCoordinates (getRowAt (loc), getColumnAt(loc));
}
/**
* Return row, given a Point in table coordinates.
* @param loc Location in table coordinates
* @return The row number (zero origin)
*/
public int getRowAt (Point loc) {
return this.rowAtPoint(loc);
}
/**
* Return column, given a Point in table coordinates.
* @param loc Location in table coordinates
* @return The column number (zero origin)
*/
public int getColumnAt (Point loc) {
return this.columnAtPoint(loc);
}
public PhotoSpreadCell getSelectedCell() {
int row = this.getSelectedRow();
int col = this.getSelectedColumn();
return getCell(row, col);
}
/****************************************************
* Methods
*****************************************************/
// The following two methods unfortunately don't work.
// I'd love to have them.
/* public void activateCell (CellCoordinates coords) {
activateCell(coords.row(), coords.column());
}
public void activateCell (int row, int col) {
getDefaultEditor().getTableCellEditorComponent(
this,
null,
true,
row,
col);
}
*/
@Override
public int getSelectedRow() {
PhotoSpreadCell selectedCellObj =
((PhotoSpreadCellEditorAndRenderer) getDefaultEditor(Object.class)).getSelectedCellObject();
return selectedCellObj.getRow();
}
/**
* Return the index of the first selected column, or -1 if none selected.
* @see javax.swing.JTable#getSelectedColumn()
*/
@Override
public int getSelectedColumn() {
PhotoSpreadCell selectedCellObj =
((PhotoSpreadCellEditorAndRenderer) getDefaultEditor(Object.class)).getSelectedCellObject();
if (selectedCellObj == null)
return -1;
return selectedCellObj.getColumn();
}
/**
* Provides the TableCellEditor for PhotoSpreadTable instances.
* This object is primarily useful to provide CellEditor instances,
* that is PhotoSpreadCellHandler instances, for given row/column
* pairs.
* @return
*/
public PhotoSpreadCellEditorAndRenderer getDefaultEditor () {
TableCellEditor jTableObjectTypedCellEditor = getDefaultEditor (Object.class);
return (PhotoSpreadCellEditorAndRenderer) jTableObjectTypedCellEditor;
}
public PhotoSpreadCellHandler getActiveCellEditor(){
return getCellEditorFor (new CellCoordinates (getSelectedRow(), getSelectedColumn()));
}
public PhotoSpreadCellHandler getCellEditorFor (int row, int col) {
PhotoSpreadCellHandler cellEditor = null;
PhotoSpreadCellEditorAndRenderer tableCellEditor = getDefaultEditor();
cellEditor = tableCellEditor.getTableCellEditorComponent(this, row, col);
return cellEditor;
}
public PhotoSpreadCellHandler getCellEditorFor (CellCoordinates coords) {
return getCellEditorFor(coords.row(), coords.column());
}
public void clearCell(int row, int col) {
((PhotoSpreadCell) _tableModel.getValueAt(row, col)).clear();
}
protected void adjustRowHeight (
int rowToResize,
int cursorYWas,
int cursorYIs,
int motionSensitivity) {
int relativeMove = motionSensitivity * (cursorYIs - cursorYWas);
// int currRowHeight = getRowHeight(_rowBeingResized);
int newSize = getRowHeight(rowToResize) + relativeMove;
setRowHeight(
rowToResize,
Math.max (PhotoSpread.photoSpreadPrefs.getInt(PhotoSpread.sheetRowHeightMinKey),
newSize));
}
public void redraw(){
this._workspace.redraw();
}
public boolean validEditingCell(int row, int col){
// return (row >= 0 && col >= 0 && row < this.getRowCount() && col < this.getRowCount());
return (row >= 0 && col >= 1 && row < this.getRowCount() && col < this.getColumnCount());
}
public boolean isCellEditable(int row, int column) {
if (column > 0)
return true;
else
return false;
}
/**
* Convenience method to fire a table cell change
* event just knowing the table, row, and col, and
* without having to dig out the table model.
* @param row
* @param col
*/
public void fireTableCellUpdated(int row, int col) {
_tableModel.fireTableCellUpdated(row, col);
}
public static boolean loadFiles (PhotoSpreadCell cell, ArrayList<File> files) {
return ObjectsPanel.loadFiles(cell, files);
}
public static boolean loadFiles (PhotoSpreadCell cell, File[] files) {
return ObjectsPanel.loadFiles(cell, files);
}
}