/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.table;
import java.awt.Container;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.DefaultCellEditor;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.plaf.TableUI;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import com.jstatcom.model.JSCConstants;
import com.jstatcom.model.JSCData;
import com.jstatcom.model.JSCNArray;
import com.jstatcom.model.JSCTypeDef;
import com.jstatcom.model.JSCTypes;
import com.jstatcom.model.Scope;
import com.jstatcom.model.SymbolTable;
/**
* Abstract table to be subclassed by tables for data arrays, like
* JSCSArray and JSCNArray. Subclasses must
* provide implementations for setting the model according to the displayed data
* type. They should also overwrite createDefaultDataModel for a
* default model.
*
* @author Markus Kraetzig
*/
public abstract class JSCAbstractDataTable extends BorderTable {
private final Map colIndexEditableMap = new HashMap();
private final Map rowIndexEditableMap = new HashMap();
private JSCDataTableScrollPane enclosingScrollPane = null;
private TableCellRenderer cellRenderer = JSCCellRendererTypes.DEFAULT;
/**
* The MouseAdapter for triggering the right click popup menu.
*/
private MouseListener triggerPopUp = null;
/**
* The MouseAdapter for the table, usually one of
* DataTableMouseListenerTypes to edit by left mouse clicks.
*/
private MouseListener mouseListener = null;
/**
* The reference to the tablePopup. The default is the precision popup, but
* it can be changed.
*/
private JPopupMenu tablePopup = null;
private boolean isEditable = false;
private String symbolName = null;
private Scope symbolScope = Scope.GLOBAL;
/**
* Only referenced if explicitely set.
*/
private SymbolTable symbolTable = null;
private JSCTypeDef symbolTypeDef = null;
/**
* Default constructor. The dynamicColumnWidth property is
* set to true.
*/
public JSCAbstractDataTable() {
super();
setDynamicColumnWidth(true);
setRowSelectionAllowed(false);
}
/**
* Sets a columns editable.
*
* @param editable
* @param colIndex
* @param clearPreviousBefore clear previous editable states
*/
public void setColEditable(boolean editable, int colIndex,
boolean clearPreviousBefore) {
if (clearPreviousBefore)
colIndexEditableMap.clear();
colIndexEditableMap.put(colIndex, editable);
if (getModel() instanceof JSCAbstractTableModel)
((JSCAbstractTableModel) getModel())
.setColEditableMap(colIndexEditableMap);
}
/**
* Sets a rows editable.
*
* @param editable
* @param rowIndex
* @param clearPreviousBefore clear previous editable states
*/
public void setRowEditable(boolean editable, int rowIndex,
boolean clearPreviousBefore) {
if (clearPreviousBefore)
rowIndexEditableMap.clear();
rowIndexEditableMap.put(rowIndex, editable);
if (getModel() instanceof JSCAbstractTableModel)
((JSCAbstractTableModel) getModel())
.setRowEditableMap(rowIndexEditableMap);
}
/**
* Returns the scroll increment that completely exposes one new row or
* column (depending on the orientation).
*
* @param visibleRect
* view area visible within the viewport
* @param orientation
* either SwingConstants.VERTICAL or
* SwingConstants.HORIZONTAL
* @param direction
* less than zero to scroll up/left, greater than zero for
* down/right
* @return the "unit" increment for scrolling in the specified direction
* @see Scrollable#getScrollableUnitIncrement
*
* public int getScrollableUnitIncrement( Rectangle visibleRect, int
* orientation, int direction) { // rect00 from BorderTable return
* orientation == SwingConstants.VERTICAL ? rect00.height : rect00.width; }
*/
/**
* Gets whether table is editable. Default is false.
*
* @return true if editable
*/
public boolean isEditable() {
return isEditable;
}
/**
* Sets whether table is editable. Default is false.
*
* @param editable
* true if editable
*/
public void setEditable(boolean editable) {
isEditable = editable;
if (getModel() instanceof JSCAbstractTableModel)
((JSCAbstractTableModel) getModel()).setEditable(isEditable);
}
/**
* Gets the name of the symbol to display. The symbol is retrieved from the
* symbol table of the given scope.
*
* @return name of the symbol
*/
public String getSymbolName() {
return symbolName;
}
/**
* Sets the name of the symbol to display. The symbol is retrieved from the
* symbol table of the given scope.
*
* @param symbolName
* symbol name
* @throws IllegalArgumentException
* if symbolName is invalid
*/
public void setSymbolName(String symbolName) {
JSCConstants.checkNameThrowEx(symbolName);
this.symbolName = symbolName;
if (symbolTable != null)
setModelForSymbolName(symbolTable, symbolName);
else {
SymbolTable sTableFromScope = symbolScope.getSymbolTable(this);
if (sTableFromScope != null)
setModelForSymbolName(sTableFromScope, symbolName);
}
}
/**
* Gets the current scope, which defines which symbol table to use (
* local,global or upper).
*
* @return symbol scope
*/
public Scope getSymbolScope() {
return symbolScope;
}
/**
* Sets the symbol scope, which defines which symbol table to use (
* local,global or upper).
* This also sets a previously set symbol table to null.
*
* @param scope
* the symbol scope
* @throws IllegalArgumentException
* if (scope == null)
*/
public void setSymbolScope(Scope scope) {
if (scope == null)
throw new IllegalArgumentException("Argument was null.");
symbolScope = scope;
symbolTable = null;
// update because of new scope and only if table was not explicitely set
SymbolTable sTableFromScope = symbolScope.getSymbolTable(this);
if (sTableFromScope != null && symbolName != null)
setModelForSymbolName(sTableFromScope, symbolName);
updateSelectionData();
}
/**
* Notification from the UIManager that the L&F has changed.
* Replaces the current UI object with the latest version from the
* UIManager.
*
* Reimplementation of superclass method.
*
* @see JComponent#updateUI
*/
public void updateUI() {
// Update the UIs of the cell renderers, cell editors and header
// renderers.
if (cellRenderer != null)
((JComponent) cellRenderer).updateUI();
if (cellEditor != null)
((JComponent) ((DefaultCellEditor) cellEditor).getComponent())
.updateUI();
setUI((TableUI) UIManager.getUI(this));
if (isDynamicColumnWidth())
setColumnWidth(maxCellLength());
if (enclosingScrollPane != null) {
enclosingScrollPane.setColumnHeaderCellRenderer(enclosingScrollPane
.getColumnHeaderCellRenderer());
}
resizeAndRepaint();
}
/**
* Gets the mouse listener. Can be used to change table values by clicking
* on it. Default is null.
*
* @return a mouse listener
*/
public MouseListener getMouseListener() {
return mouseListener;
}
/**
* Sets the mouse listener. Can be used to change table values by clicking
* on it. Default is null. An enumeration of mouse listeners
* is given in JSCMouseListenerTypes.
*
* @param listener
* a mouse listener
*/
public void setMouseListener(MouseListener listener) {
if (mouseListener != null)
removeMouseListener(mouseListener);
mouseListener = listener;
if (mouseListener != null)
addMouseListener(mouseListener);
}
/**
* Gets the renderer for the cells. The default is
* JSCCellRendererTypes.DEFAULT.
*
* @return cell renderer that is used for all cells
*/
public TableCellRenderer getCellRenderer() {
return cellRenderer;
}
/**
* Sets the renderer for all cells. The default is
* JSCCellRendererTypes.DEFAULT.
*
* @param renderer
* a cell renderer
*/
public void setCellRenderer(TableCellRenderer renderer) {
cellRenderer = renderer;
TableModel m = getModel();
if (m != null) {
TableColumnModel cm = getColumnModel();
int n = cm.getColumnCount();
for (int i = 0; i < n; i++) {
TableColumn tc = cm.getColumn(i);
tc.setCellRenderer(cellRenderer);
}
}
updateUI();
}
/**
* Gets the RIGHT mouse popup for the table.
*
* @return a popup menu
*/
public JPopupMenu getTablePopup() {
return tablePopup;
}
/**
* Sets the RIGHT mouse popup for the table. An enumeration
* of popups can be found in JSCPopupTypes.
*
* @param menu
* a popup menu
*/
public void setTablePopup(JPopupMenu menu) {
tablePopup = menu;
if (tablePopup == null)
removeMouseListener(triggerPopUp);
else {
triggerPopUp = new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
tablePopup.processMouseEvent(e, null, null);
}
};
addMouseListener(triggerPopUp);
}
}
/**
* Gets a reference (not a copy) to the data object displayed in this table.
* If the underlying table model is not of type
* JSCAbstractTableModel,null is returned.
*
* Usage note:
* The returned data is a reference to the underlying JSCData
* object only until a new data object has been set. It should be used read
* only. Changes that should not affect this table must be made on a copy of
* the returned object.
*
* @return table data
*/
public JSCData getJSCData() {
if (dataModel instanceof JSCAbstractTableModel)
return ((JSCAbstractTableModel) dataModel).getJSCData();
return null;
}
/**
* Sets the data object to be displayed in this table. If this is set, then
* the symbol name be set to null. If
* data == null, then the table will be cleared and display
* zero rows and zero columns.
*
* @param data
* to display
*/
public void setJSCData(JSCData data) {
setSymbolName(null);
setModelForJSCData(data);
}
/**
* Overwrites supermethod to retrieve symbol table from component hierarchy
* if this table is displayed.
*/
public void addNotify() {
super.addNotify();
// only if table was not explicitely set
if (symbolTable == null) {
SymbolTable sTableFromScope = symbolScope.getSymbolTable(this);
if (sTableFromScope != null && symbolName != null) {
// updateSelectionData();
setModelForSymbolName(sTableFromScope, symbolName);
}
}
// set editable now because it might be pending
if (getModel() instanceof JSCAbstractTableModel) {
JSCAbstractTableModel model = (JSCAbstractTableModel) getModel();
model.setEditable(isEditable);
model.setColEditableMap(colIndexEditableMap);
model.setRowEditableMap(rowIndexEditableMap);
}
updateUI();
}
/**
* Overwrites supermethod to fire a "model" property change event.
*
* @param model
* the new table model
*/
public void setModel(TableModel model) {
super.setModel(model);
if (getModel() instanceof JSCAbstractTableModel) {
JSCAbstractTableModel m = (JSCAbstractTableModel) getModel();
m.setEditable(isEditable);
m.setColEditableMap(colIndexEditableMap);
m.setRowEditableMap(rowIndexEditableMap);
}
firePropertyChange("model", null, model);
}
/**
* Gets the explicitely set symbol table used by this table to retrieve
* symbols by their name. If non has been set before, null is
* returned.
*
* @return symbol table
*/
public SymbolTable getSymbolTable() {
return symbolTable;
}
/**
* Sets the table model for a data object. Implementations should throw an
* IllegalArgumentException if data is not of
* the correct type for the table. A null argument should be
* allowed for to make it possible to clear the table model. The method is
* called by setJSCData.
*
* @param data
* the new data for this table
*/
public abstract void setModelForJSCData(JSCData data);
/**
* Sets the table model according to the symbol defined by
* table and name. The type of the symbol to
* retrieve must be set by the implementing table. The method is called by
* setSymbolTable and setSymbolName.
*
* The implementation should also handle null aguments. In
* this case, an empty model should be set.
*
* @param table
* the data table to retrieve the symbol from
* @param name
* the name of the symbol to get
*/
public abstract void setModelForSymbolName(SymbolTable table, String name);
/**
* Sets the symbol table used by this table to retrieve symbols by their
* name and the corresponding type for the table. If
* table == null then the symbol will be retrieved from the
* symbol scope hierarchy.
*
* @param table
* a symbol table
*/
public void setSymbolTable(SymbolTable table) {
// nothing to do
if (table == symbolTable)
return;
symbolTable = table;
if (table == null) {
SymbolTable sTableFromScope = symbolScope.getSymbolTable(this);
if (sTableFromScope != null && symbolName != null)
setModelForSymbolName(sTableFromScope, symbolName);
}
// set a new model
if (table != null && symbolName != null)
setModelForSymbolName(table, symbolName);
updateSelectionData();
}
/**
* Overrides JTable method to set up scrollpane.
*/
protected void configureEnclosingScrollPane() {
// setTableHeader(createDefaultTableHeader());
Container p = getParent();
if (p instanceof JViewport) {
Container gp = p.getParent();
if (gp instanceof JScrollPane) {
JScrollPane scrollPane = (JScrollPane) gp;
// Make certain we are the viewPort's view and not, for
// example, the rowHeaderView of the scrollPane -
// an implementor of fixed columns might do this.
JViewport viewport = scrollPane.getViewport();
if (viewport == null || viewport.getView() != this)
return;
// necessary to update scrollpane view
if (scrollPane instanceof JSCDataTableScrollPane) {
enclosingScrollPane = (JSCDataTableScrollPane) scrollPane;
enclosingScrollPane.setColumnHeaderView(enclosingScrollPane
.getColumnHeaderDataTable());
}
}
}
}
/**
* This method will create default columns for the table from the data
* model.
*
* This method will clear any exsiting columns before creating the new
* columns based on information from the model.
*
* @see #getAutoCreateColumnsFromModel
*/
public void createDefaultColumnsFromModel() {
super.createDefaultColumnsFromModel();
TableModel m = getModel();
if (m != null) {
TableColumnModel cm = getColumnModel();
TableCellRenderer renderer = getCellRenderer();
TableCellRenderer columnHeaderRenderer = null;
if (enclosingScrollPane != null && enclosingScrollPane != null) {
columnHeaderRenderer = enclosingScrollPane
.getColumnHeaderCellRenderer();
}
int n = cm.getColumnCount();
for (int i = 0; i < n; i++) {
TableColumn tc = cm.getColumn(i);
tc.setCellEditor(cellEditor);
tc.setCellRenderer(renderer);
if (columnHeaderRenderer != null)
tc.setHeaderRenderer(columnHeaderRenderer);
}
}
}
/**
* Sets the selection mode for rows.
*
* @param newRowSelMode
* one of ListSelectionModel.SINGLE_SELECTION,
* ListSelectionModel.SINGLE_INTERVAL or
* ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
*/
public void setRowSelectionMode(int newRowSelMode) {
getSelectionModel().setSelectionMode(newRowSelMode);
}
/**
* Gets the selection mode for table rows.
*
* @return one of ListSelectionModel.SINGLE_SELECTION,
* ListSelectionModel.SINGLE_INTERVAL or
* ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
*/
public int getRowSelectionMode() {
return getSelectionModel().getSelectionMode();
}
/**
* Gets the symbol name for the data object with the index of selected rows.
*
* @return the symbol name for the row selection index
*/
public String getSymbolNameSelRows() {
return symbolTypeDef.name;
}
/**
* Sets the name of the symbol with the index of selected table rows. The
* contents of this data object are the results from the a call to
* getIntSelectionIndex. The symbol is updated everytime the
* table selection changes. Changes in this symbol do not change the
* selection in this table. The selection index will only be stored, if the
* symbol name is set and the symbol table has been set as well or can be
* retrieved for the symbol scope.
*
* @param symbolNameSelRows
* symbol name
* @throws IllegalArgumentException
* if symbolNameSelRows is invalid
*/
public void setSymbolNameSelRows(String symbolNameSelRows) {
if (symbolNameSelRows == null)
symbolTypeDef = null;
else
symbolTypeDef = new JSCTypeDef(symbolNameSelRows, JSCTypes.NARRAY);
updateSelectionData();
}
/**
* Updates the data in the symbol for the row selection index if a symbol
* name has been set for it.
*/
private void updateSelectionData() {
if (symbolTypeDef == null)
return;
JSCNArray indexData = new JSCNArray("index", getIntSelectionIndex());
if (symbolTable != null)
symbolTable.get(symbolTypeDef).setJSCData(indexData);
else {
SymbolTable sTableFromScope = symbolScope.getSymbolTable(this);
if (sTableFromScope != null)
sTableFromScope.get(symbolTypeDef).setJSCData(indexData);
}
}
/**
* Overwrites supermethod to configure ecnclosing scrollpane and update
* selection data. It fires a "columnWidth" property change event. The old
* value is always set to -1.
*
* @param e
* the table model event
*/
public void tableChanged(TableModelEvent e) {
super.tableChanged(e);
if (enclosingScrollPane != null
&& enclosingScrollPane.getParent() != null)
((JComponent) enclosingScrollPane.getParent()).revalidate();
// update the vector of selected rows (0-1-vector)
updateSelectionData();
firePropertyChange("columnWidth", -1, getColumnWidth());
}
/**
* Invoked when the list selection changes. Updates selection data.
*
* @param e
* the list selection event
*/
public void valueChanged(ListSelectionEvent e) {
super.valueChanged(e);
// update the vector of selected rows (0-1-vector)
if (!e.getValueIsAdjusting())
updateSelectionData();
}
/**
* Gets a vector with length getRowCount of zeros and ones
* representing the selection status for each row. This is different from
* getSelectedRows which returns the selected indizes. In
* case of an empty selection, a zero vector with length
* getRowCount is returned.
*
* @return selection index for each row, 1 selects
*/
public int[] getIntSelectionIndex() {
int length = getModel().getRowCount();
int[] selRows = getSelectedRows();
int[] index = new int[length];
for (int i = 0; i < selRows.length; i++)
index[selRows[i]] = 1;
return index;
}
}
Ads help maintain this website.