home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Symantec Visual Cafe for Java 2.5
/
symantec-visual-cafe-2.5-database-dev-edition.iso
/
VCafe
/
prosrc.bin
/
TableView.java
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Java Source
|
1998-03-18
|
187.6 KB
|
6,315 lines
/*
* Copyright (c) 1997 Krumel & Associates, Inc. All Rights Reserved.
*
* www.krumel.com - controls@krumel.com
*
* Permission is given to the buyer of this package for one software
* developer to use this software on one CPU (one workstation) and to make
* one backup copy. You may uitilize and/or modify this class for use in your
* projects. You may distribute or sell any executable which results from
* using this code in yur application, except a utility or class of similar
* nature to this product. You may distribute this product in compiled
* form only, but soley to be used with your cmpiled executable product
* for the puposes of dynamic loading. You may NOT redistribute the source
* code in any form or make it accessible through a network or other
* distribution media to others. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* The source code is the confidential and proprietary information
* of Krumel & Associates, Inc. ("Confidential Information"). You shall
* not disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Krumel & Associates, Inc..
* KRUMEL & ASSOCIATES MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KRUMEL & ASSOCIATES SHALL NOT
* BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
* MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
package symantec.itools.db.awt;
import java.awt.*;
import java.util.*;
import java.io.*;
import symantec.itools.db.awt.genutil.*;
import java.awt.event.*;
import symantec.itools.db.awt.event.*;
import symantec.itools.db.awt.awt.Scroller;
import symantec.itools.db.awt.genutil.Out;
import symantec.itools.db.awt.awt.Spacer;
import java.sql.SQLException;
import java.util.ResourceBundle;
/**
* A spreadsheet style control.<p>
*
* All column and row numbers are one relative.<p>
*
* The TableView supports column and row headings. The row headings may be blank or
* autonumbered based on a user defined base number.<p>
*
* The TableView provides support for a toolbar along the bottom row to the right
* of the horizontal scrollbar. Any type of component may be added to the toolbar
* or pre-defined buttons may be added using the available addXXXXButton methods.<p>
*
* To increase the modularity of the TableView, events and exceptions are passed to
* an EventHandler object when set. Event IDs have been defined for the table
* and cells. Additional events are generated by the toolbar components and
* forwarded to the EventHandler as well. Of course the tradional event model
* of JDK 1.0.2 is also supported but tends to make GUI code functional.
* All highlighting, inserts, gotos, ... are performed by the EventHandler. <p>
*
* To further increase modularity, data is kept in DataSources. A default
* DataSource if one is not defined that simply stores the data in a Matrix
* instance. If a RelationView is specified in the constructor, then a special
* DataSource is created to drive the TableView off of the database query.<p>
*
* A number of APIs are defined to allow the TableView color, font, and other visual
* styles to be effected.
*
* @version 1.0 12 Jan 1997
* @author Andy Krumel - Krumel & Associates, Inc
*/
public class TableView extends Panel
implements /*Notifiable,*/ AdjustmentListener, FocusListener
{
//BS:added for backward compatibility
//private int m_NumberOfRows = 5;
//private int m_NumberOfColumns = 5;
public String name = "";
boolean autoRedraw = true;
boolean disableDrawing = false;
Panel toolbar;
Panel bottomPanel;
Vector toolbarComponents = new Vector();
TvEventHandler eventHandler;
Matrix cells;
Matrix colHeadings;
Matrix cellAttributes; //cell hints
Matrix headingAttributes; //col heading hints
int preferredRowCount = 10;
int preferredPixelWidth = -1;
/**
* The bounds of each column. There are cols+1 elements.
*/
int splitters[];
/**
* Determines if column is hidden. Use true to indicate do not show so when
* array created, all columns are visible by default.
*/
boolean colHidden[];
/**
* Specified whether last column should fill all remaining space.
*/
boolean fillLastColumn;
Scroller vsb;
Scroller hsb;
Spacer spacer;
int cursor = Frame.DEFAULT_CURSOR;
long clickTime;
int topRow;
int lockedColumn = -1;
int leftCol = lockedColumn+1/*next col*/ +1 /*one relative*/+1;
int dragColumn = -1;
int xDragLast = -1;
boolean isDragging;
int headingHeight;
int cellHeight;
int clickMargin = 5;
int currentCursor = Frame.DEFAULT_CURSOR;
transient Image im;
transient Graphics gg;
int height;
int width;
int hsbPosition=1;
long scrollbarTimer;
boolean fetchMode = false;
DataSource dataSource;
DataSource headingSource;
DefaultDataSource rowHeadingSource;
/**
* Hints used to draw cells. Prevents extra heap allocation
*/
transient CellHints hints;
/**
* Cell that has the keyboard focus.
*/
TableCell currSelectedCell = null;
/**
* Cell that has the mouse focus.
*/
TableCell currCaptureCell = null;
/**
* The default cell used when none is specified. Default is BasicCell.
*/
TableCell defaultCell;
//-----------------------------------------------------------------------------
// Column Heading Variables
//-----------------------------------------------------------------------------
boolean useRowHeadings = true;
int rowHeadingWidth = 30;
CellHints rowHeadingHints;
boolean autoCreate = true;
int rowHeadingLabelStyle = BLANK;
int firstNumber = 1; //used for autonumbering
TableCell headingCell; //the default row heading cell
TableCell currHeadingCell; //the one currently being pressed
//this is null if not in use
TableCell cornerCell; //the default row heading cell
//row heading styles
/**
* Autonumber row heading style
*/
public final static int AUTONUMBER = 0;
/**
* Blank row headings - the default
*/
public final static int BLANK = 1;
/**
* User defined style - not currently supported
*/
public final static int USER_DEFINED = 2;
/**
* TableCell type as returned by TableCell.type()
*/
public final static Integer TABLE_CELL = new Integer(1);
/**
* The corner cell type as returned by TableCell.type()
*/
public final static Integer CORNER_CELL = new Integer(2);
/**
* Column heading type as returned by TableCell.type()
*/
public final static Integer COLUMN_HEADING = new Integer(3);
/**
* Row heading cell type as returned by TableCell.type()
*/
public final static Integer ROW_HEADING = new Integer(4);
/**
* default append row toolbar button label
*/
public static String appendRowLabel;
/**
* default delete row toolbar button label
*/
public static String deleteRowLabel;
/**
* default insert row toolbar button label
*/
public static String insertRowLabel;
/**
* default goto row toolbar button label
*/
public static String gotoRowLabel;
/**
* default save row toolbar button label
*/
public static String saveLabel;
/**
* default restart row toolbar button label
*/
public static String restartLabel;
/**
* default undo row toolbar button label
*/
public static String undoRowLabel;
/**
* default undelete row toolbar button label
*/
public static String undeleteRowLabel;
/**
* The name assigned to goto toolbar components.
*/
public static final String gotoName = "GOTO";
/**
* The name assigned to delete toolbar components.
*/
public static final String deleteName = "DELETE";
/**
* The name assigned to insert toolbar components.
*/
public static final String insertName = "INSERT";
/**
* The name assigned to save toolbar components.
*/
public static final String saveName = "SAVE";
/**
* The name assigned to undo toolbar components.
*/
public static final String restartName = "RESTART";
/**
* The name assigned to undo toolbar components.
*/
public static final String undoName = "UNDO";
/**
* The name assigned to undelete toolbar components.
*/
public static final String undeleteName = "UNDELETE";
/**
* The name assigned to append toolbar components.
*/
public static final String appendName = "APPEND";
//-----------------------------------------------------------------------------
// Constant IDs
//-----------------------------------------------------------------------------
/**
* Time to register a double click (in milliseconds).
*/
public final static long CLICKTHRESHOLD = 250;
/**
* Left-justify constant.
*/
public final static int LEFT = 0;
/**
* Center-justify constant.
*/
public final static int CENTER = 1;
/**
* Right-justify constant.
*/
public final static int RIGHT = 2;
//Cell line styles
/**
* Line style specifies that no line should be drawn.
*/
public final static int NO_LINE = 0;
/**
* Line style specifies that a thin line should be drawn.
*/
public final static int THIN_LINE = 1;
/**
* Line style specifies that a thick line (3 pixels wide) should be drawn.
*/
public final static int THICK_LINE = 2;
/**
* This one is not implemented yet, but it will be soon.
*/
final static int DASHED_LINE = 3;
//Cell vertical align styles
final static int TOP = 0;
// public final static int CENTER = 1; //already defined
final static int BOTTOM = 2;
//Cell fill styles
static TableCell defaultHeadingCell_ = new ButtonCell();
static boolean defaultsInitialized = false;
static void initDefaults() {
if (!defaultsInitialized) {
defaultHeadingCell_.setCoordinates(new Coordinate(0,0));
defaultHeadingCell_.type(TableCell.ROW_HEADING);
defaultsInitialized = true;
}
}
/**
* Default constructor for new TableView.
*/
public TableView() {
this(0, Color.white);
// System.out.println("into tableview()");
}
/**
* Constructs a new TableView with the specified number of columns.
* @param cols the number of columns
*/
public TableView(int cols) {
this(cols, Color.white);
//System.out.println("cols");
}
/**
* Constructs a new TableView with the preferred height based on
* specified number of rows.
* @param rows the number of rows to show
*/
public TableView(long rows) {
this(rows, 0);
//System.out.println("rows");
}
/**
* Constructs a new TableView with the spcified number of columns
* and whether multiple row selection allowed.
* @param rows the number of rows to show
* @param cols the number of columns
*/
public TableView(long rows, int cols) {
//this(cols, Color.white);
ResourceBundle res = ResourceBundle.getBundle("symantec.itools.db.resources.ResBundle");
appendRowLabel = res.getString("Append");
deleteRowLabel = res.getString("Delete");
insertRowLabel = res.getString("Insert");
gotoRowLabel = res.getString("Goto");
saveLabel = res.getString("Save");
restartLabel = res.getString("Restart");
undoRowLabel = res.getString("Undo");
undeleteRowLabel = res.getString("Undelete");
//System.out.println("rows,cols");
dataSource = new DefaultDataSource(this);
assignDefaults();
dataSource.setDefaultData();
createColumns(cols);
setupAutonumbering(1);
setBackground(Color.white);
//BS
installDefaultEventHandler();
try
{
for( int i=0; i < rows; i++)
{
appendRow();
}
for(int i=0; i< cols;i++)
{
setHeading( "Column:"+(i+1), i+1, 10);
}
} catch( Exception e)
{System.out.println( "EXCEPTION " + e);}
preferredRowCount = (int)rows;
}
/**
* Constructs a new TableView with the spcified number of columns
* and whether multiple row selection allowed, and background color.
* @param cols the number of columns
* @bg Color instance for background color
*/
public TableView(int cols, Color bg) {
ResourceBundle res = ResourceBundle.getBundle("symantec.itools.db.resources.ResBundle");
appendRowLabel = res.getString("Append");
deleteRowLabel = res.getString("Delete");
insertRowLabel = res.getString("Insert");
gotoRowLabel = res.getString("Goto");
saveLabel = res.getString("Save");
restartLabel = res.getString("Restart");
undoRowLabel = res.getString("Undo");
undeleteRowLabel = res.getString("Undelete");
dataSource = new DefaultDataSource(this);
assignDefaults();
dataSource.setDefaultData();
createColumns(cols);
//setupAutonumbering(1);
setBackground(bg);
//BS
installDefaultEventHandler();
}
/**
* Constructs a TableView using the specified DataSource instance. If the
* DataSource supports meta data then it will be used to setup the TableView.
* @param ds The DataSource used to keep the TableView's data
*/
public TableView(DataSource ds) {
dataSource = ds;
dataSource.setDefaultData();
assignDefaults();
dataSource.setTableView(this);
if (dataSource.supportsMeta()) {
try { dataSource.setupTableView(this); } catch(Exception e) {
e.printStackTrace();
}
}
}
boolean guiConstructed = false;
void assignDefaults() {
initDefaults();
if (!guiConstructed) {
//enable mouse events and key events.
enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK
| AWTEvent.MOUSE_EVENT_MASK
| AWTEvent.KEY_EVENT_MASK
| AWTEvent.FOCUS_EVENT_MASK);
String osName = System.getProperty("os.name");
setLayout(new BorderLayout());
setFont(CellHints.stdFont);
setBackground(Color.white);
setForeground(Color.black);
vsb = new Scroller(Scrollbar.VERTICAL);
vsb.hide();
vsb.addAdjustmentListener(this);
toolbar = new Panel();
toolbar.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
toolbar.setBackground(Color.lightGray);
hsb = new Scroller(Scrollbar.HORIZONTAL);
hsb.addAdjustmentListener(this);
spacer = new Spacer();
bottomPanel = new Panel();
bottomPanel.setBackground(Color.lightGray);
bottomPanel.setLayout(new BorderLayout());
bottomPanel.add("West", toolbar);
Panel padding = new Panel();
padding.setLayout(new BorderLayout());
padding.add("North", hsb);
bottomPanel.add("Center", padding);
bottomPanel.add("East", spacer);
add("East",vsb);
add("South", bottomPanel);
guiConstructed = true;
}
hints = new CellHints(this);
defaultCell = new BasicCell(this, dataSource);
defaultCell.setCoordinates(new Coordinate(0, 0));
defaultCell.setDefaultFlag();
headingHeight = getFontMetrics(CellHints.stdFont).getHeight() + 4;
cellHeight = headingHeight;
cells = new Matrix();
colHeadings = new Matrix();
cellAttributes = new Matrix(); //cell hints
headingAttributes = new Matrix(); //col heading hints
headingSource = new DefaultDataSource(this);
rowHeadingSource = new DefaultDataSource(this, true);
//create row heading hints with default bg of light gray
rowHeadingHints = new CellHints(this);
rowHeadingHints.bg = Color.lightGray;
setCornerCell(defaultHeadingCell_);
setDefaultRowHeadingCell(defaultHeadingCell_);
try {
rowHeadingSource.setData(0, 1, new ImageStringData(dataSource, ""));
rowHeadingSource.setDefaultData(new ImageStringData(dataSource));
} catch(TypeNotSupported ex) {}
}
public void setToolbarBackground(Color c) { toolbar.setBackground(c); }
public Color getToolbarBackground() { return toolbar.getBackground(); }
/**
* Sets the DataSource for keeping the TableView's data. Replaces the previous
* DataSource if previously set.
* @param ds The DataSource to keep the TableView's data.
*/
public void setDataSource(DataSource ds) {
clear();
dataSource = ds;
dataSource.setDefaultData();
assignDefaults();
dataSource.setTableView(this);
if (dataSource.supportsMeta()) {
try { dataSource.setupTableView(this); } catch(Exception e) {
e.printStackTrace();
}
}
}
/**
* Sets the DataSource for keeping the TableView's data. Replaces the previous
* DataSource if previously set.
* @param ds The DataSource to keep the TableView's data.
*/
public void setDataSource(DataSource ds, int first) {
setDataSource(ds);
setupAutonumbering(first);
}
/**
* Gets the current DataSource for cell data
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* Sets the automatic redraw behavior of the TableView. If auto redraw is
* turned on then adding rows or changing data will cause the TableView to
* automatically redraw. Turning it off can improve performance when
* making many changes at one time.
* @param redraw true to turn on automatic redrawing
*/
public void setAutoRedraw(boolean redraw) {
autoRedraw = redraw;
}
/**
* Gets the automatic redraw behavior of the TableView. If auto redraw is
* turned on then adding rows or changing data will cause the TableView to
* automatically redraw. Turning it off can improve performance when
* making many changes at one time.
*/
public boolean getAutoRedraw() {
return autoRedraw;
}
/**
* Disables all drawing of the TableView. This can greatly increase performance
* if intensive methods occur by decreasing the algorithmic calculations.
*/
public void disableDrawing() {
disableDrawing = true;
}
/**
* Enables drawing functions.
*/
public void enableDrawing() {
disableDrawing = false;
}
/**
* Returns whether the TableView is currently performing drawing functions.
*/
public boolean isDrawingEnabled() {
return !disableDrawing;
}
/**
* Installs the TableView's default event handler used to handle cell and
* table level events and perform actions required when exception occur.
*/
public void installDefaultEventHandler() {
installEventHandler(new DefaultTvEventHandler());
}
/**
* Installs an event handler used to handle cell and
* table level events and perform actions required when exception occur.
*/
public void installEventHandler(TvEventHandler h) {
eventHandler = h;
eventHandler.setupView(this);
}
//-------------------------------------------------------------------------
// Toolbar button functions
//-------------------------------------------------------------------------
/**
* Adds an add button to the TableView's toolbar.
*/
public void addAppendButton() {
addToolbarButton(appendName, appendRowLabel);
m_HasAppendButton = true;
}
/**
* Adds an add button to the TableView's toolbar with the specified label.
*/
public void addAppendButton(String label) {
removeAppendButton();
appendRowLabel = label;
addToolbarButton(appendName, label);
m_HasAppendButton = true;
}
/**
* Removes the add button from the toolbar.
*/
public void removeAppendButton() {
removeToolbarButton(appendName);
m_HasAppendButton = false;
}
/**
* Adds an insert button to the TableView's toolbar.
*/
public void addInsertButton() {
addToolbarButton(insertName, insertRowLabel);
m_HasInsertButton = true;
}
/**
* Adds an insert button to the TableView's toolbar.
*/
public void addInsertButton(String label) {
removeInsertButton();
insertRowLabel = label;
addToolbarButton(insertName, label);
m_HasInsertButton = true;
}
/**
* Removes the insert button to the TableView's toolbar.
*/
public void removeInsertButton() {
removeToolbarButton(insertName);
m_HasInsertButton = false;
}
/**
* Adds a delete button to the TableView's toolbar.
*/
public void addDeleteButton() {
addToolbarButton(deleteName, deleteRowLabel);
m_HasDeleteButton = true;
}
/**
* Adds a delete button to the TableView's toolbar with the specified label.
*/
public void addDeleteButton(String label) {
removeDeleteButton();
deleteRowLabel = label;
addToolbarButton(deleteName, label);
m_HasDeleteButton = true;
}
/**
* Removes the delete button to the TableView's toolbar.
*/
public void removeDeleteButton() {
removeToolbarButton(deleteName);
m_HasDeleteButton = false;
}
/**
* Adds a save button to the TableView's toolbar.
*/
public void addSaveButton() {
addToolbarButton(saveName, saveLabel);
m_HasSaveButton = true;
}
/**
* Adds a save button to the TableView's toolbar with the specified label.
*/
public void addSaveButton(String label) {
removeSaveButton();
saveLabel = label;
addToolbarButton(saveName, label);
m_HasSaveButton = true;
}
/**
* Removes the save button to the TableView's toolbar.
*/
public void removeSaveButton() {
removeToolbarButton(saveName);
m_HasSaveButton = false;
}
/**
* Adds a "restart row" button to the TableView's toolbar.
* @see #removeRestartButton
*/
public void addRestartButton() {
addToolbarButton(restartName, restartLabel);
m_HasRestartButton = true;
}
/**
* Adds a "restart row" button with the specified label to the TableView's toolbar.
* @param label the button label
* @see #removeRestartButton
*/
public void addRestartButton(String label) {
removeRestartButton();
restartLabel = label;
addToolbarButton(restartName, label);
m_HasRestartButton = true;
}
/**
* Removes the "restart row" button to the TableView's toolbar.
* @see #addRestartButton
*/
public void removeRestartButton() {
removeToolbarButton(restartName);
m_HasRestartButton = false;
}
/**
* Adds a delete button to the TableView's toolbar.
*/
public void addUndeleteButton() {
addToolbarButton(undeleteName, undeleteRowLabel);
m_HasUndeleteButton = true;
}
/**
* Adds a delete button to the TableView's toolbar with the specified label.
*/
public void addUndeleteButton(String label) {
removeUndeleteButton();
undeleteRowLabel = label;
addToolbarButton(undeleteName, label);
m_HasUndeleteButton = true;
}
/**
* Removes the delete button to the TableView's toolbar.
*/
public void removeUndeleteButton() {
removeToolbarButton(undeleteName);
m_HasUndeleteButton = false;
}
/**
* Adds the undo button to the TableView's toolbar.
*/
public void addUndoButton() {
addToolbarButton(undoName, undoRowLabel);
m_HasUndoButton = true;
}
/**
* Adds a undo button to the TableView's toolbar with the specified label.
*/
public void addUndoButton(String label) {
removeUndoButton();
undoRowLabel = label;
addToolbarButton(undoName, label);
m_HasUndoButton = true;
}
/**
* Removes the undo button to the TableView's toolbar.
*/
public void removeUndoButton() {
removeToolbarButton(undoName);
m_HasUndoButton = false;
}
Panel gotoPanel;
TextField gotoTextField;
Button gotoButton;
/**
* Add a goto button to the TableView's toolbar.
*/
public void addGotoButton() {
if (gotoPanel == null) {
gotoPanel = new Panel();
gotoPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0));
gotoPanel.add(gotoButton = new Button(gotoRowLabel));
gotoPanel.add(gotoTextField = new TextField(4));
gotoTextField.setName(gotoName);
tbAdapter.add(gotoTextField);
gotoButton.setName(gotoName);
tbAdapter.add(gotoButton);
} else {
return;
}
addToToolbar(gotoPanel);
m_HasGotoButton = true;
}
/**
* Removes the goto button to the TableView's toolbar.
*/
public void removeGotoButton() {
if (gotoPanel == null) {
return;
} else {
removeFromToolbar(gotoPanel);
tbAdapter.remove(gotoTextField);
tbAdapter.remove(gotoButton);
gotoPanel = null;
}
m_HasGotoButton = false;
}
ToolbarAdapter tbAdapter = new ToolbarAdapter(this);
/**
* Adds a button to the TableView's toolbar using the specified label. An adapter
* is used to automatically register the button created such that when the button
* is pushed, the event will be routed to the event handler, the data source, and
* to any registered objects interested in TableEvents.
*/
public void addToolbarButton(String name, String label) {
Enumeration e = toolbarComponents.elements();
Component c;
while(e.hasMoreElements()) {
c = (Component)e.nextElement();
if (c instanceof Button && ((Button)c).getLabel().equals(label)) {
return;
}
}
Button button = new Button(label);
button.setName(name);
tbAdapter.add(button);
addToToolbar(button);
}
/**
* Removes a button to the TableView's toolbar using the specified label. The
* button will no longer notify the TableView via the adapter after this method.
*/
public void removeToolbarButton(String name) {
Enumeration e = toolbarComponents.elements();
Component c;
while(e.hasMoreElements()) {
c = (Component)e.nextElement();
if (c instanceof Button && ((Button)c).getName().equals(name)) {
//BS: did not remove the button from the toolbar
removeFromToolbar(c);
//toolbarComponents.removeElement(c);
tbAdapter.remove((Button)c);
return;
}
}
}
/**
* Returns whether the specified component is located on the toolbar
* @param c The component to check the toolbar for containment
*/
public boolean isToolbarComponent(Object c) {
return toolbarComponents.contains(c) || c == gotoTextField || c == gotoButton;
}
/**
* Adds a component to the toolbar. If it is desired that events generate a TableEvent
* and be routed to the event handler, a ToolbarAdapter should be created and the
* the object registered with it. If the component is not a Button or TextField, the
* adapter class will need to be subclassed and specialized.
* @param c The component to add
*/
public void addToToolbar(Component c) {
toolbarComponents.addElement(c);
toolbar.add(c);
}
/**
* Removes an item from the toolbar
*/
public void removeFromToolbar(Component c) {
toolbarComponents.removeElement(c);
toolbar.remove(c);
}
/**
* Sets the column which does not scroll. All columns to the left of the
* locked column do not scroll, all of the columns to the right are
* scrollable.
* @param col The column to be locked, 0 if all columns should be scrollable.
*/
public void setLockedColumn(int col) {
lockedColumn = col - 1;
showScrollbars();
redrawAsync();
}
/**
* Gets the column which does not scroll. All columns to the left of the
* locked column do not scroll, all of the columns to the right are
* scrollable.
* @return The locked column, 0 if all columns are scrollable.
*/
public int getLockedColumn() { return lockedColumn+1; }
/**
* Gets whether a column is within the range of locked columns.
*/
public boolean isColumnLocked(int col) {
return lockedColumn!=-1 && col<=lockedColumn+1;
}
/**
* Sets whether the last column should be filled to the end of the visible
* space of the TableView
*/
public void setFillLastColumn(boolean fill) {
fillLastColumn = fill;
}
/**
* Gets whether the last column should be filled to the end of the visible
* space of the TableView
*/
public boolean getFillLastColumn() {
return fillLastColumn;
}
/**
* Set heading text and width for a column. The cell type created will be a ButtonCell.
* @param cell The cell to used for the heading.
* @param h string for heading text
* @param i number of column (one relative)
* @param chars width of column in characters
*/
public /*synchronized */void setHeading(TableCell cell, String h, int col, int chars) {
cell.type(TableCell.COL_HEADING);
cell.setTableView(this, headingSource);
colHeadings.updateElement(0, col-1, cell);
try {
headingSource.setData(cell.getCoordinates(), new ImageStringData(dataSource, h));
} catch(TypeNotSupported ex) {}
setHeadingSize(col, chars);
if (autoRedraw) {
redrawAsync();
}
}
/**
* Set heading text and width for a column. The cell type created will be a ButtonCell.
* @param h string for heading text
* @param i number of column (one relative)
* @param chars width of column in characters
*/
public /*synchronized*/ void setHeading(String h, int col, int chars) {
ButtonCell cell = new ButtonCell(1, col);
setHeading(cell, h, col, chars);
}
/**
* Set column width of a column.
* @param col column number to set width
* @param chars width of column in characters
*/
public /*synchronized*/ void setHeadingSize(int col, int chars) {
CellHints hints = getHeadingHints(0, col);
int width = getFontMetrics(hints.font).charWidth('W') * chars;
setColumnSize(col, width);
}
/**
* Sets the preferred row count. Used to determine the preferred size
*/
public void setPreferredRowCount(int c) { preferredRowCount = c; }
/**
* Gets the preferred row count. Used to determine the preferred size
*/
public int getPreferredRowCount() { return preferredRowCount; }
/**
* Sets the preferred width in pixels used to calculate preferredSize.
* @param pixels Number of pixels for preferred width. Use -1 to calculate
* preferred width based on column widths.
*/
public void setPreferredWidth(int pixels) {
preferredPixelWidth = pixels;
}
/**
* Gets the number of pixels specified as the desired width of the TableView.
* @return The number of pixel specified using setPreferredWidth, -1 otherwise.
*/
public int getPreferredWidth() { return preferredPixelWidth; }
/**
* Returns the preferred size of this component.
* @see #minimumSize
*/
public Dimension preferredSize() {
return getPreferredSize();
}
/**
* Returns the preferred size of this component.
* @see #getMinimumSize
*/
public Dimension getPreferredSize() {
int w=0, h=0;
w = splitters[splitters.length-1] + rowHeadingWidth;
h = (preferredRowCount + 1) * cellHeight;
//compensate for hidden columns
int cols = cols();
for (int i=0; i<cols; i++) {
if (isColumnVisible(i+1)) { w -= getColumnWidth(i+1); }
}
Dimension toolsize = toolbar.preferredSize();
return new Dimension(Math.max(w, toolsize.width),
h + toolsize.height);
}
/**
* Returns the minimum size of this component.
* @see #getPreferredSize
* @see java.awt.LayoutManager
*/
public Dimension getMinimumSize() {
return getPreferredSize();
}
/**
* Returns the minimum size of this component.
* @see #preferredSize
* @see LayoutManager
*/
public Dimension minimumSize() {
return getPreferredSize();
}
/**
* Create specified number of columns for TableView.
* @param i number of columns for TableView
*/
public /*synchronized*/ void createColumns(int i) {
cellAttributes.removeAllElements();
headingAttributes.removeAllElements();
//set the corner cell hints
headingAttributes.addElement(0, 0, new CellHints(this));
//set cell and heading hints
for(int col=1;col<=i;col++) {
//always a valid cell hints for each column
cellAttributes.addElement(0, col, new CellHints(this));
//set the heading hints with default color of black on lightGray
CellHints headingHints = new CellHints(this);
headingHints.bg = Color.lightGray;
headingAttributes.addElement(0, col, headingHints);
}
splitters = new int[i+1];
colHidden = new boolean[i];
}
/**
* Determines if a column is showable or hidden.
* @return True if column is showable, false if it has been designated a
* hidden column.
*/
public boolean isColumnVisible(int col) {
return !colHidden[col-1];
}
/**
* Sets the showable/hidden flag for a column.
* @param col The column number to set the flag.
* @param show True if the column should be showable, false to hide it.
*/
public void showColumn(int col, boolean show) {
colHidden[col-1] = !show;
redrawAsync();
}
//-----------------------------------------------------------------------------
// Heading APIs
//-----------------------------------------------------------------------------
/**
* Sets the cell to be used by the corner cell. The corner cell is in
* the upper left and is only visible if row headings are present.
*/
public /*synchronized*/ void setCornerCell(TableCell c) {
cornerCell = c.cloneCell();
cornerCell.setTableView(this, rowHeadingSource);
cornerCell.setCoordinates(new Coordinate(0, 1));
cornerCell.type(TableCell.CORNER_CELL);
}
/**
* Sets the cell type used to display the row headings. All row headings
* must be of the same type.
*/
public /*synchronized*/ void setDefaultRowHeadingCell(TableCell c) {
headingCell = c.cloneCell();
headingCell.setTableView(this, rowHeadingSource);
}
/**
* Sets the width of the row headings
*/
public /*synchronized*/ void setRowHeadingWidth(int w) {
rowHeadingWidth = w;
}
/**
* Gets the width of the row headings
*/
public /*synchronized*/ int getRowHeadingWidth() {
return rowHeadingWidth;
}
/**
* Sets the row heading style.
* @param s The style and must be one of BLANK or AUTONUMBER
*/
public /*synchronized*/ void setHeadingHeight(int h) {
headingHeight = h;
if (autoRedraw) {
redrawAsync();
}
}
/**
* Sets whether row headings should be displayed.
*/
public void useRowHeadings(boolean use) {
useRowHeadings = use;
}
/**
* Gets whether row headings should be displayed.
*/
public boolean isUsingRowHeadings() {
return useRowHeadings;
}
/**
* Sets the display height of cells.
*/
public /*synchronized*/ void setCellHeight(int h) {
int stdHeight = getFontMetrics(CellHints.stdFont).getHeight() + 4;
if (h < stdHeight) {
cellHeight = stdHeight;
} else {
cellHeight = h;
}
if (autoRedraw) { redrawAsync(); }
}
/**
* Sets the row heading style.
* @param s The style and must be one of BLANK or AUTONUMBER
*/
public /*synchronized*/ void setRowLabelHeadingStyle(int s) {
if (s < AUTONUMBER || s > USER_DEFINED) {
throw new IllegalArgumentException("Illegal heading label style");
}
rowHeadingLabelStyle = s;
if (s == AUTONUMBER) {
rowHeadingSource.doAutoNumbering(true);
}
}
/**
* Sets the row heading label alignment style.
* @param a The alignment style: LEFT, CENTER, RIGHT
*/
public /*synchronized*/ void setRowHeadingAlignment(int a) {
if (a < LEFT || a > RIGHT) {
throw new IllegalArgumentException("Illegal alignment style");
}
rowHeadingHints.align = a;
}
/**
* Gets the row heading alignment style
* @return The alignment style: LEFT, CENTER, RIGHT
*/
public int getRowHeadingAlignment() {
return rowHeadingHints.align;
}
/**
* Gets the row heading label style
* @return The alignment style: LEFT, CENTER, RIGHT
*/
public int getRowLabelHeadingStyle() {
return rowHeadingLabelStyle;
}
/**
* Gets the number of the first row in the TableView
*/
public int getFirstAutoNumber() {
return rowHeadingWidth;
}
/**
* Causes row headings to be visible displaying row numbers starting with
* the specified value.
* @param first The number of the first row
*/
public void setupAutonumbering(int first) {
setupAutonumbering(first, rowHeadingWidth);
}
/**
* Causes row headings to be visible displaying row numbers starting with
* the specified value and sets the row headings to a fixed width to start.
* @param first The number of the first row
* @param pixels The starting width of the row headings
*/
public /*synchronized*/ void setupAutonumbering(int first, int pixels) {
rowHeadingLabelStyle = AUTONUMBER;
rowHeadingSource.doAutoNumbering(true, first);
useRowHeadings = true;
rowHeadingWidth = pixels;
}
/**
* Sets the default value used when the row headings do not contain
* data for the row
*/
public /*synchronized*/ void setDefaultRowData(String s) {
rowHeadingSource.setDefaultData(new ImageStringData(dataSource, s));
}
/**
* Sets the default data used when the row headings do not contain
* data for the row
*/
public /*synchronized*/ void setDefaultRowData(Data d) {
rowHeadingSource.setDefaultData(d);
}
/**
* Return heading text of specified column.
*/
public String getHeading(int col) throws DataNotAvailable {
col--;
return headingSource.getData(0, col).toString();
}
/**
* Set the font of the heading text.
* @param f font instance for heading text.
* @param col the heading column to set (1 relative).
*/
public /*synchronized*/ void setHeadingFont(Font f, int col) {
CellHints hints = getHeadingHints(0, col);
hints.font = f;
headingHeight = getFontMetrics(f).getHeight();
if (autoRedraw) { redrawAsync(); }
}
/**
* Returns the font of the heading text
* @param col the heading column to get (1 relative).
*/
public Font getHeadingFont(int col) {
return getHeadingHints(0, col).font();
}
//-----------------------------------------------------------------------------
// Line style and line color Control APIs
//-----------------------------------------------------------------------------
/**
* Sets the horizontal line style for a column.
* @param col The column number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setColumnHorizontalLineStyle(int col, int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
col--;
CellHints hints = getColHints(col);
hints.setTopLineStyle(style);
hints.setBottomLineStyle(style);
}
/**
* Gets the horizontal line style for a column.
* @param col The column number to set the style.
* @return The horizontal line style.
*/
public int getColumnHorizontalLineStyle(int col) {
col--;
CellHints hints = getColHints(col);
return hints.lineTopStyle;
}
/**
* Sets the horizontal line style for a column.
* @param col The column number to set the style.
* @param c The horizontal line color.
*/
public void setColumnHorizontalLineColor(int col, Color c) {
col--;
CellHints hints = getColHints(col);
hints.setTopLineColor(c);
hints.setBottomLineColor(c);
}
/**
* Gets the horizontal line style for a column.
* @param col The column number to set the style.
* @return The color used for horizontal lines.
*/
public Color getColumnHorizontalLineColor(int col) {
col--;
CellHints hints = getColHints(col);
return hints.lineTopColor;
}
/**
* Sets the left line style for a column. This method is equivalant to calling
* setColumnRightLineStyle() for the previous column.
* @param col The column number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setColumnLeftLineStyle(int col, int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
col--;
CellHints hints = getColHints(col);
hints.setLeftLineStyle(style);
if (col >= 1) {
col--;
hints = getColHints(col);
hints.setRightLineStyle(style);
}
}
/**
* Gets the left line style for a column. This method is equivalant to calling
* getColumnRightLineStyle() for the previous column.
* @param col The column number to set the style.
* @return One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public int getColumnLeftLineStyle(int col) {
col--;
CellHints hints = getColHints(col);
return hints.lineLeftStyle;
}
/**
* Sets the left line color for a column. This method is equivalant to calling
* setColumnRightLineStyle() for the previous column.
* @param col The column number to set the style.
* @param c The line color for the left side of the column.
*/
public void setColumnLeftLineColor(int col, Color c) {
col--;
CellHints hints = getColHints(col);
hints.setLeftLineColor(c);
if (col >= 1) {
col--;
hints = getColHints(col);
hints.setRightLineColor(c);
}
}
/**
* Gets the left line color for a column. This method is equivalant to calling
* getColumnRightLineColor() for the previous column.
* @param col The column number to set the style.
* @return The line color for the left side of the column.
*/
public Color getColumnLeftLineColor(int col) {
col--;
CellHints hints = getColHints(col);
return hints.lineLeftColor;
}
/**
* Sets the right line style for a column. This method is equivalant to calling
* setColumnLeftLineStyle() for the next column.
* @param col The column number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setColumnRightLineStyle(int col, int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
col--;
CellHints hints = getColHints(col);
hints.setRightLineStyle(style);
if (col < cols()-1) {
col++;
hints = getColHints(col);
hints.setLeftLineStyle(style);
}
}
/**
* Gets the right line style for a column. This method is equivalant to calling
* getColumnLeftLineStyle() for the next column.
* @param col The column number to set the style.
* @return style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public int getRightLineStyle(int col) {
col--;
CellHints hints = getColHints(col);
return hints.lineRightStyle;
}
/**
* Sets the right line color for a column. This method is equivalant to calling
* setColumnLeftLineStyle() for the next column.
* @param col The column number to set the style.
* @param c The line color for the right side of the column.
*/
public void setColumnRightLineColor(int col, Color c) {
col--;
CellHints hints = getColHints(col);
hints.setRightLineColor(c);
if (col < cols()-1) {
col++;
hints = getColHints(col);
hints.setLeftLineColor(c);
}
}
/**
* Gets the right line color for a column. This method is equivalant to calling
* getColumnLeftLineColor() for the previous column.
* @param col The column number to set the style.
* @return The line color for the right side of the column.
*/
public Color getColumnRightLineColor(int col) {
col--;
CellHints hints = getColHints(col);
return hints.lineRightColor;
}
/**
* Sets the horizontal line style for a view.
* @param row The row number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setHorizontalLineStyle(int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
for(int col=0; col<cols(); col++) {
CellHints hints = getColHints(col);
hints.setTopLineStyle(style);
hints.setBottomLineStyle(style);
}
}
/**
* Sets the bottom line style for a view.
* @param row The row number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setSideLineStyle(int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
for(int col=0; col<cols(); col++) {
CellHints hints = getColHints(col);
hints.setLeftLineStyle(style);
hints.setRightLineStyle(style);
}
}
/**
* Sets the top line style for a row. This method is equivalant to calling
* setRowBottomLineStyle() for the previous row.
* @param row The row number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setRowTopLineStyle(int row, int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
row--;
CellHints hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setTopLineStyle(style);
//set the bottom style of the cell above
row--;
hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setBottomLineStyle(style);
}
/**
* Sets the top line color for a row. This method is equivalant to calling
* setRowBottomLineColor() for the previous row.
* @param row The row number to set the style.
* @param c The color of the row line.
*/
public void setRowTopLineColor(int row, Color c) {
row--;
CellHints hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setTopLineColor(c);
//set the bottom color of the cell above
if (row >= 1) {
row--;
hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setBottomLineColor(c);
}
}
/**
* Sets the bottom line style for a row. This method is equivalant to calling
* setRowTopLineStyle() for the next row.
* @param row The row number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setRowBottomLineStyle(int row, int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
row--;
CellHints hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setBottomLineStyle(style);
//set the line color for the row below
row++;
hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setTopLineStyle(style);
}
/**
* Sets the bottom line color for a row. This method is equivalant to calling
* setRowTopLineColor() for the next row.
* @param row The row number to set the style.
* @param c The color of the row line.
*/
public void setRowBottomLineColor(int row, Color c) {
row--;
CellHints hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setBottomLineColor(c);
//set the line color for the row below
row--;
hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setTopLineColor(c);
}
/**
* Sets the vertical line style for a row.
* @param row The row number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE.
*/
public void setRowVerticalLineStyle(int row, int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
row--;
CellHints hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setLeftLineStyle(style);
hints.setRightLineStyle(style);
}
/**
* Gets the vertical line style for a row.
* @param row The row number to set the style.
* @param style One of NO_LINE, THIN_LINE, THICK_LINE, DASHED_LINE; or -1 if row line
* style has not been overriden.
*/
public int getRowVerticalLineStyle(int row, int style) {
if (style < NO_LINE || style > DASHED_LINE) {
throw new IllegalArgumentException("Line style " + style + " is not valid");
}
row--;
CellHints hints = getRowHints(row);
if (hints != null) {
return hints.lineLeftStyle;
} else {
return -1;
}
}
/**
* Sets the vertical line color for a row.
* @param row The row number to set the style.
* @param c The color of the row line.
*/
public void setRowVerticalLineColor(int row, Color c) {
row--;
CellHints hints = getRowHints(row);
if (hints == null) {
hints = new CellHints(this);
addRowHint(row, hints);
}
hints.setLeftLineColor(c);
hints.setRightLineColor(c);
}
/**
* Gets the vertical line color for a row.
* @param row The row number to set the style.
* @return The color of the row line.
*/
public Color getRowVerticalLineColor(int row, int style) {
row--;
CellHints hints = getRowHints(row);
if (hints != null) {
return hints.lineLeftColor;
} else {
return null;
}
}
/**
* Sets the line styles for all of the sides for a cell.
* @param row The row of the cell.
* @param col The column of the cell.
* @param t The style for the top of the cell.
* @param l The style for the left of the cell.
* @param b The style for the bottom of the cell.
* @param r The style for the right of the cell.
*/
public void setCellLineStyles(int row, int col, int t, int l, int b, int r) {
row--; col--;
CellHints h = getCellHints(row, col);
if (h == null) {
h = new CellHints(this);
addCellHint(row, col, h);
}
h.setTopLineStyle(t);
h.setLeftLineStyle(l);
h.setBottomLineStyle(b);
h.setRightLineStyle(r);
//set style of cell above if not on first row
if (row >= 1) {
if ((h=getCellHints(row-1, col)) == null) {
h = new CellHints(this);
addCellHint(row-1, col, h);
}
h.setBottomLineStyle(t);
}
//set style of cell to left if not first column
if (col >= 1) {
if ((h=getCellHints(row, col-1)) == null) {
h = new CellHints(this);
addCellHint(row, col-1, h);
}
h.setRightLineStyle(l);
}
//set style of cell to cell below
if ((h=getCellHints(row+1, col)) == null) {
h = new CellHints(this);
addCellHint(row+1, col, h);
}
h.setTopLineStyle(b);
//set style of cell to right
if ((h=getCellHints(row, col+1)) == null) {
h = new CellHints(this);
addCellHint(row, col+1, h);
}
h.setLeftLineStyle(r);
}
/**
* Sets the line colors for all of the sides for a cell.
* @param row The row of the cell.
* @param col The column of the cell.
* @param t The color for the top of the cell.
* @param l The color for the left of the cell.
* @param b The color for the bottom of the cell.
* @param r The color for the right of the cell.
*/
public void setCellLineStyles(int row, int col, Color t, Color l, Color b, Color r) {
row--; col--;
CellHints hints = getCellHints(row, col);
if (hints == null) {
hints = new CellHints(this);
addCellHint(row, col, hints);
}
hints.setTopLineColor(t);
hints.setLeftLineColor(l);
hints.setBottomLineColor(b);
hints.setRightLineColor(r);
//set style of cell above if not on first row
if (row >= 1) {
if ((hints=getCellHints(row-1, col)) == null) {
hints = new CellHints(this);
addCellHint(row-1, col, hints);
}
hints.setBottomLineColor(t);
}
//set style of cell to left if not first column
if (col >= 1) {
if ((hints=getCellHints(row, col-1)) == null) {
hints = new CellHints(this);
addCellHint(row, col-1, hints);
}
hints.setRightLineColor(l);
}
//set style of cell to cell below
if ((hints=getCellHints(row+1, col)) == null) {
hints = new CellHints(this);
addCellHint(row+1, col, hints);
}
hints.setTopLineColor(b);
//set style of cell to right
if ((hints=getCellHints(row, col+1)) == null) {
hints = new CellHints(this);
addCellHint(row, col+1, hints);
}
hints.setLeftLineColor(r);
}
//-----------------------------------------------------------------------------
// Cell Control APIs
//-----------------------------------------------------------------------------
/**
* Gets the cell that currently possesses the keyboard focus.<p>
* WARNING: This is shared data - Do not hold onto this past current call!!
* @return The TableCell that has the keyboard focus.
*/
public TableCell getCurrentCell() {
return currSelectedCell;
}
/**
* Gets the data for the cell that currently has the keyboard focus.<p>
* WARNING: This is shared data - Do not hold onto this past current call!!
* @return The Data instance for the cell that has the keyboard focus.
* @exception DataNotAvailable If the DataSource does not currently have
* data for the cell
*/
public Data getCurrentCellData() throws DataNotAvailable {
if (currSelectedCell == null) return null;
return dataSource.getData(currSelectedCell.getCoordinates());
}
/**
* Gets the coordinate of the cell that has the keyboard focus
*/
public Coordinate getCurrentCellCoordinates() {
if (currSelectedCell == null) return null;
Coordinate c = currSelectedCell.getCoordinates();
//make one relative
return new Coordinate(c.row()+1, c.col()+1);
}
/**
* Ensures that no cell is marked as the current cell for editing
*/
public /*synchronized*/ void clearCurrentCell() {
if (currSelectedCell == null) {
return;
}
sendFocusEvents(currSelectedCell, null);
}
/**
* Sets the cell that has the keyboard focus to the one at the specified
* row and column
*/
public /*synchronized*/ void setCurrentCell(int row, int col) {
// System.out.println("TableView: setCurrentCell r:" + row + " c: " + col);
row--;
col--;
TableCell newSelection = null;
if (currSelectedCell == null || row != currSelectedCell.row() ||
col != currSelectedCell.col())
{
if (!cells.contains(row, col)) {
if (row < 0 || !validRow(row)
|| col < 0 || col >= cols())
{
//row and/or column not valid
return;
}
//then we need to use the default cell
newSelection = getUniqueCell(row, col);
} else {
newSelection = (TableCell)cells.elementAt(row, col);
}
sendFocusEvents(currSelectedCell, newSelection);
try {
dataSource.setCurrentRow(row);
} catch(TypeNotSupported ex) {
handleException(row, col, ex);
}
}
}
/**
* Sets the background color for a column
*/
public /*synchronized*/ void setColBgColor(int col, Color bg) {
CellHints h = getColHints(col-1);
h.bg = bg;
}
/**
* Sts the background color for a row
*/
public /*synchronized*/ void setRowBgColor(int row, Color bg) {
row--;
CellHints h = getRowHints(row);
if (h == null) {
h = new CellHints(this);
addRowHint(row, h);
}
h.set(CellHints.BG_BIT);
h.bg = bg;
}
/**
* Sets the background color for a cell
*/
public /*synchronized*/ void setCellBgColor(int row, int col, Color bg) {
row--; col--;
CellHints h = getCellHints(row, col);
if (h == null) {
h = new CellHints(this);
addCellHint(row, col, h);
}
h.set(CellHints.BG_BIT);
h.bg = bg;
}
/**
* Sets the foreground color for a column
*/
public /*synchronized*/ void setColFgColor(int col, Color fg) {
CellHints h = getColHints(col-1);
h.fg = fg;
}
/**
* Sets the foreground color for a row
*/
public /*synchronized*/ void setRowFgColor(int row, Color fg) {
row--;
CellHints h = getRowHints(row);
if (h == null) {
h = new CellHints(this);
addRowHint(row, h);
}
h.set(CellHints.FG_BIT);
h.fg = fg;
}
/**
* Sets the foreground color for a cell
*/
public /*synchronized*/ void setCellFgColor(int row, int col, Color fg) {
row--; col--;
CellHints h = getCellHints(row, col);
if (h == null) {
h = new CellHints(this);
addCellHint(row, col, h);
}
h.set(CellHints.FG_BIT);
h.fg = fg;
}
/**
* Set the font of all cell text.
* @param col the column to set the font
* @param f font instance for cell text
*/
public /*synchronized*/ void setColFont(int col, Font f) {
col--;
CellHints h = getColHints(col);
h.font = f;
cellHeight = getFontMetrics(f).getHeight() + 4;
if (autoRedraw) { redrawAsync(); }
}
/**
* Set the font of for a row.
* @param row the row to set the font
* @param f font instance for cell text
*/
public /*synchronized*/ void setRowFont(int row, Font f) {
row--;
CellHints h = getRowHints(row);
if (h == null) {
h = new CellHints(this);
addRowHint(row, h);
}
h.set(CellHints.FONT_BIT);
h.font = f;
}
/**
* Set the font of for a cell.
*/
public /*synchronized*/ void setCellFont(int row, int col, Font f) {
row--; col--;
CellHints h = getCellHints(row, col);
if (h == null) {
h = new CellHints(this);
addCellHint(row, col, h);
}
h.set(CellHints.FONT_BIT);
h.font = f;
}
/**
* Returns the current font setting for cell text.
*/
public Font getColFont(int col) {
col--;
return getColHints(col).font();
}
public /*synchronized*/ void setColDefaultCell(int col, TableCell c) {
col--;
CellHints h = getColHints(col);
h.setDefaultCell(c);
if (autoRedraw) { redrawAsync(); }
}
public /*synchronized*/ void setRowDefaultCell(int row, TableCell c) {
row--;
CellHints h = getRowHints(row);
if (h == null) {
h = new CellHints(this);
addRowHint(row, h);
}
h.setDefaultCell(c);
}
public TableCell getColDefaultCell(int col) {
col--;
return getColHints(col).defaultCell();
}
/**
* Sets whether the cells in a column may be edited
*/
public /*synchronized*/ void setColEditable(int col, boolean edit) {
// align must be LEFT, CENTER, or RIGHT
CellHints h = getColHints(col-1);
h.editable = edit;
}
/**
* Sets whether the cells in a row may be edited
*/
public /*synchronized*/ void setRowEditable(int row, boolean edit) {
row--;
CellHints h = getRowHints(row);
if (h == null) {
h = new CellHints(this);
addRowHint(row, h);
}
h.set(CellHints.EDITABLE_BIT);
h.editable = edit;
}
/**
* Sets whether a cell can be edited
*/
public /*synchronized*/ void setCellEditable(int row, int col, boolean edit) {
row--; col--;
CellHints h = getCellHints(row, col);
if (h == null) {
h = new CellHints(this);
addCellHint(row, col, h);
}
h.set(CellHints.EDITABLE_BIT);
h.editable = edit;
}
/**
* Clears the CellHints for a cell
*/
public /*synchronized*/ void clearCellHint(int row, int col, int bit) {
row--; col--;
CellHints h = getCellHints(row, col);
if (h != null) {
h.clear(bit);
}
}
/**
* Clears the CellHints for a row
*/
public /*synchronized*/ void clearRowHint(int row, int bit) {
row--;
CellHints h = getRowHints(row);
if (h != null) {
h.clear(bit);
}
}
/**
* Gets whether CellHints a set for a particular cell.
* @param row The cell row
* @param col The cell column
* @param bit The particular variable of the hint that is of interest as
* defined in CellHints
*/
public boolean isCellHintSet(int row, int col, int bit) {
row--; col--;
CellHints h = getCellHints(row, col);
if (h != null) {
return h.get(bit);
}
return false;
}
/**
* Gets whether CellHints a set for a particular row.
* @param row The cell row
* @param bit The particular variable of the hint that is of interest as
* defined in CellHints
*/
public boolean isRowHintSet(int row, int bit) {
row--;
CellHints h = getRowHints(row);
if (h != null) {
return h.get(bit);
}
return false;
}
/**
* Gets whether CellHints a set for a particular column.
* @param col The cell column
* @param bit The particular variable of the hint that is of interest as
* defined in CellHints
*/
public boolean getColEditable(int col) {
col--;
return getColHints(col).editable();
}
/**
* Sets whether the column heading of interest is editable.
*/
public /*synchronized*/ void setHeadingEditable(int col, boolean edit) {
CellHints h = getHeadingHints(0, col);
h.editable = edit;
}
/**
* Gets whether the column heading of interest is editable.
*/
public boolean getHeadingEditable(int col) {
return getHeadingHints(0, col).editable();
}
/**
* Set the heading foreground and background colors of the heading hints
* @fg foreground Color for heading text
* @bg background Color for heading text
*/
public /*synchronized*/ void setHeadingColors(int col, Color fg, Color bg) {
CellHints h = getHeadingHints(0, col);
h.fg = fg;
h.bg = bg;
if (autoRedraw) { redrawAsync(); }
}
/**
* Returns the color of the column heading foreground.
*/
public Color getHeadingFg(int col) {
CellHints h = getHeadingHints(0, col);
return h.fg;
}
/**
* Returns the color of the column heading background.
*/
public Color getHeadingBg(int col) {
CellHints h = getHeadingHints(0, col);
return h.bg;
}
/**
* Get the column size in pixels for the specified column.
* @param i number of column (one relative)
*/
public int getColumnSize(int i) {
return getColumnWidth(i-1);
}
/**
* Set the justification of the text for the specified column.
* @param i number of column (one relative)
* @param align one of the values LEFT, CENTER, or RIGHT
*/
public /*synchronized*/ void setColumnAlignment(int col, int align) {
// align must be LEFT, CENTER, or RIGHT
CellHints h = getColHints(col-1);
h.align = align;
h = getHeadingHints(0, col);
h.align = align;
}
/**
* Returns the current number of columns.
*/
public int cols() {
return splitters.length - 1;
}
/**
* Ensures the topRow variable is valid
*/
public void setTopRow() {
setTopRow(-1);
}
/**
* Sets the top row to be displayed when table is drawn.
*/
public /*synchronized*/ void setTopRow(int row) {
//visible from outside so row is one relative
if (row >= 1) {
if (vsb.isVisible()) {
vsb.setValue(row-1);
topRow = vsb.getValue();
} else {
topRow = row-1;
}
}
try {
int dataRows = dataSource.validDataRowRange(0, topRow+1);
} catch(DataNotAvailable exc) {
//no data so all up to cells
if (topRow <= cells.rows()-1) {
//there are enough cells so everything is OK
return;
}
//Need to reset topRow to valid number so how about 0
topRow = 0;
}
}
/**
* Gets the top row that is used when the TableView is drawn.<p>
* WARNING: The value returned is 0 relative
* @return The top row - 0 relative
*/
public int getTopRow() {
return topRow;
}
/**
* Gets the actual number of rows being painted.
*/
public int getNumberOfVisibleRows() {
setTopRow();
int count = (int)((height-3)/cellHeight) + 1;
try {
int dataRows = dataSource.validDataRowRange(topRow, topRow+count);
int lastCellRow = cells.rows()-1;
count = Math.min(count, Math.max(dataRows, lastCellRow)-topRow+1);
} catch(DataNotAvailable exc) {
//no data so all up to cells
if (count > cells.rows()) {
count = cells.rows()-topRow;
}
}
return count;
}
/**
* Sets the mouse capture to the currently selected cell.
*/
public void setCapture() {
if (rowHeadingCell(currCaptureCell) && currSelectedCell != currCaptureCell) {
currHeadingCell = null;
}
currCaptureCell = currSelectedCell;
}
/**
* Set mouse capture to a particular cell
*/
public void setCapture(TableCell c) {
if (rowHeadingCell(currCaptureCell) && c != currCaptureCell) {
currHeadingCell = null;
}
currCaptureCell = c;
}
/**
* Removes the mouse capture if a cell currently possess it.
*/
public void lostCapture() {
if (rowHeadingCell(currCaptureCell)) {
currHeadingCell = null;
}
currCaptureCell = null;
}
/**
* Gets whether a cell currently has mouse capture.
*/
public boolean cellHasCapture() {
return currCaptureCell != null;
}
/**
* Remove all rows from the TableView.
*/
public /*synchronized*/ void clear() {
clearAllSelections();
cells.removeAllElements();
dataSource.clear();
xDragLast = -1;
isDragging = false;
topRow = 0;
leftCol = lockedColumn+1/*next col*/ +1 /*one relative*/;
vsb.setValue(0);
hsb.setValue(0);
currSelectedCell = currCaptureCell = null;
if (autoRedraw) { redrawAsync(); }
}
/**
* Adds a cell to the TableView
* @param cell The cell to be displayed
* @param s The value for the cell
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public /*synchronized*/ void addCell(TableCell cell, String s) throws TypeNotSupported {
cell = cell.cloneCell();
cell.setTableView(this, dataSource);
cells.updateElement(cell.row(), cell.col(), cell);
dataSource.setData(cell.getCoordinates(),
new ImageStringData(dataSource, s));
if (autoRedraw) { redrawAsync(); }
}
/**
* Adds or changes the text of a row and column position.
* @param r index of row
* @param c index of column
* @param s String text for cell
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public /*synchronized*/ void addTextCell(int r, int c, String s) throws TypeNotSupported {
r--; c--;
dataSource.setData(r, c, new ImageStringData(dataSource, s));
if (autoRedraw) {
redrawAsync();
}
}
/**
* Adds or changes the image of a row and column position.
* @param r index of row
* @param c index of column
* @param i image instance for cell
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public /*synchronized*/ void addImageCell(int r, int c, Image i) throws TypeNotSupported {
r--; c--;
dataSource.setData(r, c, new ImageStringData(dataSource, i));
if (autoRedraw) {
redrawAsync();
}
}
/**
* Add/change contents of a cell, both text and image.
* @param r index of row
* @param c index of column
* @parma s string text for cell
* @param i image instance for cell
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public /*synchronized*/ void addCell(int r, int c, String s, Image i) throws TypeNotSupported {
r--; c--;
dataSource.setData(r, c, new ImageStringData(dataSource, s, i));
if (autoRedraw) {
redrawAsync();
}
}
/**
* Returns string text of specified cell.
* @param r index of row
* @param c index of column
* @exception DataNotAvailable cell data does not exist in the data sourse
*/
public String getCellText(int r, int c) throws DataNotAvailable {
r--; c--;
return dataSource.getData(r, c).toString();
}
/**
* Gets the data value for a specified cell
* @param r index of row
* @param c index of column
* @exception DataNotAvailable cell data does not exist in the data sourse
*/
public Data getCellData(int r, int c) throws DataNotAvailable {
r--; c--;
return dataSource.getData(r, c);
}
/**
* Gets the FontMetrics for the specified cell
*/
public FontMetrics getCellFontMetrics(TableCell c) {
Font f = getCellFont(c);
return getFontMetrics(f);
}
/**
* Inherited from Component.
*/
public void update(Graphics g) {
paint(g);
}
Rectangle getClientArea() {
Dimension d = size();
d.height -= bottomPanel.preferredSize().height;
d.width -= vsb.preferredSize().width;
return new Rectangle(0, 0, d.width, d.height);
}
boolean printMode;
/**
* Gets whether the TableView is currently printing.
*/
protected boolean printMode() { return printMode; }
/**
* Prints the TableView.
*/
public void print(Graphics g) {
if (!isVisible() || gg == null) { return; }
if (!(g instanceof PrintGraphics)) {
paint(g);
return;
}
synchronized(gg) {
try {
printMode = true;
Graphics realG = gg;
gg = g.create();
gg.setClip(getClientArea());
forceRedraw(false);
gg = realG;
super.print(g);
} finally {
printMode = false;
}
}
}
/**
* Inherited from Component.
*/
public void paint(Graphics g) {
if (g instanceof PrintGraphics) {
print(g);
return;
}
if ((width!=size().width) || height!=size().height) {
redrawAsync();
}
if (im == null) { return; }
g.drawImage(im, 0, 0, this);
}
/**
* Inherited from Component.
*/
public void addNotify() {
super.addNotify();
repaint();
}
/**
* Gets the number of rows
*/
public int rows() {
return Math.max(cells.rows(), dataSource.rows());
}
void showScrollbars() {
Dimension d = size(),
actualViewSize = new Dimension(),
reqdViewSize = new Dimension(),
hsbSize = bottomPanel.preferredSize(),
vsbSize = vsb.preferredSize();
boolean changed = false;
int reqdHeight = cells.rows();
int scrollableCols = cols() - (lockedColumn+1);
try {
int dataRows = dataSource.validDataRowRange(0,getPageSize().height+2+topRow);
reqdHeight = Math.max(reqdHeight, dataSource.rows());
} catch(DataNotAvailable exc) {}
actualViewSize.height = height - headingHeight - 3 - bottomPanel.size().height;
actualViewSize.width = d.width - rowHeadingWidth;
reqdViewSize.height = reqdHeight * cellHeight;
reqdViewSize.width = splitters[splitters.length-1];
//if locking column only need enough space to show scrollable columns to
//determine if horizontal scrollbar required.
if (lockedColumn != -1) {
reqdViewSize.width -= splitters[lockedColumn+1];
actualViewSize.width -= splitters[lockedColumn+1];
}
//compensate for hidden columns
int cols = cols();
int firstScrollableCol=-1, lastScrollableCol=-1;
for (int i=0; i<cols; i++) {
if (colHidden[i]) {
if (isColumnLocked(i+1)) {
//increase actual view size since locked column not visible
actualViewSize.width += getColumnWidth(i+1);
} else {
scrollableCols--;
//reduce required width by width of hidden column
reqdViewSize.width -= getColumnWidth(i+1);
}
} else {
//not hidden so adjust scroll indexes
if (!isColumnLocked(i+1)) {
if (firstScrollableCol == -1) {
firstScrollableCol = i;
}
}
}
}
//check to see if horizontal scrollbar will be shown
if (reqdViewSize.width > actualViewSize.width) {
//definitely reqd
actualViewSize.height -= hsbSize.height;
} else if (reqdViewSize.width > actualViewSize.width - vsbSize.width) {
//see if acutally need vertical scrollbar
if (reqdViewSize.height > actualViewSize.height - hsbSize.height) {
//should need veritcal vsb and thus hsb scrollbar
actualViewSize.height -= hsbSize.height;
}
}
//show vertical scrollbar if appropriate
if (reqdViewSize.height > actualViewSize.height) {
vsb.setValues(topRow, getPageSize().height, 0, reqdHeight-1);
vsb.setPageIncrement(getPageSize().height);
vsb.show();
changed = true;
actualViewSize.width -= vsbSize.width;
} else {
topRow = 0;
vsb.hide();
}
//show horizontal scrollbar if appropriate
if (reqdViewSize.width > actualViewSize.width) {
//determine what the last column completely visible is
int i = 1;
int row = 0, col = 0;
if (currSelectedCell != null) {
Coordinate c = currSelectedCell.getCoordinates();
row = c.row;
col = c.col;
}
int shiftLeft = splitters[leftCol - 1] + rowHeadingWidth;
for (;i<splitters.length; i++) {
if (splitters[i] - shiftLeft > actualViewSize.width) break;
}
if (vsb.isVisible()) {
actualViewSize.width -= vsb.size().width;
}
//set hsb values
int sliderWidth = (int)((float)actualViewSize.width/reqdViewSize.width*scrollableCols);
hsb.setValues(hsbPosition,
sliderWidth,
firstScrollableCol+1,
firstScrollableCol+scrollableCols);
hsb.setPageIncrement(sliderWidth);
hsb.show();
changed = true;
} else {
hsbPosition = leftCol = lockedColumn+1/*next col*/ +1 /*one relative*/;
hsb.hide();
}
if (changed) {
setupSpacer();
bottomPanel.validate();
doLayout();
}
}
void setupSpacer() {
if (vsb.isVisible() && hsb.isVisible()) {
spacer.setVisible(true);
int width = vsb.getPreferredSize().width;
spacer.setSize(width, width);
} else {
spacer.setVisible(false);
spacer.setSize(0, 0);
}
}
/**
* Inherited from Component.
*/
public void show() {
super.show();
}
/**
* Get the height of the heading row.
*/
public final int getHeadingHeight() {
return headingHeight;
}
/**
* Gets the height of each cell row.
*/
public final int getCellHeight() {
return cellHeight;
}
/**
* Gets the width of the specified column.
*/
public final int getColumnWidth(int col) {
col--;
if (fillLastColumn && col == cols()-1) {
return size().width - splitters[col] + 1;
} else {
return splitters[col+1] - splitters[col];
}
}
/**
* Gets whether the cell is the corner cell.
*/
public final boolean cornerCell(TableCell c) {
return c == cornerCell;
}
/**
* Gets whether the cell is a column heading cell.
*/
public final boolean colHeadingCell(TableCell c) {
return c.type() == TableCell.COL_HEADING;
}
/**
* Gets whether the cell is a row heading cell.
*/
public final boolean rowHeadingCell(TableCell cell) {
return cell != null &&
(cell == headingCell || cell == currHeadingCell
|| cell.type() == TableCell.ROW_HEADING);
}
/**
* Gets the bounding rectangle of a cell relative to the TableView
*/
public final Rectangle getCellBounds(TableCell cell) {
return getCellBounds(cell, null);
}
/**
* Gets the bounding rectangle of a cell relative to the TableView
* @param cell The cell to fetch the bounds
* @param b The rectangle object to place bounds in
*/
public final Rectangle getCellBounds(TableCell cell, Rectangle b) {
Coordinate p = cell.getCoordinates();
if (cell == cornerCell) {
if (b == null) {
b = new Rectangle(0, 0, rowHeadingWidth+1, headingHeight+2);
} else {
b.setBounds(0, 0, rowHeadingWidth+1, headingHeight+2);
}
return b;
} else if (rowHeadingCell(cell)) {
return getRowHeadingBounds(cell, b);
} else if (cell.type() == TableCell.COL_HEADING) {
return getHeadingCellBounds(p, b);
}
return getCellBounds(p, b);
}
/**
* Gets the height of the row. Presently, all rows have the same height.
* @return Row height in pixels.
*/
public final int getRowHeight(int row) {
row--;
//for now all rows have the same height
return cellHeight+1;
}
public final int getRowStartY(int row) {
row--;
return (row-topRow)*cellHeight + headingHeight + 2;
}
/**
* Gets the counds of the table cell at the specified coordinates.
*/
private final Rectangle getCellBounds(Coordinate c, Rectangle b) {
if (colHidden[c.col]) {
throw new IllegalArgumentException("Column # " + c.col + " is hidden");
}
int colStart[] = getColStartPositions();
int row = c.row;
int col = c.col;
int w = getColumnWidth(col+1);
int left = colStart[col];
//compensate for boundary
if (leftCol-1 == col) { left++; }
else { w++; }
if (b == null) {
b = new Rectangle();
}
if (w > width - rowHeadingWidth) {
w = width - rowHeadingWidth;
if (vsb.isVisible()) { w -= vsb.size().width; }
}
b.reshape(left,
getRowStartY(row+1),
w,
getRowHeight(row+1));
return b;
}
/**
* Gets the bounds of the column heading cell at the specified coordinate
* @param coords The coordinates of the heading
* @param b The rectangle to place the bounds values
*/
public final Rectangle getHeadingCellBounds(Coordinate coords, Rectangle b) {
int colStart[] = getColStartPositions();
int row = coords.row;
int col = coords.col;
int w = getColumnWidth(col+1);
int left = colStart[col];
if (col == 0) { left++; }
else { w++; }
if (b == null) {
b = new Rectangle();
}
if (w > width - rowHeadingWidth) {
w = width - rowHeadingWidth;
if (vsb.isVisible()) { w -= vsb.size().width; }
}
b.reshape(left, 0, w, headingHeight+2);
return b;
}
/**
* Gets the bounds of the row heading cell at the specified coordinate
* @param cell The row heading cell
* @param b The rectangle to place the bounds values
*/
public final Rectangle getRowHeadingBounds(TableCell cell, Rectangle b) {
if (b == null) {
b = new Rectangle();
}
b.x = 0;
b.y = getRowStartY(cell.row()+1);
b.width = rowHeadingWidth+1;
b.height = getRowHeight(cell.row()+1);
return b;
}
/**
* Redraws a particular cell only.<p>
* This function should only be called if selection has not changed since last
* redraw. This function is provided to more efficiently redraw individual
* cell updates
*/
public void redrawCell(TableCell c) {
redrawCell(c, true);
}
/**
* Redraws a particular cell only.<p>
* This function should only be called if selection has not changed since last
* redraw. This function is provided to more efficiently redraw individual
* cell updates
* @param c The cell to redraw
* @param makeVisible Specifies whether the cell should be scrolled
* to make visible before being drawn
*/
public final /*synchronized*/ void redrawCell(TableCell c, boolean makeVisible) {
redrawCell(c, makeVisible, true);
}
/**
* Redraws a particular cell only.<p>
* This function should only be called if selection has not changed since last
* redraw. This function is provided to more efficiently redraw individual
* cell updates
* @param c The cell to redraw
* @param makeVisible Specifies whether the cell should be scrolled
* to make visible before being drawn
* @param repaint Repaint whole TableView after redraw of cell.
*/
public final /*synchronized*/ void redrawCell(TableCell c, boolean makeVisible,
boolean repaint)
{
if (disableDrawing) { return; }
if (colHidden[c.col()]) {
//hidden so do not draw
return;
}
if (!isColumnDisplayed(c.col()+1) || !isRowDisplayed(c.row()+1)) {
//not visible so no need to paint just make the cell visible and
//the view will be redrawn automatically.
if (makeVisible) {
makeCellVisible(c.row()+1, c.col()+1);
}
return;
}
try {
if (gg == null) { return; }
if (!fetchMode) { dataSource.fetchMode(true); }
if (c != cornerCell && c.type() != TableCell.COL_HEADING
&& makeVisible && isCellVisible(c.row()+1, c.col()+1))
{
//make sure cell is visible if not heading
makeCellVisible(c.row()+1, c.col()+1);
}
hints.setHints(c);
//clear rectangle
gg.setColor(getBackground());
Rectangle r = hints.bounds();
//create a clipped graphics context.
Graphics cellG = gg.create();
Shape s = gg.getClip();
if (s != null) {
cellG.setClip(s);
}
cellG.clipRect(r.x, r.y, r.width, r.height);
cellG.fillRect(r.x, r.y, r.width-1, r.height-1);
cellG.setColor(getForeground());
c.drawCell(cellG, hints);
if (c == currSelectedCell && c.isCellTypeEditable()) {
frameCurrentCells();
}
if (repaint) {
//refresh the screen
paint(getGraphics());
}
} finally {
if (!fetchMode) { dataSource.fetchMode(false); }
}
}
/**
* Redraws a particular cell only.<p>
* This function should only be called if selection has not changed since last
* redraw. This function is provided to more efficiently redraw individual
* cell updates. This function is internal so it is 0 relative.
* @param row The row of the cell to redraw
* @param col The column of the cell to redraw
* @param makeVisible Specifies whether the cell should be scrolled
* to make visible before being drawn
*/
protected final void redrawCell(int row, int col, boolean makeVisible) {
redrawCell(row, col, makeVisible, true);
}
/**
* Redraws a particular cell only.<p>
* This function should only be called if selection has not changed since last
* redraw. This function is provided to more efficiently redraw individual
* cell updates. This function is internal so it is 0 relative.
* @param row The row of the cell to redraw
* @param col The column of the cell to redraw
* @param makeVisible Specifies whether the cell should be scrolled
* to make visible before being drawn
* @param repaint Repaint whole TableView after redraw of cell.
*/
protected final void redrawCell(int row, int col, boolean makeVisible,
boolean repaint)
{
if (disableDrawing) { return; }
if (!validRow(row)) {
//not asking to redraw a column heading
return;
} else if (col > -1 && !validCol(col)) {
//not asking to redraw a row heading
return;
} else if (col == -1 && validRow(row)) {
redrawRowHeadingCell(row);
return;
} else if (col == -1 && row == -1) {
//corner cell and we are not worried about it
return;
}
TableCell c = getUniqueCell(row, col);
redrawCell(c, makeVisible);
//vj: Redraw the row heading to show the state of the row
redrawRowHeadingCell(c.row());
drawTableBoundary();
}
/**
* Redraws all the cells in a row.
*/
public /*synchronized*/ void redrawRow(int row) {
if (disableDrawing) { return; }
row--;
if(!validRow(row)) {
return;
}
int cols = cols();
for (int col=0; col<cols; col++) {
redrawCell(row, col, false);
}
redrawRowHeadingCell(row);
}
public boolean isRowDisplayed(int row) {
row--;
//check if whole table is visible
if (!vsb.isVisible()) {
return true;
}
return row>=topRow && row<=topRow + getPageSize().height - 1;
//return row>topRow && row<=topRow + getPageSize().height - 1;
}
/**
* Gets whether a column is currently being displayed.
*/
public boolean isColumnDisplayed(int col) {
col--;
if ((col>lockedColumn && col<leftCol-1) || colHidden[col]) {
return false;
}
int colStart[] = getColStartPositions();
int width = getSize().width;
if (useRowHeadings) { width -= rowHeadingWidth; }
return colStart[col] < width;
}
//returns zero relative column number for last column that is visible,
// return -1 for no visible columns except row headings.
private int lastVisibleCol() {
int width = size().width;
int cols = cols();
if (useRowHeadings) {
width -= rowHeadingWidth;
if (width < 0) { return -1; }
}
for (int i=0; i<cols; i++) {
if (isColumnDisplayed(i+1)) {
width -= getColumnWidth(i+1);
if (width <= 0) {
return i;
}
}
}
return cols()-1;
}
/**
* Redraws the specified cell and all of the bordering cells
*/
public /*synchronized*/ void redrawAroundCell(TableCell c) {
if (disableDrawing) { return; }
//draw on all adjoining cells too
int row = c.row()-1;
int col = c.col();
try {
fetchMode = true;
dataSource.fetchMode(true);
//get the columns for this and adjoining cells
int cols[] = {-1/*row heading*/, col, cols()/*one too many*/};
//get the previous column
for (int i=col-1; i>=0; i--) {
if (isColumnDisplayed(i+1)) {
cols[0] = i;
break;
}
}
//get the next column
int num = cols();
for(int i=col+1; i<num; i++) {
if (isColumnDisplayed(i+1)) {
cols[2] = i;
break;
}
}
for (int i=0; i<=2; i++) {
for (int j=0; j<=2; j++) {
if (cols[j] > cols()-1) { continue; }
//redraw col heading
if (row+i < topRow) {
if (cols[j] < leftCol-1) {
//redraw corner cell
redrawCornerCell(gg.create());
}
redrawColHeadingCell(cols[j]);
} else {
//redraw regular cell
if (isCellVisible(row+i+1, cols[j]+1)) {
redrawCell(row + i, cols[j], false, false);
}
}
}
}
} finally {
fetchMode = false;
dataSource.fetchMode(false);
}
paint(getGraphics());
}
/**
* Gets the visible bounds of the drawing space required for the cells in
* the TableView.
*/
public Rectangle getTableBounds(Rectangle r) {
Dimension d = size();
r.reshape(0, 0, d.width, d.height);
//draw boundary around table panel
if (vsb.isVisible()) {
r.width -= vsb.size().width;
}
r.height -= bottomPanel.size().height;
r.width = splitters[splitters.length-1] - splitters[leftCol-1] + 1;
if (useRowHeadings) {
r.width += rowHeadingWidth;
}
//compensate for lockable columns
if (lockedColumn!=-1) {
r.width += splitters[lockedColumn+1];
}
//compensate for hidden columns
int colstart[] = getColStartPositions();
int cols = cols();
for (int i=0; i<cols; i++) {
if (colHidden[i] && colstart[i]!=-1) {
r.width -= getColumnWidth(i+1);
}
}
int visRows = (r.height-2)/cellHeight;
if (visRows + topRow > dataSource.rows()) {
r.height = headingHeight+2 + cellHeight*(dataSource.rows() - topRow)+2;
}
return r;
}
/**
* Draws the outline around the cells
*/
protected void drawTableBoundary() {
Dimension d = size();
Rectangle r = new Rectangle(0, 0, d.width, d.height);
//draw boundary around table panel
if (vsb.isVisible()) {
r.width -= vsb.size().width;
}
r.height -= bottomPanel.size().height;
gg.setColor(Color.black);
gg.drawRect(r.x, r.y, r.width-1, r.height-1);
//draw boundary around table
r.width = splitters[splitters.length-1] - splitters[leftCol-1] + 1;
if (useRowHeadings) {
r.width += rowHeadingWidth;
}
//compensate for locked columns
if (lockedColumn != -1) {
r.width += splitters[lockedColumn+1];
}
//compensate for hidden columns
int colstart[] = getColStartPositions();
int cols = cols();
for (int i=0; i<cols; i++) {
if (colHidden[i] && colstart[i]!=-1) {
r.width -= getColumnWidth(i+1);
}
}
int visRows = (r.height-2)/cellHeight;
if (visRows + topRow > dataSource.rows()) {
r.height = headingHeight+2 + cellHeight*(dataSource.rows() - topRow)+2;
}
gg.drawRect(r.x, r.y, r.width, r.height-1);
}
protected Rectangle getVisibleCellBoundary() {
Dimension d = size();
Rectangle r = new Rectangle(0, 0, d.width, d.height);
//get height of table panel
r.height -= bottomPanel.size().height;
//get boundary around table
r.width = splitters[splitters.length-1] - splitters[leftCol-1] + 1;
if (useRowHeadings) {
r.width += rowHeadingWidth;
}
int visRows = (r.height-2)/cellHeight;
if (visRows + topRow > dataSource.rows()) {
r.height = headingHeight+2 + cellHeight*(dataSource.rows() - topRow)+2;
}
return r;
}
/**
* Inserts a new row above the specified row number
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public /*synchronized*/ void insertRow(int beforeRow) throws TypeNotSupported {
beforeRow--;
dataSource.insertRow(beforeRow);
/*vj - let the new cell itself be the current cell
if (currSelectedCell != null &&
currSelectedCell.row() >= beforeRow)
{
currSelectedCell.setRow(currSelectedCell.row()+1);
}*/
if (autoRedraw) { redrawAsync(); }
}
/**
* Gets the state of the data in the specified row
* @return One of NEW_ROW, CLEAN_ROW, DELETED_ROW, MODIFIED_ROW as defined in
* DataSource
*/
public int rowState(int row) {
row--;
return dataSource.rowState(row);
}
/**
* Mark the specified row as not deleted
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public void undeleteRow(int row) throws TypeNotSupported {
row--;
dataSource.undeleteRow(row);
//BS: remove the * showed next to the row heading
/* try{
String text = rowHeadingSource.getData(row,0).toString();
if(text.endsWith("*"))
rowHeadingSource.setText(row,0,
text.substring(0,text.length()-1));
}catch(DataNotAvailable d){
//System.out.println("no data available");
}
*/
}
/**
* Mark the specified row as deleted
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public /*synchronized*/ void deleteRow(int row) throws TypeNotSupported {
row--;
// System.out.println("deleting row");
dataSource.deleteRow(row);
if (currSelectedCell != null) {
if(currSelectedCell.row() == row) {
currSelectedCell = currCaptureCell = null;
} else if (currSelectedCell.row() > row) {
currSelectedCell.setRow(currSelectedCell.row()-1);
}
}
//BS: we want to show that this row is scheduled for deletion
//add a * to the row heading
/* try{
rowHeadingSource.setText(row,0,
rowHeadingSource.getData(row,0).toString() + "*");
}catch(DataNotAvailable d){
//System.out.println("no data available");
}
*/
if (autoRedraw) { redrawAsync(); }
}
/**
* Appends a new row to the end of the TableView
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public int appendRow() throws TypeNotSupported {
int newRow;
newRow = dataSource.appendRow();
if (autoRedraw) { redrawAsync(); }
return newRow;
}
/**
* Creates a new cell and adds it to the Matrix used to store TableCells.
* @param cell The default cell to clone to make the new cell.
*/
public void addCellFromDefault(TableCell cell) {
TableCell c = cell.cloneCell();
cells.addElement(c.row(), c.col(), c);
if (currSelectedCell == cell) { currSelectedCell = c; }
if (currCaptureCell == cell) { currCaptureCell = c; }
}
/**
* Gets the default cell and sets the coordinates to the specified values
*/
protected TableCell getDefaultCell(int row, int col) {
CellHints hc = getCellHints(row, col);
CellHints hr = getRowHints(row);
CellHints h = getColHints(col);
TableCell cell = h.cascadeDefaultCell(hr, hc);
if (cell == null) {
cell = defaultCell;
}
Coordinate c = cell.getCoordinates();
cell.reset();
c.row = row;
c.col = col;
return cell;
}
/**
* Gets the cell at the specified coordinats. If the cell does not exist in
* the cells Matrix, then the default cell is returned.
*/
protected TableCell getCell(Coordinate c) {
return getCell(c.row(), c.col());
}
/**
* Gets the cell at the specified coordinats. If the cell does not exist in
* the cells Matrix, then the default cell is returned.
*/
protected TableCell getCell(int row, int col) {
if (currSelectedCell != null
&& currSelectedCell.row() == row && currSelectedCell.col() == col)
{
return currSelectedCell;
} else if (cells.contains(row, col)) {
return (TableCell)cells.elementAt(row, col);
} else {
return getDefaultCell(row, col);
}
}
//BS: fixed save so -row headings are correctly displayed
// -table is repainted, keeping the top row at the top
/**
* Requests the DataSource to save the current state of the TableView
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public void save() throws TypeNotSupported {
int top = getTopRow();//BS: 0 relative !!!
//System.out.println(top);
dataSource.commitData();
dataSource.save();
//BS:we have to update the rowheading as some rows can have been deleted
//rowHeadingSource.redoAutoNumbering();
//BS: look if that row number still exists ...
//modify rowstate in dbadatastore to return NONEXISTS_ROW if not valid row...
if(dataSource.rowState(top + 1) != DataSource.NONEXISTS_ROW)
//yes, draw grid with that row on top
setTopRow(top + 1);
else
//no draw with first row at top
setTopRow(1);
//vj: Clear current cell selection
clearCurrentCell();
redrawAsync();
}
/**
* Requests the DataSource to refresh its data. Same as refresh but for
* backward compatibility.
*/
public void restart() {
refresh();
}
/**
* Requests the DataSource to refresh its data.
*/
public void refresh() {
dataSource.refresh();
hideAuxControl();
currSelectedCell = null;
currCaptureCell = null;
topRow = 0;
redrawAsync();
}
/**
* Redraws the TableView's offscreen image. Call when through making changes to TableView.
* @param repaint If true, TableView will be repainte after it is redrawn
* offscreen.
*/
private /*synchronized*/ void redraw(boolean repaint) {
redraw();
if (repaint) {
paint(getGraphics());
}
}
/**
* Redraws the TableView. Call when through making changes to TableView. Does not
* force a redraw of the offscreen image.
*/
private /*synchronized*/ void redraw() {
Dimension d = size();
if ((width != d.width || height != d.height)
&& (d.width > 0 && d.height > 0))
{
im = createImage(width = d.width,
height = d.height);
if (im == null) return;
gg = im.getGraphics();
}
forceRedraw(false);
}
/**
* Forces the offscreen image to be redraw. It does not check for a size
* change since the last redraw.
*/
private /*synchronized*/ void forceRedraw(boolean repaint) {
if (gg == null) return;
gg.setColor(getBackground());
gg.fillRect(0,0,width,height);
try {
dataSource.fetchMode(true);
showScrollbars();
int count = getNumberOfVisibleRows();
drawHeadings(count);
drawRows(count, topRow);
moveAuxControl();
drawTableBoundary();
} finally {
dataSource.fetchMode(false);
}
if (repaint) {
paint(getGraphics());
}
}
void drawRows(int count, int cellRow) {
int r = 0;
MatrixEnumeration e = cells.elements();
TableCell c = null;
int x, w;
int cols = cols();
int hx=0, hy=0, hw=0, hh=0;
int startPos[] = getColStartPositions();
int currRow=-1, currCol=-1;
if (cellRow > 0) {
if (cellRow >= cells.rows()) {
e = null;
} else {
c = (TableCell)e.advanceTo(cellRow);
}
}
//create a new graphics so can change attributes without having to reset.
Graphics cellG = gg.create();
Shape clip = cellG.getClip();
if (clip==null) { clip = new Rectangle(0, 0, width, height); }
//iterate the rows and paint each one
int lastVisCol = lastVisibleCol();
while(count-- > 0) {
//iterate the cols of the current row
for (int col=0; col<cols && col<=lastVisCol; col++) {
if ((col>lockedColumn && col<leftCol-1) || colHidden[col]) { continue; }
w = getColumnWidth(col+1);
//check if cells matrix has a cell for current cell
while (e != null
&& (c==null || (e.currCol()<col && e.currRow()<=cellRow))
&& e.hasMoreElements())
{
c = (TableCell)e.nextElement();
currRow = e.currRow();
currCol = e.currCol();
if (currRow <= cellRow && currCol < col) {
//keep going till get to correct column
c = null;
}
}
if (currRow==cellRow && currCol==col) {
hints.setHints(c);
try {
c.drawCell(cellG, hints);
} finally {
cellG.setClip(clip);
}
c = null;
} else
if (currSelectedCell != null &&
//it is default cell, but could be currSelectedCell
currSelectedCell.row() == cellRow &&
currSelectedCell.col() == col)
{
hints.setHints(currSelectedCell);
hints.clip(cellG);
try {
currSelectedCell.drawCell(cellG, hints);
} finally {
cellG.setClip(clip);
}
} else {
//looks like this cell not set yet so use default
TableCell defCell = getDefaultCell(cellRow, col);
hints.setHints(defCell);
hints.clip(cellG);
try {
defCell.drawCell(cellG, hints);
} finally {
cellG.setClip(clip);
}
}
}
cellRow++;
r++;
}
if (currSelectedCell != null && currSelectedCell.isCellTypeEditable()) {
frameCurrentCells();
}
}
boolean drawFrame = true;
/**
* Sets whether current cell should have a frame drawn around it
*/
public boolean drawFrame() { return drawFrame; }
/**
* Gets whether current cell should have a frame drawn around it
*/
public boolean drawFrame(boolean draw) {
drawFrame = draw;
if (autoRedraw) {
redrawAsync();
}
return draw;
}
/**
* Draws the frame around the current cell. Multiple framed cells are not supported
* at this time. But as the name implies, it is coming.
*/
protected void frameCurrentCells() {
if (drawFrame && currSelectedCell != null) {
Rectangle r = getCellBounds(currSelectedCell);
if (r.x < rowHeadingWidth || r.y < headingHeight+2) { return; }
gg.setColor(Color.black);
gg.drawRect(r.x-1,r.y-1,r.width+1,r.height+1); //draw outside
gg.drawRect(r.x+1,r.y+1,r.width-3,r.height-3); //draw inside
}
}
/**
* Gets the X pixel for the left side of a colum (0 relative).
* @returns -1 if column is hidden or scrolled off to left.
*/
public int getColumnStartX(int col, boolean includeRowHeadings) {
//see if not visible due to scrolling or hidden
if (colHidden[col] || (col>lockedColumn && col<leftCol-1)) {
return -1;
}
int startsAt = splitters[col];
if (includeRowHeadings) {
startsAt += rowHeadingWidth;
}
//subtract if a column before 'col' is hidden or scrolled
for (int i=0; i<col; i++) {
if (colHidden[i] || (i>lockedColumn && i<leftCol-1)) {
startsAt -= getColumnWidth(i+1);
}
}
return startsAt;
}
/**
* Gets the left pixel position of each column. If a column is not visible
* b/c it is too far to the left then it is said to start at position -1.
* Notice that this means that determining the width of a column is not as
* simple as taking the diff between two starting positions.
*/
protected final int[] getColStartPositions() {
int starts[] = new int[splitters.length-1];
int adjust = -splitters[leftCol-1];
int len = starts.length;
if (lockedColumn != -1) {
adjust += splitters[lockedColumn+1];
}
int totalHidden = 0;
for (int col=0; col<len; col++) {
//if not visible b/c scrolled count as scrolled even if hidden
if (col<leftCol-1 && col>lockedColumn) {
starts[col] = -1;
continue;
} else if (colHidden[col]) {
starts[col] = -1;
totalHidden += (splitters[col+1] - splitters[col]);
} else if (col <= lockedColumn) {
starts[col] = splitters[col] - totalHidden;
} else {
starts[col] = splitters[col] + adjust - totalHidden;
}
if (useRowHeadings) {
starts[col] += rowHeadingWidth;
}
}
return starts;
}
/**
* If the event handler is set, the exception is passed to the event handler.<p>
* This function is 0 relative!!
*/
public void handleException(int row, int col, Exception ex) {
if (eventHandler != null) {
eventHandler.handleException(new Coordinate(row, col), ex);
}
}
/**
* Requests the DataSource to undo some action to the specified row
* @exception TypeNotSupported If action is not supported by the DataSource
*/
public void undoRow(int row) throws TypeNotSupported {
row--;
dataSource.undoRow(row);
}
/**
* Requests the DataSource to commit any changes made to the current cell
* since the last commit.
*/
public void commitCurrentCell() throws TypeNotSupported {
dataSource.commitData();
if (currSelectedCell != null) {
redrawCell(currSelectedCell);
}
}
/**
* Requests the DataSource to undo any changes made to the current cell
* since the last commit.
*/
public void undoCurrentCell() {
dataSource.rollbackCurrentData();
if (currSelectedCell != null) {
redrawCell(currSelectedCell);
}
}
void handleHeadingClick(MouseEvent e, int x, int y) {
int colstart[] = getColStartPositions();
if (x<=rowHeadingWidth && useRowHeadings) {
//it was a click in row header region
if ((x < rowHeadingWidth + clickMargin) &&
(x > rowHeadingWidth - clickMargin))
{
dragColumn = 0;
isDragging = true;
mouseDragged(e, x, y); //draw drag line immediately
} else {
//upper left corner clicked
if (rowHeadingCell(currCaptureCell)) {
currHeadingCell = null;
}
currCaptureCell = cornerCell;
CellEvent.generateMouseEvent(this, cornerCell, e, CellEvent.MOUSE_PRESSED);
return;
}
return;
} else {
//NOTE: dragColumn is 1 relative (==0 means row heading size change)
//check for changing size of row headings
if (useRowHeadings) {
if (x < rowHeadingWidth+clickMargin
&& x > rowHeadingWidth-clickMargin)
{
dragColumn = 0;
isDragging = true;
mouseDragged(e, x, y);
return;
}
}
// is it close enough to be a column size drag?
for (int i=0; i<colstart.length; i++) {
//if column not visible
if (colstart[i] == -1) { continue; }
int colEnd = colstart[i] + getColumnWidth(i+1);
if ((x < colEnd + clickMargin) &&
(x > colEnd - clickMargin)) {
dragColumn = i+1;
isDragging = true;
mouseDragged(e, x, y); //draw drag line immediately
return;
}
}
}
//get the heading for the coordinates
TableCell cell = (TableCell)colHeadings.elementAt(0, columnClicked(x));
//check to see if x is past last column
if (cell.col() == cols()-1) {
Rectangle bounds = getCellBounds(cell);
if (!bounds.contains(x, y)) {
//not really in cell so that's it, we are done.
return;
}
}
if (cell == null) {
return;
}
if (rowHeadingCell(currCaptureCell)) {
currHeadingCell = null;
}
currCaptureCell = cell;
CellEvent.generateMouseEvent(this, cell, e, CellEvent.MOUSE_PRESSED);
}
int resizeRowHeightFrom = -1;
boolean isDraggingRowHeight = false;
void handleRowHeadingClick(MouseEvent e, int x, int y) {
//System.out.println("Into handleRowHeadingClick");
if (resizeClick(x, y)) {
//row resize time
resizeRowHeightFrom = y;
isDraggingRowHeight = true;
mouseDragged(e, x, y); //draw drag line immediately
return;
}
//BS:deselect the currently selected cell, otherwise multiple row
//selection doesn't work
sendFocusEvents(currSelectedCell,null);
//create a new current heading cell since this could be a shared resource
//during event handling
//get the heading for the coordinates
currHeadingCell = headingCell.cloneCell();
Coordinate c = currHeadingCell.getCoordinates();
c.row = cellRow(y);
c.col = 0;
if (!validRow(c.row)) { return; }
//BS:set the currentRow to the one clicked
try{
dataSource.setCurrentRow(c.row);
}catch(TypeNotSupported ex){};
currCaptureCell = currHeadingCell;
CellEvent.generateMouseEvent(this, currCaptureCell, e, CellEvent.MOUSE_PRESSED);
}
void handleSuccessiveClick(MouseEvent e) {
//must only be called if no focus change
// track time elapsed since last mouseDown
long clickSpeed = e.getWhen() - clickTime;
TableCell cell = (currCaptureCell != null) ?currCaptureCell :currSelectedCell;
boolean handled = false;
if (cell == null) {
handled = false;
} else if (clickSpeed < CLICKTHRESHOLD) {
CellEvent.generateMouseEvent(this, cell, e, CellEvent.MOUSE_DOUBLE);
} else {
//single click so send mouse event to control
CellEvent.generateMouseEvent(this, cell, e, CellEvent.MOUSE_PRESSED);
}
clickTime = e.getWhen();
return;
}
int cellRow(int y) {
return ((int)(y-headingHeight-2)/cellHeight)+topRow;
}
int columnClicked(int x) {
int colstart[] = getColStartPositions();
if (x<rowHeadingWidth) {
return -1;
}
for (int i=0; i<colstart.length; i++) {
if (colstart[i] == -1) { continue; }
if (x<colstart[i]+getColumnWidth(i+1)) {
return i;
}
}
return cols()-1;
}
//BS: added setFocusToRow
void setFocusToRow(int row)
{
// System.out.println("Into setFocusToRow " + row);
row--;
if (currSelectedCell == null || currSelectedCell.row() != row)
{
//vj: replaced gotoRow with scrollUpDownAbsolute
// because gotoRow would now call dataSource.setCurrentRow(row).
// Also scrollUpDownAbsolute expects a zero-relative row number
//row++;
//gotoRow(row);
if(row != topRow){
scrollUpDownAbsolute(row);
}
}
else
{
row++;
redrawRow(row);
//BS: changed sendFocusEvents call
//sendFocusEvents(null, currSelectedCell);
sendFocusEvents(currSelectedCell,null);
}
}
//this method refreshes the grid without refreshing the datasource
//it is useful when the Grid receives notification that the datasource has changed
//(or restarted)
public void refreshView(){
hideAuxControl();
currSelectedCell = null;
currCaptureCell = null;
topRow = 0;
redrawAsync();
}
void sendFocusEvents(TableCell lost, TableCell get) {
//System.out.println("In sendFocusEvents");
CellEvent e = new CellEvent(this, lost, null, CellEvent.LOST_FOCUS);
if (lost != null) {
if (!lost.canLoseFocus()) {
//not a legal state so do not change
return;
}
//set selected cell to get so frame not drawn around cell
currSelectedCell = get;
lost.focusEvent(e);
lostCapture();
}
if (get == null) {
currSelectedCell = null;
return;
}
currSelectedCell = get;
e.setCell(get);
e.setID(e.GOT_FOCUS);
get.focusEvent(e);
//change currently selected row now
makeCellVisible(get.row()+1, get.col()+1);
//BS:the setCurrentRow method of the datasource was not used...
try{
if(lost == null)
dataSource.setCurrentRow(get.row());
else
if(lost.row() != get.row())
dataSource.setCurrentRow(get.row());//table row 0 is datasource row 0
}catch(TypeNotSupported ex){};
clickTime = 0;
}
/**
* Inherited from Component
*/
public void mouseExited(int x, int y) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
/**
* Inherited from Component
*/
public /*synchronized*/ void mousePressed(MouseEvent e, int x, int y) {
requestFocus();
// Check for a click on the heading area:
if (y<headingHeight+1) {
handleHeadingClick(e, x, y);
return;
}
Rectangle r = new Rectangle();
r = getTableBounds(r);
if (!r.inside(x, y)) {
return;
}
if (x<rowHeadingWidth && useRowHeadings) {
//click in row heading area
handleRowHeadingClick(e, x, y);
return;
}
//it's a click in the row area
//check for hit on the scrollbar
if (x > width) return;
// set new row selection
int row = cellRow(y);
int col = columnClicked(x);
TableCell newSelection = null;
if (currSelectedCell == null || row != currSelectedCell.row() ||
col != currSelectedCell.col())
{
if (!cells.contains(row, col)) {
//check for mouse below last row
if (!validRow(row)) {
return;
}
//then we need to use the default cell
newSelection = getDefaultCell(row, col).cloneCell();
newSelection.setDefaultFlag();
} else {
newSelection = (TableCell)cells.elementAt(row, col);
}
sendFocusEvents(currSelectedCell, newSelection);
if (cellHasCapture()) {
CellEvent.generateMouseEvent(this, newSelection, e, CellEvent.MOUSE_PRESSED);
}
} else {
//check for double click since didn't change cells
handleSuccessiveClick(e);
}
}
/**
* Determines whether the specified row exists in the TableView. <p>
* This function is 0 based!
*/
public boolean validRow(int row) {
if (row < 0) return false;
if (cells.rows()-1 > row) {
return true;
}
try {
dataSource.validDataRowRange(row, row);
} catch(DataNotAvailable exc) {
//Yep, it was a click below cells
return false;
}
return true;
}
/**
* Determines whether the specified column exists in the TableView. <p>
* This function is 0 based!
*/
public boolean validCol(int col) {
return col >= 0 && col < cols();
}
/**
* Inherited from Component
*/
public /*synchronized*/ void mouseDragged(MouseEvent e, int x, int y) {
if (isDragging) {
handleColDrag(x);
return;
} else if (isDraggingRowHeight) {
handleRowDrag(y);
return;
}
if (cellHasCapture()) {
CellEvent.generateMouseEvent(this, currCaptureCell, e, CellEvent.MOUSE_DRAGGED);
}
}
boolean captureByHeading() {
if (currCaptureCell == null) { return false; }
Coordinate c = currCaptureCell.getCoordinates();
return colHeadings.contains(c.row, c.col, currCaptureCell)
|| cornerCell == currCaptureCell;
}
boolean captureByRowHeading() {
if (currCaptureCell == null) { return false; }
return rowHeadingCell(currCaptureCell) || cornerCell == currCaptureCell;
}
Event translate(Event e) {
//get coords of current cell
TableCell cell = (currCaptureCell != null) ?currCaptureCell :currSelectedCell;
int row = cell.getCoordinates().row;
int col = cell.getCoordinates().col;
int currY = (row - topRow) * cellHeight + headingHeight + 2;
int currX = getColumnStartX(col, false);
//translate event's x and y to cell relative
if (!captureByRowHeading()) {
e.x -= (currX + rowHeadingWidth);
}
if (!captureByHeading()) {
e.y -= currY;
}
return e;
}
//reminder - dragColumn is 1 relative (0 means row heading size change)
void handleColDrag(int x) {
//fix any drag that went off the left side of cell
int startsAt;
if (dragColumn == 0) {
//dragging to change size of header area
x = Math.max(x, 0);
rowHeadingWidth = x;
} else if (x<(startsAt=getColumnStartX(dragColumn-1, true))) {
x = startsAt;
}
//erase prvious drag line and draw in new position
gg.setColor(getBackground());
gg.setXORMode(Color.gray);
gg.drawLine(xDragLast,0, xDragLast, size().height);
gg.drawLine(x, 0, x, size().height);
gg.setColor(Color.black);
gg.setPaintMode();
//save x position of drag line
xDragLast = x;
repaint();
}
int yDragLast = -1;
void handleRowDrag(int y) {
//erase prvious drag line and draw in new position
gg.setColor(getBackground());
gg.setXORMode(Color.black);
gg.drawLine(0, yDragLast, size().width, yDragLast);
gg.drawLine(0, y, size().width, y);
gg.setColor(Color.black);
gg.setPaintMode();
//save x position of drag line
yDragLast = y;
repaint();
}
/**
* Sets the size of a column as specified in pixels.
* @param col The column to be sized.
* @param width The number of pixels to size the column to.
*/
public /*synchronized*/ void setColumnSize(int col, int width) {
col--;
int diff = width - splitters[col+1] + splitters[col];
int len = splitters.length;
for (int i=col+1; i<len; i++) {
splitters[i] += diff;
}
}
/**
* Resizes the specified column so that the right side of the column
* is set to the requested pixel position. This method is one relative.
* @param col The column number to resize.
* @param rightSide The pixel position of the right side of the column.
* This position does not account for the possible presence of the
* row headings.
*/
protected final /*synchronized*/ void adjustColumnSide(int col, int rightSide) {
int diff;
int len = splitters.length;
diff = rightSide - (getColumnStartX(col-1,false) + getColumnWidth(col));
if (isColumnLocked(col)) {
//if locked column then this is the side
splitters[col] = rightSide;
} else {
//scrollable column
splitters[col] += diff;
}
for(int i=col+1;i<len;i++) {
splitters[i] += diff;
}
redrawAsync();
}
/**
* Inherited from Component
*/
public /*synchronized*/ void mouseReleased(MouseEvent e, int x, int y) {
if (isDragging) {
//erase drag line
gg.setColor(getBackground());
gg.setXORMode(Color.black);
gg.drawLine(xDragLast, 0, xDragLast, size().height);
gg.setColor(Color.black);
gg.setPaintMode();
//set column width to dragged position
if (dragColumn == 0) {
//changed header size
redrawAsync();
} else {
adjustColumnSide(dragColumn, xDragLast - rowHeadingWidth);
}
//turn off dragging
xDragLast = -1;
isDragging = false;
return;
} else if (isDraggingRowHeight) {
//erase drag line
gg.setColor(getBackground());
gg.setXORMode(Color.black);
gg.drawLine(0, yDragLast, size().width, yDragLast);
gg.setColor(Color.black);
gg.setPaintMode();
//set column width to dragged position
setCellHeight(cellHeight + y - resizeRowHeightFrom);
//turn off dragging
yDragLast = -1;
isDraggingRowHeight = false;
return;
} else {
if (cellHasCapture()) {
CellEvent.generateMouseEvent(this, currCaptureCell, e, CellEvent.MOUSE_RELEASED);
}
}
}
/**
* Inherited from Component
*/
public /*synchronized*/ void mouseMoved(MouseEvent ev, int x, int y) {
boolean isCloseEnough = false;
int colstart[] = getColStartPositions();
if (y<headingHeight && locate(x, y) instanceof Adjustable && vsb.isVisible()) {
//noop - over vertical scrollbar but still might want to change cursor so
//don't return yet.
} else
// Use resize cursor ?
if (useRowHeadings
&& x<rowHeadingWidth+clickMargin && y < headingHeight)
{
if ((x < rowHeadingWidth + clickMargin)
&& (x > rowHeadingWidth - clickMargin))
{
isCloseEnough = true;
}
} else if (y<headingHeight) {
// Moving mouse around in header area
// is it close enough to be a column size drag?
for (int i=1; i<colstart.length; i++) {
if (colstart[i]==-1) { continue; }
if ((x < colstart[i] + clickMargin) &&
(x > colstart[i] - clickMargin))
{
isCloseEnough = true;
}
//check the last column
int lastPos = colstart[colstart.length-1] + getColumnWidth(cols());
if ((x < lastPos + clickMargin) &&
(x > lastPos - clickMargin)) {
isCloseEnough = true;
}
}
} else if (useRowHeadings
&& x<rowHeadingWidth+clickMargin)
{
handleRowHeadingMove(x, y);
return;
}
int cursor = (isCloseEnough? Cursor.W_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR);
setCursor(Cursor.getPredefinedCursor(cursor));
}
boolean resizeClick(int x, int y) {
Rectangle b = getVisibleCellBoundary();
if (y > b.height) {
return false;
}
//adjust y so can determine if mouse within 3 pixels of boundary
y = y - (headingHeight + 2) + 3 - cellHeight;
if (y >= 0 && y % cellHeight < 6) {
//resize cursor
return true;;
}
return false;
}
boolean handleRowHeadingMove(int x, int y) {
//mouse is moving around in row headings
//set the cursor as appropriate
//for now assume constant cell height
Rectangle b = getVisibleCellBoundary();
int cursor = Cursor.DEFAULT_CURSOR;
if (y <= b.height) {
//adjust y so can determine if mouse within 3 pixels of boundary
y = y - (headingHeight + 2) + 3 - cellHeight;
if (y >= 0 && y % cellHeight < 6) {
//resize cursor
cursor = Frame.N_RESIZE_CURSOR;
} else {
//normal cursor
cursor = Frame.DEFAULT_CURSOR;
}
}
setCursor(Cursor.getPredefinedCursor(cursor));
return true;
}
/**
* Inherited from Component
*/
public /*synchronized*/ void keyReleased(KeyEvent e) {
if (currSelectedCell != null) {
CellEvent.generateKeyEvent(this, currSelectedCell, e, CellEvent.KEY_RELEASED);
}
}
/**
* Implements FocusListener interface.
*/
public /*synchronized*/ void focusLost(FocusEvent e) {
// if (tabbed) {
// //if visible and tabbed then the auxControl must have received focus
// if (auxControl!=null && !auxControl.isVisible()) {
// requestFocus();
// }
// tabbed = false;
// if (!auxControl.isVisible()) {
// requestFocus();
// }
// } else {
//need to deactivate cursor in current cell
if (currSelectedCell != null) {
currSelectedCell.deactivateCursor();
}
lostCapture();
hideAuxControl(true, false);
// }
}
/**
* Implements FocusListener interface.
*/
public /*synchronized*/ void focusGained(FocusEvent e) {
//need to activate the cursor in the current cell
if (currSelectedCell != null) {
currSelectedCell.activateCursor();
}
}
TableCell getUniqueCell(int row, int col) {
if (!cells.contains(row, col)) {
//then we need to use the default cell
TableCell cell = getDefaultCell(row, col).cloneCell();
cell.setDefaultFlag();
return cell;
}
return (TableCell)cells.elementAt(row, col);
}
/**
* Inherited from Component
*/
public /*synchronized*/ void keyPressed(KeyEvent e) {
int row, col;
boolean changed = false;
if (currSelectedCell == null) {
row = topRow;
col = 0;
} else {
row = currSelectedCell.row();
col = currSelectedCell.col();
}
int dataRows = -1;
try {
dataRows = dataSource.validDataRowRange(0, row+2);
} catch(DataNotAvailable exc) {}
switch (e.getKeyCode()) {
case KeyEvent.VK_ENTER:
if (currSelectedCell == null) { return; }
if (!currSelectedCell.loseFocusOnArrow()) {
CellEvent.generateKeyEvent(this, currSelectedCell,
e, CellEvent.KEY_PRESSED);
return;
}
//if shift down go up one level, o.w. fall through to down arrow code
if (e.isShiftDown()) {
if (row > 0) {
TableCell newSelection = getUniqueCell(row-1, col);
sendFocusEvents(currSelectedCell, newSelection);
}
break;
}
case KeyEvent.VK_DOWN:
if (currSelectedCell == null) { return; }
if (!currSelectedCell.loseFocusOnArrow()) {
CellEvent.generateKeyEvent(this, currSelectedCell,
e, CellEvent.KEY_PRESSED);
return;
}
if (row < cells.rows()-1 || row < dataRows) {
TableCell newSelection = getUniqueCell(row+1, col);
sendFocusEvents(currSelectedCell, newSelection);
}
break;
case KeyEvent.VK_TAB:
e.consume();
//if tab and shift pressed go left
if (e.isShiftDown()) {
if (col > 0) {
//get the previous not hidden cell in same row
int prev = -1;
for (int i=col-1; i>=0; i--) {
if (!colHidden[i]) {
prev = i;
break;
}
}
if (prev == -1) { return; }
TableCell newSelection = getUniqueCell(row, prev);
sendFocusEvents(currSelectedCell, newSelection);
}
break;
}
//if only tab pressed then go right
int numcols = cols();
if (col < numcols-1) {
int next = -1;
for (int i=col+1; i<numcols; i++) {
if (!colHidden[i]) {
next = i;
break;
}
}
if (next == -1) { return; }
TableCell newSelection = getUniqueCell(row, next);
sendFocusEvents(currSelectedCell, newSelection);
}
requestFocus();
break;
case KeyEvent.VK_RIGHT:
if (currSelectedCell == null) { return; }
if (!currSelectedCell.loseFocusOnArrow()) {
CellEvent.generateKeyEvent(this, currSelectedCell,
e, CellEvent.KEY_PRESSED);
return;
}
int cols = cols();
if (col < cols-1) {
int next = -1;
for (int i=col+1; i<cols; i++) {
if (!colHidden[i]) {
next = i;
break;
}
}
if (next == -1) { return; }
TableCell newSelection = getUniqueCell(row, next);
sendFocusEvents(currSelectedCell, newSelection);
}
break;
case KeyEvent.VK_LEFT:
if (currSelectedCell == null) { return; }
if (!currSelectedCell.loseFocusOnArrow()) {
CellEvent.generateKeyEvent(this, currSelectedCell,
e, CellEvent.KEY_PRESSED);
return;
}
if (col > 0) {
//get the previous not hidden cell in same row
int prev = -1;
for (int i=col-1; i>=0; i--) {
if (!colHidden[i]) {
prev = i;
break;
}
}
if (prev == -1) { return; }
TableCell newSelection = getUniqueCell(row, prev);
sendFocusEvents(currSelectedCell, newSelection);
}
break;
case KeyEvent.VK_UP:
if (currSelectedCell == null) { return; }
if (!currSelectedCell.loseFocusOnArrow()) {
CellEvent.generateKeyEvent(this, currSelectedCell,
e, CellEvent.KEY_PRESSED);
return;
}
if (row > 0) {
TableCell newSelection = getUniqueCell(row-1, col);
sendFocusEvents(currSelectedCell, newSelection);
}
break;
case KeyEvent.VK_PAGE_UP:
scrollPageUp();
changed = true;
break;
case KeyEvent.VK_PAGE_DOWN:
scrollPageDown();
changed = true;
break;
case KeyEvent.VK_END:
if (currSelectedCell == null || currSelectedCell.loseFocusOnArrow()) {
if (e.isControlDown()) {
setCurrentCell(rows(), cols());
} else {
//this call is one relative
setCurrentCell(currSelectedCell.row() + 1,
cols());
}
return;
}
case KeyEvent.VK_HOME:
if (currSelectedCell == null || currSelectedCell.loseFocusOnArrow()) {
if (e.isControlDown()) {
//this call is one relative
setCurrentCell(1, 1);
} else {
//this call is one relative
setCurrentCell(currSelectedCell.row() + 1, 1);
}
if (leftCol != hsb.getMinimum()) {
if (hsb.isVisible()) {
hsbPosition = leftCol = hsb.getMinimum();
} else {
hsbPosition = leftCol = 1;
}
redrawAsync();
}
}
default:
if (currSelectedCell != null) {
CellEvent.generateKeyEvent(this, currSelectedCell,
e, CellEvent.KEY_PRESSED);
return;
}
}
if (changed) redrawAsync();
}
/* public void asyncNotify(Vector args) {
redraw(true);
}*/
protected /* synchronized */ void myNotifyAll()
throws java.lang.IllegalMonitorStateException
{
// notifyAll();
redraw(true);
}
/**
* Request for an asynchronous redraw of the TableView. Calling this function can
* greatly increase the efficiency of scrolling and rapid cell movement.
*/
public void redrawAsync() {
if (disableDrawing) { return; }
//AsyncNotifier.notify(this);
try {
//setRedrawAgain(true);
try
{
myNotifyAll();
}
catch (java.lang.IllegalMonitorStateException e)
{
System.out.println(e.getMessage());
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
/**
* Inherited from Component
*/
public void adjustmentValueChanged(AdjustmentEvent e) {
Adjustable scroller = (Adjustable)e.getSource();
if (scroller == vsb) {
if (topRow != vsb.getValue()) {
switch(e.getAdjustmentType()) {
case AdjustmentEvent.UNIT_INCREMENT:
scrollLineDown(); break;
case AdjustmentEvent.BLOCK_INCREMENT :
scrollPageDown(); break;
case AdjustmentEvent.BLOCK_DECREMENT:
scrollPageUp(); break;
case AdjustmentEvent.UNIT_DECREMENT:
scrollLineUp(); break;
case AdjustmentEvent.TRACK:
scrollUpDownAbsolute(vsb.getValue());
break;
}
}
} else {
if (hsb.getValue() == hsbPosition) { return; }
hsbPosition = leftCol = hsb.getValue();
//compensate for hidden columns
for (int i=hsb.getMinimum()+1; i<leftCol; i++) {
if (i > colHidden.length) { break; }
if (colHidden[i]) { leftCol++; }
}
redrawAsync();
}
}
/**
* Scrolls to the specified row.
*/
public void gotoRow(int row) {
row--;
//System.out.println("row: " + row + " topRow: " + topRow);
//BS: we want to have the row 'row' at the top of our TableView
//vj: Change the current row of the data source to match with that
// of table view
try{
dataSource.setCurrentRow(row);
}catch(TypeNotSupported ex){};
if(row != topRow){
//if (row < topRow || row > topRow + getPageSize().height - 1) {
scrollUpDownAbsolute(row);
}
}
/**
* Scrolls up to the specified row.
*/
public /*synchronized*/ void scrollUpTo(int newTop) {
newTop = Math.max(0, newTop);
if (newTop != topRow) {
vsb.setValue(newTop);
//topRow = vsb.getValue();
//BS: we want the line were scrolled to, to be the top one
topRow = newTop;
redrawAsync();
}
}
/**
* Scrolls in the direction required to make the specified row visible.
*/
public void scrollUpDownAbsolute(int row) {
if (row < topRow) {
scrollUpTo(row);
} else {
scrollDownTo(row);
}
}
/**
* Scrolls the TableView one line up
*/
public void scrollLineUp() {
scrollUpTo(topRow - 1);
}
/**
* Scrolls one page up
*/
public void scrollPageUp() {
scrollUpTo(topRow - getPageSize().height + 1);
}
/**
* Scrolls down so the specified row will be visible.
*/
public /*synchronized*/ void scrollDownTo(int nextTop) {
//int lastRow = cells.rows()-1;
//BS: cells.rows() was always == 0 ...
int lastRow = dataSource.rows();
try {
lastRow = Math.max(lastRow,
dataSource.validDataRowRange(0, nextTop));
} catch(DataNotAvailable ex) {}
nextTop = Math.min(nextTop, lastRow);
if (nextTop >= topRow) {
vsb.setValue(nextTop);
//topRow = vsb.getValue();
//BS: we want the line were scrolled to, to be the top one
topRow = nextTop;
redrawAsync();
}
}
/**
* Scrolls down one line.
*/
public void scrollLineDown() {
scrollDownTo(topRow + 1);
}
/**
* Scrolls down one page.
*/
public void scrollPageDown() {
scrollDownTo(topRow + getPageSize().height - 1);
}
/**
* Gets whether a particular cell is currently being drawn.
*/
public boolean isCellVisible(Coordinate pos) {
if (pos == null) {
return false;
}
return isCellVisible(pos.row+1, pos.col+1);
}
/**
* Gets whether a particular cell is currently being drawn.
*/
public boolean isCellVisible(int row, int col) {
row--;
col--;
boolean changed = false;
int rc = cells.rows();
try {
rc = Math.max(rc, dataSource.validDataRowRange(0, row+1)+1);
} catch(DataNotAvailable ex) {}
row = Math.min(rc-1, row);
row = Math.max(0, row);
col = Math.min(col, splitters.length);
col = Math.max(0, col);
if (row < topRow
|| row > topRow + getPageSize().height
|| !isColumnDisplayed(col+1))
{
return false;
}
return true;
}
/**
* Makes the specified cell visible by scrolling as required.
*/
public /*synchronized*/ void makeCellVisible(int row, int col) {
row--;
col--;
boolean changed = false;
int rc = cells.rows();
try {
rc = Math.max(rc, dataSource.validDataRowRange(0, row+1)+1);
} catch(DataNotAvailable ex) {}
row = Math.min(rc-1, row);
row = Math.max(0, row);
col = Math.min(col, cols()-1/*zero relative*/);
col = Math.max(0, col);
// scroll UPWARD
if (row < topRow) {
topRow = row;
vsb.setValue(topRow);
topRow = vsb.getValue();
changed = true;
}
// scroll DOWNWARD
if (row > topRow + getPageSize().height - 1) {
topRow = row - getPageSize().height + 1;
vsb.setValue(topRow);
topRow = vsb.getValue();
changed = true;
}
// scroll LEFT
col++;
boolean scrollLeft = false;
if (col<leftCol && col>lockedColumn+1 && !colHidden[col-1]) {
hsbPosition = leftCol = col;
//compensate for hidden columns
for (int i=hsb.getMinimum()+1; i<col-1; i++) {
if (colHidden[i]) { hsbPosition--; }
}
hsb.setValue(hsbPosition);
scrollLeft = true;
changed = true;
}
// scroll RIGHT
int totalWidth = size().width-vsb.preferredSize().width;
if (getColumnStartX(col-1, true)+getColumnWidth(col) > totalWidth) {
hsbPosition = leftCol = col;
//compensate hsbPosition for hidden columns - it only counts non-hidden
//columns
for (int i=hsb.getMinimum()+1; i<col-1; i++) {
if (colHidden[i]) { hsbPosition--; }
}
hsb.setValue(hsbPosition);
changed = true;
}
if (changed) {
if (scrollLeft) {
redrawAsync();
} else {
redrawAsync();
}
}
}
boolean postEvent(int id, int num) {
Event e = new Event(this, id, new Integer(num));
return postEvent(e);
}
/**
* Get the number of visible columns not counting locked and hidden columns
* or row headings.
*/
Dimension getPageSize() {
//i is 1 based in this method b/c leftCol is 1 based
Dimension d = size();
int i = leftCol;
//take out the width for vsb and row headings and locked columns
if (vsb.isVisible()) { d.width -= vsb.size().width; }
if (lockedColumn!=-1) {
d.width -= getColumnStartX(leftCol-1, true);
}
//determine how many non-hidden columns are visible
int totalHidden=0, hiddenCols=0;
for (;i<splitters.length;i++) {
if (colHidden[i-1]) {
totalHidden += getColumnWidth(i);
hiddenCols++;
continue;
}
if (splitters[i]-splitters[leftCol-1]-totalHidden > d.width) {
break;
}
}
//set the width
d.width = i - leftCol - hiddenCols;
//compensate for the toolbar and set the height
d.height -= bottomPanel.size().height;
d.height = (d.height - headingHeight)/cellHeight;
return d;
}
void drawHeadings(int numRowsToDraw) {
//iterate headings and draw them
int r = 0;
MatrixEnumeration e = colHeadings.elements();
TableCell c = null;
int x, w;
int cols = cols();
//create a new graphics so can change attributes without having to reset.
Graphics headingG = gg.create();
Shape clip = gg.getClip();
if (clip==null) { clip = new Rectangle(0, 0, width, height); }
int lastVisCol = lastVisibleCol();
//iterate the cols of the current row
for (int col=0; col<cols && col<=lastVisCol; col++) {
if ((col>lockedColumn && col<leftCol-1) || colHidden[col]) { continue; }
// for (int col=leftCol-1; col<cols; col++) {
if (col>lockedColumn && col<leftCol-1 || colHidden[col]) { continue; }
w = getColumnWidth(col+1);
c = (TableCell)colHeadings.elementAt(0, col);
hints.setHints(c);
hints.clip(headingG);
try {
c.drawCell(headingG, hints);
} finally {
headingG.setClip(clip);
}
}
drawRowHeadings(headingG, numRowsToDraw);
}
synchronized void redrawColHeadingCell(int col) {
if (col < leftCol-1) {
//col heading not visible so get out now
return;
}
if (colHeadings.contains(0, col)) {
TableCell cell = (TableCell)colHeadings.elementAt(0, col);
hints.setHints(cell);
cell.drawCell(gg, hints);
}
}
synchronized void redrawRowHeadingCell(int row) {
if (!useRowHeadings || !validRow(row)) {
//not drawing row heading so get out quickly
return;
}
//if current cell is heading cell we need to draw it in proper state
if (rowHeadingCell(currCaptureCell) && currCaptureCell.row() == row) {
hints.setHints(currHeadingCell);
currHeadingCell.drawCell(gg, hints);
} else {
TableCell cell = headingCell.cloneCell();
cell.setRow(row);
hints.setHints(cell);
cell.drawCell(gg, hints);
}
}
void drawRowHeadings(Graphics headingG, int count) {
if (!useRowHeadings) {
//not drawing row heading so get out quickly
return;
}
TableCell cell = headingCell.cloneCell();
Coordinate c = cell.getCoordinates();
Shape clip = headingG.getClip();
for (int i=0; i<count; i++) {
cell.setRow(i + topRow);
hints.setHints(cell);
cell.drawCell(gg, hints);
}
//if current cell is heading cell we need to draw it in proper state
if (rowHeadingCell(currCaptureCell)) {
hints.setHints(currHeadingCell);
hints.clip(headingG);
try {
currHeadingCell.drawCell(headingG, hints);
} finally {
headingG.setClip(clip);
}
}
redrawCornerCell(headingG);
}
synchronized void redrawCornerCell(Graphics headingG) {
Shape clip = headingG.getClip();
if (clip==null) { clip = new Rectangle(0, 0, width, height); }
//draw corner cell
hints.setHints(cornerCell);
hints.clip(headingG);
try {
cornerCell.drawCell(headingG, hints);
} finally {
headingG.setClip(clip);
}
}
Frame frame() {
Container c = this;
while(!(c instanceof Frame)) {
c = c.getParent();
}
return (Frame)c;
}
//-------------------------------------------------------------------------
// Event methods
//-------------------------------------------------------------------------
/**
* Route a cell event to appropriate event handler function, data source,
* and then the registered listeners.
*/
public void routeTableEvent(TableEvent e) {
if (eventHandler != null) {
eventHandler.handleTableEvent(e);
}
dataSource.handleTableEvent(e);
processEvent(e);
}
/**
* Route a cell event to appropriate event handler function, data source,
* and then the registered listeners.
*/
public void routeEvent(CellEvent e) {
if (eventHandler != null) {
TableCell cell = e.getCell();
if (rowHeadingCell(cell)) {
eventHandler.handleRowHeadingEvent(e, cell);
} else if (colHeadingCell(cell)) {
eventHandler.handleColHeadingEvent(e, cell);
} else if (cornerCell(cell)) {
eventHandler.handleCornerCellEvent(e, cell);
} else {
//must be a cell
eventHandler.handleCellEvent(e, cell);
}
}
//send the event to the datasource
dataSource.handleCellEvent(e);
processEvent(e);
}
/**
* Processes events on this TreeView. If the event is an ActionEvent,
* it invokes the processActionEvent method, else it invokes its
* superclass's processEvent.
* @param e the event
*/
protected void processEvent(AWTEvent e) {
//vj: Commit current cell contents if the grid loses focus.
if ((e.getID() == java.awt.event.FocusEvent.FOCUS_LOST) && (currSelectedCell != null)) {
try{
commitCurrentCell();
} catch(Exception ex) {
//Can throw RuntimeExceptions so catch everything
//handleException(currSelectedCell.row(), currSelectedCell.col(), ex);
return;
}
}
if (e instanceof CellEvent) {
processCellEvent((CellEvent)e);
return;
} else if (e instanceof TableEvent) {
processTableEvent((TableEvent)e);
return;
}
super.processEvent(e);
}
/**
* The cell listeners that are registered to recieve event notifications.
*/
Vector cellListeners = new Vector();
/**
* The table listeners that are registered to recieve event notifications.
*/
Vector tableListeners = new Vector();
/**
* Adds the specified cell listener to recieve cell events from this view.
* @param l the cell event listener
*/
public void addCellListener(CellListener l) {
if (!cellListeners.contains(l)) {
cellListeners.addElement(l);
}
}
/**
* Removes the specified cell listener so that it no longer
* receives cell events from this view.
* @param l the cell event listener
*/
public void removeCellListener(CellListener l) {
cellListeners.removeElement(l);
}
/**
* Processes cell events occurring on this view by
* dispatching them to any registered CellEventListener objects.
* NOTE: This method will not be called unless cell events
* are enabled for this component; this happens when an CellListener
* object is registered via addCellListener().
* @param e the cell event.
*/
protected void processCellEvent(CellEvent e) {
Enumeration enum = cellListeners.elements();
while(enum.hasMoreElements()) {
CellListener l = (CellListener)enum.nextElement();
try {
l.cellEventPerformed(e);
} catch(Exception ex) {
System.err.println("Error occured during event processing");
ex.printStackTrace();
}
}
}
/**
* Adds the specified table listener to recieve table events from this view.
* @param l the table event listener
*/
public void addTableListener(TableListener l) {
if (!tableListeners.contains(l)) {
tableListeners.addElement(l);
}
}
/**
* Removes the specified table listener so that it no longer
* receives table events from this view.
* @param l the table event listener
*/
public void removeTableListener(TableListener l) {
tableListeners.removeElement(l);
}
/**
* Processes table events occurring on this view by
* dispatching them to any registered TableEventListener objects.
* NOTE: This method will not be called unless cell events
* are enabled for this component; this happens when an TableListener
* object is registered via addTableListener().
* @param e the cell event.
*/
protected void processTableEvent(TableEvent e) {
Enumeration enum = tableListeners.elements();
while(enum.hasMoreElements()) {
TableListener l = (TableListener)enum.nextElement();
try {
l.tableEventPerformed(e);
} catch(Exception ex) {
System.err.println("Error occured during event processing");
ex.printStackTrace();
}
}
}
/**
* Component on toolbar triggered an event. This will result in the firing of
* a TableEvent.
*/
public void toolbarActionPerformed(ActionEvent event) {
TableEvent e = new TableEvent(this, event, TableEvent.TOOLBAR_EVENT);
if (event.getSource() == gotoButton) {
e.setParameter(gotoTextField.getText());
}
routeTableEvent(e);
}
/**
* Processes mouse events occurring on this component by
* dispatching them to any registered MouseListener objects.
* Classes overriding this method should call super.processMouseEvent()
* to ensure default event processing continues normally.
*/
protected void processMouseEvent(MouseEvent e) {
int id = e.getID();
switch(id) {
case MouseEvent.MOUSE_PRESSED:
mousePressed(e, e.getX(), e.getY());
break;
case MouseEvent.MOUSE_RELEASED:
mouseReleased(e, e.getX(), e.getY());
break;
case MouseEvent.MOUSE_CLICKED:
//no op
break;
case MouseEvent.MOUSE_EXITED:
mouseExited(e.getX(), e.getY());
break;
case MouseEvent.MOUSE_ENTERED:
//no op
break;
}
//let the parent class process the event as normal
super.processMouseEvent(e);
}
/**
* Processes mouse motion events occurring on this component by
* dispatching them to any registered MouseMotionListener objects.
* Classes overriding this method should call super.processMouseMotionEvent()
* to ensure default event processing continues normally.
*/
protected void processMouseMotionEvent(MouseEvent e) {
if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
mouseDragged(e, e.getX(), e.getY());
} else if (e.getID() == MouseEvent.MOUSE_MOVED) {
mouseMoved(e, e.getX(), e.getY());
}
super.processMouseMotionEvent(e);
}
/**
* Processes key events occurring on this component by
* dispatching them to any registered KeyListener objects.
* NOTE: This method will not be called unless key events
* are enabled for this component; this happens when one of the
* following occurs:
* a) A KeyListener object is registered via addKeyListener()
* b) Key events are enabled via enableEvents()
* Classes overriding this method should call super.processKeyEvent()
* to ensure default event processing continues normally.
* @param e the key event
*/
protected void processKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
keyPressed(e);
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
keyReleased(e);
}
super.processKeyEvent(e);
}
//-----------------------------------------------------------------------------
// Search & Filter methods (well just search for now)
//-----------------------------------------------------------------------------
public int findSubstring(String query, int col, boolean forward, boolean useCase) {
col--;
if (col < 0 || col >= cols()) {
throw new IllegalArgumentException("col=" + (col+1) + " is not in view");
}
int start,
stop = rows() - 1;
if (!useCase) {
query = query.toUpperCase();
}
int step = forward ?1 :-1;
if (currSelectedCell != null) {
start = currSelectedCell.row() + step;
} else if (forward) {
start = 0;
} else {
start = rows() - 1;
stop = 0;
}
try {
int index = -1;
for (int row=start; row<=stop; row+=step) {
String curr = getCellText(row+1, col+1);
if (useCase) {
index = curr.indexOf(query);
} else {
index = curr.toUpperCase().indexOf(query);
}
if (index != -1) {
return row + 1;
}
}
} catch (DataNotAvailable ex) {
ex.printStackTrace();
}
return -1;
}
//-----------------------------------------------------------------------------
// AuxControl methods
//-----------------------------------------------------------------------------
Component auxControl = null;
Coordinate auxOwnerPos = null;
boolean auxShowing = false;
Matrix storage = new Matrix();
public /*synchronized*/ Component registerAuxControl(Component aux, TableCell owner) {
if (aux == null || owner == null) {
return null;
}
if (auxControl != null) {
unregisterAuxControl(getAuxOwner());
}
add(aux);
aux.hide();
auxControl = aux;
auxOwnerPos = owner.getCoordinates();
getAuxControlRegion();
return aux;
}
public Component registerAuxControl(TableCell owner) {
return registerAuxControl(getCellControl(owner), owner);
}
public Component getAuxControl(TableCell owner) {
if (!isAuxOwner(owner)) {
throw new IllegalArgumentException("Only control owner may unregister the aux control");
}
return auxControl;
}
public /*synchronized*/ TableCell getAuxOwner() {
if (auxControl == null) {
return null;
}
return getCell(auxOwnerPos);
}
public /*synchronized*/ void unregisterAuxControl(TableCell owner) {
if (!owner.getCoordinates().equals(auxOwnerPos)) {
throw new IllegalArgumentException("Only control owner may unregister the aux control");
}
remove(auxControl);
auxShowing = false;
auxControl = null;
owner.lostAuxControl();
auxOwnerPos = null;
}
public boolean isAuxOwner(TableCell cell) {
return cell.getCoordinates().equals(auxOwnerPos);
}
public void doLayout() {
super.doLayout();
//awt hack! Makes control fill entire area otherwise.
moveAuxControl();
}
public /*synchronized*/ void moveAuxControl() {
boolean visible = isCellVisible(auxOwnerPos);
if (auxControl != null && visible && auxShowing) {
Rectangle b = getAuxControlRegion();
auxControl.show();
auxControl.reshape(b.x, b.y, b.width, b.height);
auxControl.doLayout();
} else if (!visible) {
hideAuxControl(true, true);
}
}
public /*synchronized*/ void showAuxControl() {
if (auxControl == null) {
return;
}
auxShowing = true;
if (!isCellVisible(auxOwnerPos)) {
return;
}
moveAuxControl();
auxControl.show();
// tabbed = true;
auxControl.requestFocus();
}
public void hideAuxControl() {
hideAuxControl(false, true);
}
public /*synchronized*/ void hideAuxControl(boolean tempHide, boolean keepFocus) {
if (auxControl != null) {
auxControl.hide();
if (keepFocus) {
requestFocus();
}
if (!tempHide) {
auxShowing = false;
}
}
}
public boolean isAuxVisible() {
if (auxControl == null) {
return false;
}
return auxControl.isVisible();
}
public /*synchronized*/ Rectangle getAuxControlRegion() {
if (auxControl == null) {
throw new NullPointerException("AuxControl not set");
}
//get size
Dimension size = size();
//get cell bounds
Rectangle bounds = getCellBounds(getAuxOwner());
Dimension tbSize = toolbar.size();
//get the controls preferred size
Dimension prefSize = auxControl.preferredSize();
prefSize.width = Math.max(prefSize.width, bounds.width);
//try to put below first, if can't then put above if possible
//o.w. put it in the best place
if (bounds.y + bounds.height + prefSize.height < size.height - tbSize.height) {
//can put below. try to align with left first
if (bounds.x + prefSize.width < size.width) {
//flush left below
return setAuxArea(bounds.x, bounds.y+bounds.height,
prefSize.width, prefSize.height);
} else if (bounds.x + bounds.width - prefSize.width > 0) {
//flush right below
return setAuxArea(bounds.x + bounds.width - prefSize.width,
bounds.y + bounds.height,
prefSize.width, prefSize.height);
} else {
return bestFitBelow(size, bounds, prefSize);
}
} else if (bounds.y - prefSize.height > 0) {
//can put above
if (bounds.x + prefSize.width < size.width) {
//flush left above
return setAuxArea(bounds.x, bounds.y-prefSize.height+2,
prefSize.width, prefSize.height);
} else if (bounds.x + bounds.width - prefSize.width > 0) {
//flush right below
return setAuxArea(bounds.x + bounds.width - prefSize.width,
bounds.y-prefSize.height+2,
prefSize.width, prefSize.height);
} else {
return bestFitAbove(size, bounds, prefSize);
}
} else {
return bestFit(size, bounds, prefSize, tbSize);
}
}
Rectangle setAuxArea(int x, int y, int w, int h) {
return new Rectangle(x, y, w, h);
}
Rectangle bestFitBelow(Dimension size, Rectangle bounds, Dimension prefSize) {
//can't put flush left or right so put as far right as possible
if (prefSize.width > size.width) {
//too wide so reduce to TableView width
prefSize.width = size.width;
}
//put as far right as possible
return setAuxArea(size.width - prefSize.width, bounds.y + bounds.height,
prefSize.width, prefSize.height);
}
Rectangle bestFitAbove(Dimension size, Rectangle bounds, Dimension prefSize) {
//can't put flush left or right so put as far right as possible
if (prefSize.width > size.width) {
//too wide so reduce to TableView width
prefSize.width = size.width;
}
//put as far right as possible
return setAuxArea(size.width - prefSize.width, bounds.y - prefSize.height,
prefSize.width, prefSize.height);
}
Rectangle bestFit(Dimension size, Rectangle bounds, Dimension prefSize, Dimension tbSize) {
//can't put above or below full size so put where most room
if (prefSize.width > size.width) {
//too wide so reduce to TableView width
prefSize.width = size.width;
}
int above = bounds.y;
int below = size.height - bounds.y - bounds.height - tbSize.height;
int maxRoom = Math.max(above, below);
if (prefSize.height > maxRoom) {
//too tall so reduce to either height above or below cell
prefSize.height = maxRoom;
}
if (maxRoom == above) {
//put above
if (bounds.x + prefSize.width < size.width) {
//flush left above
return setAuxArea(bounds.x, bounds.y-prefSize.height+2,
prefSize.width, prefSize.height);
} else if (bounds.x + bounds.width - prefSize.width > 0) {
//flush right below
return setAuxArea(bounds.x + bounds.width - prefSize.width,
bounds.y-prefSize.height+2,
prefSize.width, prefSize.height);
} else {
return setAuxArea(size.width - prefSize.width,
0,
prefSize.width, above);
}
} else {
//put below
if (bounds.x + prefSize.width < size.width) {
//flush left below
return setAuxArea(bounds.x, bounds.y+bounds.height,
prefSize.width, prefSize.height);
} else if (bounds.x + bounds.width - prefSize.width > 0) {
//flush right below
return setAuxArea(bounds.x + bounds.width - prefSize.width,
bounds.y + bounds.height,
prefSize.width, prefSize.height);
} else {
//put all the way to right
return setAuxArea(size.width - prefSize.width,
bounds.y + bounds.height,
prefSize.width, below);
}
}
}
public void setStore(TableCell cell, Object s) {
storage.updateElement(cell.row(), cell.col(), s);
}
public Object getStore(TableCell cell) throws NullPointerException {
if (storage.contains(cell.row(), cell.col())) {
return storage.elementAt(cell.row(), cell.col());
}
throw new NullPointerException("No object store for cell row="
+ cell.row() + " col=" + cell.col());
}
public boolean isStored(TableCell cell) {
return storage.contains(cell.row(), cell.col());
}
//-----------------------------------------------------------------------------
// CellHints methods
//-----------------------------------------------------------------------------
/**
* Adds a CellHint dedicated to a row.
* <p>This fucntion is 0 relative
*/
public void addRowHint(int row, CellHints c) {
row++;
cellAttributes.addElement(row, 0, c);
}
/**
* Adds a CellHint dedicated to a cell.
* <p>This fucntion is 0 relative
*/
public void addCellHint(int row, int col, CellHints c) {
row++;
col++;
cellAttributes.addElement(row, col, c);
}
/**
* Gets a CellHint dedicated to a cell.
* <p>This fucntion is 0 relative
*/
public CellHints getCellHints(int row, int col) {
row++;
col++;
if (cellAttributes.contains(row, col)) {
return (CellHints)cellAttributes.elementAt(row, col);
} else {
return null;
}
}
/**
* Gets a CellHint dedicated to a row.
* <p>This fucntion is 0 relative
*/
public CellHints getRowHints(int row) {
row++;
if (cellAttributes.contains(row, 0)) {
return (CellHints)cellAttributes.elementAt(row, 0);
} else {
return null;
}
}
/**
* Gets a CellHint dedicated to a column.
* <p>This fucntion is 0 relative
*/
public CellHints getColHints(int col) {
//column attributes must be set
col++;
return (CellHints)cellAttributes.elementAt(0, col);
}
/**
* Gets the cell hints for a row heading.
*/
public final CellHints getRowHeadingHints(int row) {
return rowHeadingHints;
}
/**
* Gets the CellHints for a column heading<p>
* row is zero relative<br>
* col is one relative - with cornercell coord being 0
*/
public final CellHints getHeadingHints(int row, int col) {
return (CellHints)headingAttributes.elementAt(row, col);
}
public void putLineStyles(TableCell c, CellHints hints) {
if (colHeadingCell(c)) {
hints.cascadeLineStyles(null, null, getHeadingHints(c.row(), c.col()+1));
return;
} else if (c == cornerCell) {
hints.cascadeLineStyles(null, null, getHeadingHints(0, 0));
return;
} else if (rowHeadingCell(c)) {
hints.cascadeLineStyles(null, null, getRowHeadingHints(c.row()));
return;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
hints.cascadeLineStyles(hr, hc, h);
}
/**
* Gets the cell alignment for the specified cell
*/
public int getCellAlignment(TableCell c) {
if (colHeadingCell(c)) {
return getColHints(c.col()).align;
} else if (c == cornerCell || rowHeadingCell(c)) {
return getRowHeadingHints(c.row()).align;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
return h.cascadeAlignment(hr, hc);
}
/**
* Gets the cell foreground for the specified cell
*/
public Color getCellFG(TableCell c) {
if (printMode) { return Color.black; }
if (colHeadingCell(c)) {
return getHeadingHints(c.row(), c.col()+1).fg;
} else if (c == cornerCell) {
return getHeadingHints(0, 0).fg;
} else if (rowHeadingCell(c)) {
return getRowHeadingHints(c.row()).fg;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
return h.cascadeForeground(hr, hc);
}
/**
* Gets the cell background for the specified cell
*/
public Color getCellBG(TableCell c) {
if (printMode) { return Color.white; }
if (colHeadingCell(c)) {
return getHeadingHints(c.row(), c.col()+1).bg;
} else if (c == cornerCell) {
return getHeadingHints(0, 0).bg;
} else if (rowHeadingCell(c)) {
return getRowHeadingHints(c.row()).bg;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
return h.cascadeBackground(hr, hc);
}
/**
* Gets whether a specified cell is editable according to the CellHints
*/
public boolean getCellEditable(TableCell c) {
if (colHeadingCell(c)) {
return getHeadingHints(c.row(), c.col()+1).editable;
} else if (c == cornerCell) {
return getHeadingHints(0, 0).editable;
} else if (rowHeadingCell(c)) {
return getRowHeadingHints(c.row()).editable;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
return h.cascadeEditable(hr, hc);
}
/**
* Determines whether the specified cell is highlighted
*/
public boolean getCellHighlighted(TableCell c) {
//adjust for all highlight functions being 1 relative
int row = c.row() + 1;
int col = c.col() + 1;
if (colHeadingCell(c)) {
isColumnSelected(col);
} else if (c == cornerCell) {
return isViewSelected();
} else if (rowHeadingCell(c)) {
return isRowSelected(row) || isCellSelected(row, col);
}
//must be an honest to goodness cell
return isCellSelected(row, col);
}
/**
* Gets the font for the specified cell
*/
public Font getCellFont(TableCell c) {
if (colHeadingCell(c)) {
return getHeadingHints(c.row(), c.col()+1).font;
} else if (c == cornerCell) {
return getHeadingHints(0, 0).font;
} else if (rowHeadingCell(c)) {
return getRowHeadingHints(c.row()).font;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
return h.cascadeFont(hr, hc);
}
/**
* Gets the storage object associated with a cell.
*/
public Object getCellStorage(TableCell c) {
if (colHeadingCell(c)) {
return getHeadingHints(c.row(), c.col()+1).cellStorage;
} else if (c == cornerCell) {
return getHeadingHints(0, 0).cellStorage;
} else if (rowHeadingCell(c)) {
return getRowHeadingHints(c.row()).cellStorage;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
return h.cascadeStorage(hr, hc);
}
/**
* Gets the auxillary control for cell.
*/
public Component getCellControl(TableCell c) {
if (colHeadingCell(c)) {
return getHeadingHints(c.row(), c.col()+1).control;
} else if (c == cornerCell) {
return getHeadingHints(0, 0).control;
} else if (rowHeadingCell(c)) {
return getRowHeadingHints(c.row()).control;
}
CellHints hc = getCellHints(c.row(), c.col());
CellHints hr = getRowHints(c.row());
CellHints h = getColHints(c.col());
return h.cascadeControl(hr, hc);
}
//-----------------------------------------------------------------------------
// Selection control variables and member functions
//-----------------------------------------------------------------------------
//ALL OF THESE FUNCTIONS ARE ONE RELATIVE!!!!!!!
/**
* The bit set that defines which cells/rows/cols are selected. <BR>
* bit 0 = corner cell <BR>
* bit 1 - # of cols = columns <BR>
* bit (# of cols) * row + 1 = rows <BR>
* all other bits represent individual cells
*/
BitSet selected = new BitSet();
Coordinate selectionBase = null;
Coordinate selectionLimit = null;;
boolean selectionMade = false;
/**
* Gets whether the specified row is selected
*/
public boolean isRowSelected(int row) {
return selected.get(tx4Sxn(row));
}
/**
* Gets whether the specified cell is selected
*/
public boolean isCellSelected(int row, int col) {
return selected.get(tx4Sxn(row, col));
}
/**
* Gets whether the specified column is selected
*/
public boolean isColumnSelected(int col) {
return selected.get(col);
}
/**
* Gets whether the whole TableView is selected
*/
public boolean isViewSelected() { return selected.get(0); }
public void toggleCell(int row, int col) {
if (!isRowSelected(row)) {
toggleBit(tx4Sxn(row, col));
}
}
/**
* Gets a list of the coordinates for all of the selected cells
*/
public Coordinate[] getSelectedCells() {
Vector cells = new Vector();
int cols = cols();
int rows = sxnRows(cols);
for (int row=1;row<rows;row++) {
for (int col=1; col<cols; col++) {
if (selected.get(tx4Sxn(row, col))) {
cells.addElement(new Coordinate(row, col));
}
}
}
Coordinate sxn[] = new Coordinate[cells.size()];
cells.copyInto(sxn);
return sxn;
}
/**
* Gets the row number of the first selected row
* @return The first selected row, -1 if none are selected
*/
public int getFirstSelectedRow() {
int cols = cols();
int total = selected.size();
int row = 1;
for (int index=cols+1; index<total; index+=(cols+1), row++) {
if (selected.get(index)) {
return row;
}
}
return -1;
}
/**
* Gets an array of all of the selected rows
*/
public int[] getSelectedRows() {
int cols = cols();
int total = selected.size();
int srows[] = new int[dataSource.rows()];
int row = 1;
int next = 0;
for (int index=cols+1; index<total; index+=(cols+1), row++) {
if (selected.get(index)) {
srows[next++] = row;
}
}
int sxn[] = new int[next];
System.arraycopy(srows, 0, sxn, 0, next);
return sxn;
}
/**
* Gets an array of all of the selected columns
*/
public int[] getSelectedCols() {
int cols = cols();
int scols[] = new int[cols];
int next = 0;
for (int col=1; col<=cols; col++) {
if (selected.get(col)) {
scols[next-1] = col;
next++;
}
}
int sxn[] = new int[next];
System.arraycopy(scols, 0, sxn, 0, next);
return sxn;
}
/**
* Determines whether the specified row is hightlighted
*/
public boolean isRowSet(int row) {
return selected.get(tx4Sxn(row));
}
/**
* Selects the specified row
*/
public /*synchronized*/ void setRow(int row, boolean setTo) {
int bit = tx4Sxn(row);
int cols = cols();
for (int i=0;i<=cols;i++) {
if (setTo) {
selected.set(bit+i);
} else {
selected.clear(bit+i);
}
}
selectionMade = true;
if (autoRedraw) { redrawRow(row); }
}
/**
* Toggles the selection on the specified row.
*/
public /*synchronized*/ void toggleRow(int row) {
int bit = tx4Sxn(row);
boolean set = selected.get(bit);
int cols = cols();
for (int i=0;i<=cols;i++) {
if (set) {
selected.clear(bit+i);
} else {
selected.set(bit+i);
}
}
selectionMade = true;
if (autoRedraw) { redrawRow(row); }
}
/**
* Toggles the selection for the specified column
*/
public /*synchronized*/ void toggleCol(int col) {
boolean set = selected.get(col);
int cols = cols();
int total = selected.size();
for (int i=col; i<=total; i+=(cols+1)) {
if (set) {
selected.clear(i);
} else {
selected.set(i);
}
}
selectionMade = true;
if (autoRedraw) { redrawAsync(); }
}
/**
* Either clears all cell selections or selects all cells.
*/
public /*synchronized*/ void toggleAll() {
if (selected.get(0)) {
//clear all bits
clearAllSelections();
} else {
//set all of them
int total = selected.size();
for (int i=0; i<total; i++) {
selected.set(i);
}
selectionMade = true;
}
if (autoRedraw) { redrawAsync(); }
}
/**
* Unselects all cells.
*/
public /*synchronized*/ void clearAllSelections() {
if (selectionMade) {
selectionBase = null;
selectionLimit = null;
selectionMade = false;
selected.xor(selected);
if (autoRedraw) { redrawAsync();; }
}
}
/**
* Sets the cell by which range selections are performed
*/
public void setSelectionBase(int row, int col) {
selectionBase = new Coordinate(row, col);
}
/**
* Determines whether a selection base cell has been set.
*/
public boolean isSelectionBaseSet() {
return selectionBase != null;
}
/**
* Determines if a range of cells are currently selected.
*/
public boolean isRangeSelected() {
return selectionLimit != null;
}
//need to provide for erasing values in cells then make public
void eraseRangeSelection() {
//need to turn autoRedraw off while doing this
setRange(false);
}
/*synchronized*/ void setRange(boolean setTo) {
//if value == true then set bits o.w. clear bits
if (selectionBase == null) { return; }
if (selectionLimit == null) {
selectionLimit = selectionBase;
}
int topRow = Math.min(selectionBase.row, selectionLimit.row);
int topCol = Math.min(selectionBase.col, selectionLimit.col);
int bottomRow = Math.max(selectionBase.row, selectionLimit.row);
int bottomCol = Math.max(selectionBase.col, selectionLimit.col);
//need to turn autoRedraw off while doing this
boolean auto = autoRedraw;
autoRedraw = false;
//check for two rows defining range
if (selectionBase.col == 0 && selectionLimit.col ==0) {
//two row selections made - this is easy case
for (int row=topRow; row<=bottomRow; row++) {
setRow(row, setTo);
}
}
autoRedraw = auto;
if (autoRedraw) { redrawAsync();; }
}
/**
* Clears all selections
*/
public void clearRangeSelection() {
eraseRangeSelection();
selectionBase = null;
selectionLimit = null;
}
/**
* Selects the range that spans from the currently selected base cell to
* the specified row and column.
*/
public /*synchronized*/ void selectRange(int limitRow, int limitCol) {
//if no base, then we do not select any rows
if (selectionBase == null) {
selectionBase = new Coordinate(limitRow, limitCol);
}
boolean auto = autoRedraw;
autoRedraw = false;
//erase old range and draw new one
eraseRangeSelection();
selectionLimit = new Coordinate(limitRow, limitCol);
setRange(true);
autoRedraw = auto;
if (autoRedraw) { redrawAsync();; }
}
/**
* Selects a range of cells and sets the base for future selections
*/
public void selectRange(int baseRow, int baseCol, int limitRow, int limitCol) {
//if previous selection erase
clearRangeSelection();
//create a new range base
selectionBase = new Coordinate(baseRow, baseCol);
selectRange(limitRow, limitCol);
}
final void toggleBit(int bit) {
selectionMade = true;
if (selected.get(bit)) {
selected.clear(bit);
} else {
selected.set(bit);
}
}
//1 relative
final int tx4Sxn(int row, int col) {
return tx4Sxn(row) + col;
}
//1 relative
final int tx4Sxn(int row) {
int cols = cols();
return (cols+1) * (row);
}
final int sxnRows(int cols) {
return (int)Math.ceil(selected.size() / (cols + 1f));
}
private boolean m_HasAppendButton = false;
public boolean getHasAppendButton() {
return m_HasAppendButton;
}
private boolean m_HasInsertButton = false;
public boolean getHasInsertButton() {
return m_HasInsertButton;
}
private boolean m_HasGotoButton = false;
public boolean getHasGotoButton() {
return m_HasGotoButton;
}
private boolean m_HasUndoButton = false;
public boolean getHasUndoButton() {
return m_HasUndoButton;
}
private boolean m_HasRestartButton = false;
public boolean getHasRestartButton() {
return m_HasRestartButton;
}
private boolean m_HasDeleteButton = false;
public boolean getHasDeleteButton() {
return m_HasDeleteButton;
}
private boolean m_HasUndeleteButton = false;
public boolean getHasUndeleteButton() {
return m_HasUndeleteButton;
}
private boolean m_HasSaveButton = false;
public boolean getHasSaveButton() {
return m_HasSaveButton;
}
}