org.wings.STable Maven / Gradle / Ivy
/*
* Copyright 2000,2005 wingS development team.
*
* This file is part of wingS (http://wingsframework.org).
*
* wingS 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.
*
* Please see COPYING for the complete licence.
*/
package org.wings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wings.event.*;
import org.wings.plaf.TableCG;
import org.wings.style.*;
import org.wings.table.*;
import org.wings.sdnd.TextAndHTMLTransferable;
import org.wings.sdnd.CustomDragHandler;
import org.wings.sdnd.SDropMode;
import javax.swing.event.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.util.*;
import java.util.List;
/**
* Displays information contained in a {@link TableModel} object.
*
* @author Holger Engels
* @author Armin Haaf
*/
public class STable extends SComponent
implements TableModelListener, Scrollable, CellEditorListener, LowLevelEventListener {
/**
* Apache jakarta commons logger
*/
private final static Logger log = LoggerFactory.getLogger(STable.class);
/**
* Table selection model. See {@link STable#setSelectionMode(int)}
*/
public static final int NO_SELECTION = SListSelectionModel.NO_SELECTION;
/**
* Table selection model. See {@link STable#setSelectionMode(int)}
*/
public static final int SINGLE_SELECTION = SListSelectionModel.SINGLE_SELECTION;
/**
* Table selection model. See {@link STable#setSelectionMode(int)}
*/
public static final int SINGLE_INTERVAL_SELECTION = SListSelectionModel.SINGLE_INTERVAL_SELECTION;
/**
* Table selection model. See {@link STable#setSelectionMode(int)}
*/
public static final int MULTIPLE_SELECTION = SListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
/**
* Table selection model. See {@link STable#setSelectionMode(int)}
*/
public static final int MULTIPLE_INTERVAL_SELECTION = SListSelectionModel.MULTIPLE_INTERVAL_SELECTION;
/**
* the table model.
*/
protected TableModel model;
/**
* the selection model.
*/
protected SListSelectionModel selectionModel;
/**
* The default renderer is used if no other renderer is set for the
* content of a cell.
*/
protected STableCellRenderer defaultRenderer;
/**
* The headerRenderer
is used to render the header line.
*/
protected STableCellRenderer headerRenderer;
/**
* A special cell renderer, that displays the control used to select
* a table row.
Ususally, this would be some checkbox. The plaf is the
* last instance to decide this.
*/
protected STableCellRenderer rowSelectionRenderer;
/**
* In this Map
, the renderers for the different
* classes of cell content are stored.
The class is treated
* as key, the renderer as the value.
*/
protected final HashMap renderer = new HashMap();
/**
* If this table is editable, clicks on table cells will be catched and interpreted as
* editor calls. Otherwise they may result in a selection event if {@link #isSelectable()}
*/
protected boolean editable = true;
/**
* If this table is marked as selectable, clicks on non-editable table cells will
* be catched and interpreted as selection calls.
*/
protected boolean selectable = true;
/**
* If editing, this is the SComponent
that is handling the editing.
*/
transient protected SComponent editorComp;
/**
*
The object that overwrites the screen real estate occupied by the
* current cell and allows the user to change those contents.
*/
transient protected STableCellEditor cellEditor;
transient protected LowLevelEventListener cellEditorComponent;
/**
* Identifies the column of the cell being edited.
*/
transient protected int editingColumn = -1;
/**
* Identifies the row of the cell being edited.
*/
transient protected int editingRow = -1;
/**
* In this Map
, the STableCellEditor
s for the different
* classes of cell content are stored.
The class is treated
* as key, the STableCellEditor
as the value.
*/
protected final HashMap editors = new HashMap();
/**
* Determines whether the header is visible or not.
By
* default the header is visible.
CAVEAT:The
* header is not (yet) implemented like in Swing. But maybe
* someday. So you can disable it if you like.
*/
protected boolean headerVisible = true;
/**
* Determines if horizontal lines in the table should be
* painted.
This is off by default.
*/
protected boolean showHorizontalLines = false;
/**
* Determines if vertical lines in the table should be
* painted.
This is off by default.
*/
protected boolean showVerticalLines = false;
protected SDimension intercellSpacing;
protected SDimension intercellPadding = new SDimension("1", "1");
/**
* Implementation of the {@link Scrollable} interface.
*/
protected Rectangle viewport;
/**
* Used to detect if the number of rows changed in
* which case we might have to update the viewport.
*/
private int rowCountBackUp;
/**
* Used to detect if the number of columns changed in
* which case we might have to update the viewport.
*/
private int columnCountBackUp;
/**
* @see LowLevelEventListener#isEpochCheckEnabled()
*/
protected boolean epochCheckEnabled = true;
/**
* The column model holds state information about the columns of the table.
*/
protected STableColumnModel columnModel;
transient protected STableColumnModelListener tableColumnModelListener;
/**
* If true, the column model is autorebuild from the table model.
*/
private boolean autoCreateColumnsFromModel;
/**
* A Pseudo CSS selector addressing the header row elements.
* Refer to {@link SComponent#setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}
*/
public static final Selector SELECTOR_HEADER = new Selector("HEADER");
/**
* A Pseudo CSS selector addressing the selected row elements.
* Refer to {@link SComponent#setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}
*/
public static final Selector SELECTOR_SELECTED = new Selector("SELECTED");
/**
* A Pseudo CSS selector addressing the regular odd row elements.
* Refer to {@link SComponent#setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}
*/
public static final Selector SELECTOR_ODD_ROWS = new Selector("ODD_ROWS");
/**
* A Pseudo CSS selector addressing the regular even row elements.
* Refer to {@link SComponent#setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}
*/
public static final Selector SELECTOR_EVEN_ROWS = new Selector("EVEN_ROWS");
/**
* A Pseudo CSS selector addressing the cell.
* Can be applied to the table globally and gets overridden, if applied to cell renderer components
* Refer to {@link SComponent#setAttribute(org.wings.style.Selector, org.wings.style.CSSProperty, String)}
*/
public static final Selector SELECTOR_CELL = new Selector("CELL");
/**
* The last low level event values this table received.
*/
private String[] lastReceivedLowLevelEvents;
/**
* Helper variable for {@link #nameRendererComponent(SComponent, int, int)}
*/
private StringBuilder nameBuffer = new StringBuilder();
/**
* changes in the selection model should force a reload if possible
*/
protected final ListSelectionListener fwdSelectionEvents = new ListSelectionListener() {
List deselectedIndices;
List selectedIndices;
@Override
public void valueChanged(ListSelectionEvent e) {
if (isUpdatePossible() && STable.class.isAssignableFrom(STable.this.getClass())) {
deselectedIndices = new ArrayList<>();
selectedIndices = new ArrayList<>();
if (getSelectionMode() == SINGLE_SELECTION) {
addIndex(e.getFirstIndex());
addIndex(e.getLastIndex());
} else {
for (int index = e.getFirstIndex(); index <= e.getLastIndex(); ++index) {
addIndex(index);
}
}
update(((TableCG) getCG()).getSelectionUpdate(STable.this, deselectedIndices, selectedIndices));
} else {
reload();
}
}
private void addIndex(int index) {
int visibleIndex = index;
if (getViewportSize() != null) {
visibleIndex = index - getViewportSize().y;
if (visibleIndex < 0 || visibleIndex >= getViewportSize().height)
return;
}
if (isRowSelected(index)) {
selectedIndices.add(visibleIndex);
} else {
deselectedIndices.add(visibleIndex);
}
}
};
/**
*
* Creates a new STable
.
*
*/
public STable() {
this(null);
}
/**
* Creates a new STable
.
*
* @param tm the TableModel
for the table's contents.
*/
public STable(TableModel tm) {
this(tm, null);
}
/**
* Creates a new STable
.
*
* @param model the TableModel
for the table's contents. May be null
.
* @param columnModel The column model. Implicitly created and maintained if null
.
*/
public STable(TableModel model, STableColumnModel columnModel) {
setSelectionModel(new SDefaultListSelectionModel());
createDefaultEditors();
if (columnModel == null) {
// no issue if model == null!
columnModel = createDefaultColumnModel();
autoCreateColumnsFromModel = true;
}
setColumnModel(columnModel);
if (model == null)
model = createDefaultDataModel();
setModel(model); // the resulting tableChanged event will update the default column mdoel
installTransferHandler();
createActionMap();
}
/**
* Sets the model of the table.
*
* @param tm the TableModel
to set.
*/
public void setModel(TableModel tm) {
if (tm == null)
throw new IllegalArgumentException("Cannot set a null TableModel");
TableModel oldVal = this.model;
if (this.model != tm) {
if (model != null)
model.removeTableModelListener(this);
model = tm;
model.addTableModelListener(this);
tableChanged(new TableModelEvent(tm, TableModelEvent.HEADER_ROW));
}
propertyChangeSupport.firePropertyChange("model", oldVal, this.model);
}
/**
* returns the model of the table
*/
public TableModel getModel() {
return model;
}
public int getColumnCount() {
return model.getColumnCount();
}
public int getVisibleColumnCount() {
if (columnModel != null) {
int columnCount = 0;
for (Object o : columnModel.getColumns()) {
STableColumn tableColumn = (STableColumn) o;
if (!tableColumn.isHidden())
columnCount++;
}
return columnCount;
}
else
return model.getColumnCount();
}
public String getColumnName(int col) {
return model.getColumnName(convertColumnIndexToModel(col));
}
public Class getColumnClass(int col) {
return model.getColumnClass(convertColumnIndexToModel(col));
}
/**
* Convienece method / Swing compatiblity to model.getRowCount()
* @return numer of rows inside the table model
*/
public int getRowCount() {
return model.getRowCount();
}
/**
* Define an optional CSS class which should be applied additionally to the passed row num.
* Override this method, if you want to give rows different attributes.
* E.g. for displaying an alternating background color for rows.
*
* @return the style of a specific row number.
*/
public String getRowStyle(int row) {
return null;
}
public Object getValueAt(int row, int column) {
return model.getValueAt(row, convertColumnIndexToModel(column));
}
public void setValueAt(Object v, int row, int column) {
Object oldVal = model.getValueAt(row, column);
model.setValueAt(v, row, convertColumnIndexToModel(column));
propertyChangeSupport.firePropertyChange("valueAt", oldVal, model.getValueAt(row, column));
}
public int convertColumnIndexToModel(int viewColumnIndex) {
if (viewColumnIndex < 0) {
return viewColumnIndex;
}
return getColumnModel().getColumn(viewColumnIndex).getModelIndex();
}
/**
* Maps the index of the column in the table model at
* modelColumnIndex
to the index of the column
* in the view. Returns the index of the
* corresponding column in the view; returns -1 if this column is not
* being displayed. If modelColumnIndex
is less than zero,
* returns modelColumnIndex
.
*
* @param modelColumnIndex the index of the column in the model
* @return the index of the corresponding column in the view
*
* @see #convertColumnIndexToModel
*/
public int convertColumnIndexToView(int modelColumnIndex) {
if (modelColumnIndex < 0) {
return modelColumnIndex;
}
STableColumnModel cm = getColumnModel();
int count = cm.getColumnCount();
for (int column = 0; column < count; column++) {
if (cm.getColumn(column).getModelIndex() == modelColumnIndex) {
return column;
}
}
return -1;
}
/**
* Adds the row from index0 to index0 inclusive to the current selection.
*/
public void addRowSelectionInterval(int index0, int index1) {
selectionModel.addSelectionInterval(index0, index1);
}
/**
* Sets the container, this component resides in.
*
* @param p the container, this component resides in.
*/
@Override
public void setParent(SContainer p) {
super.setParent(p);
if (cellRendererPane != null) {
cellRendererPane.setParent(p);
}
if (editorComp != null) {
editorComp.setParent(p);
}
}
/**
* Sets the frame in which this component resides.
*
* @param f the frame in which this component resides.
*/
@Override
protected void setParentFrame(SFrame f) {
super.setParentFrame(f);
if (cellRendererPane != null) {
cellRendererPane.setParentFrame(f);
}
}
// check fireIntermediateEvents !
@Override
public void processLowLevelEvent(String action, String... values) {
processKeyEvents(values);
if (action.endsWith("_keystroke"))
return;
if (isEditing() && action.contains("_e_") && cellEditorComponent != null) {
cellEditorComponent.processLowLevelEvent(action, values);
}
else {
if (this.lastReceivedLowLevelEvents == null) {
this.lastReceivedLowLevelEvents = values;
} else if (values != null && values.length > 0) {
// more than one parameter targets the table. collecting parameter values
String[] joinedParameters = new String[this.lastReceivedLowLevelEvents.length + values.length];
System.arraycopy(this.lastReceivedLowLevelEvents, 0, joinedParameters, 0, this.lastReceivedLowLevelEvents.length);
System.arraycopy(values, 0, joinedParameters, this.lastReceivedLowLevelEvents.length, values.length);
this.lastReceivedLowLevelEvents = joinedParameters;
}
}
SForm.addArmedComponent(this);
}
private SCellRendererPane cellRendererPane = new SCellRendererPane();
public SCellRendererPane getCellRendererPane() {
return cellRendererPane;
}
/**
* Sets The default renderer is used if no other renderer is set for the
* content of a cell.
*
* @param r The default renderer is used if no other renderer is set for the
*/
public void setDefaultRenderer(STableCellRenderer r) {
STableCellRenderer oldVal = this.defaultRenderer;
defaultRenderer = r;
propertyChangeSupport.firePropertyChange("defaultRenderer", oldVal, this.defaultRenderer);
}
/**
* Returns
The default renderer is used if no other renderer is set for the
* content of a cell.
*
* @return The default renderer is used if no other renderer is set for the
*/
public STableCellRenderer getDefaultRenderer() {
return defaultRenderer;
}
public void setDefaultRenderer(Class columnClass, STableCellRenderer r) {
if (renderer != null) {
Object oldVal = renderer.get(columnClass);
renderer.remove(columnClass);
renderer.put(columnClass, r);
propertyChangeSupport.firePropertyChange("defaultRenderer", oldVal, this.renderer.get(columnClass));
}
}
public STableCellRenderer getDefaultRenderer(Class columnClass) {
if (columnClass == null) {
return defaultRenderer;
} else {
Object r = renderer.get(columnClass);
if (r != null) {
return (STableCellRenderer) r;
} else {
return getDefaultRenderer(columnClass.getSuperclass());
}
}
}
/**
* The renderer component responsible for rendering the table's header cell.
* @param headerCellRenderer
*/
public void setHeaderRenderer(STableCellRenderer headerCellRenderer) {
STableCellRenderer oldVal = this.headerRenderer;
headerRenderer = headerCellRenderer;
propertyChangeSupport.firePropertyChange("headerRenderer", oldVal, this.headerRenderer);
}
/**
* The renderer component responsible for rendering the table's header cell.
* @return The renderer component for the header row
*/
public STableCellRenderer getHeaderRenderer() {
return headerRenderer;
}
/**
* The cell renderer used to render a special selection column needed in cases clicks on table
* cell cannot be distinguished as 'edit' or 'selection' click.
* @return The table cell renderer used to render the selection column, or null
* if no selection row should be rendered.
*/
public STableCellRenderer getRowSelectionRenderer() {
return rowSelectionRenderer;
}
/**
* The cell renderer used to render a special selection column needed in cases clicks on table
* cell cannot be distinguished as 'edit' or 'selection' click.
* @param rowSelectionRenderer The table cell renderer used to render the selection column.
* Set this to null
if you don't want to have a selection row in any case
*/
public void setRowSelectionRenderer(STableCellRenderer rowSelectionRenderer) {
STableCellRenderer oldVal = this.rowSelectionRenderer;
this.rowSelectionRenderer = rowSelectionRenderer;
propertyChangeSupport.firePropertyChange("rowSelectionRenderer", oldVal, this.rowSelectionRenderer);
}
/**
* Returns the cell renderer for the given table cell.
* @param row Table row
* @param col Table column
* @return The cell renderer for the given table cell.
*/
public STableCellRenderer getCellRenderer( int row, int col ) {
STableColumnModel columnModel = getColumnModel();
if (columnModel != null) {
STableColumn column = columnModel.getColumn(col);
if (column != null) {
STableCellRenderer renderer = column.getCellRenderer();
if (renderer != null)
return renderer;
}
}
return getDefaultRenderer(getColumnClass(col));
}
/**
* Returns the header renderer for the given header cell.
* @param col Table column
* @return The header renderer for the given header cell.
*/
public STableCellRenderer getHeaderRenderer( int col ) {
STableColumnModel columnModel = getColumnModel();
if ( columnModel != null) {
STableColumn column = columnModel.getColumn(col);
if (column != null) {
STableCellRenderer renderer = column.getHeaderRenderer();
if (renderer != null)
return renderer;
}
}
return headerRenderer;
}
public SComponent prepareRenderer(STableCellRenderer r, int row, int col) {
final SComponent tableCellRendererComponent = r.getTableCellRendererComponent(this,
getValueAt(row, col),
isRowSelected(row),
row, col);
nameRendererComponent(tableCellRendererComponent, row, col);
return tableCellRendererComponent;
}
/**
* Generates the name (= id) of the editing component so that
* the STable implementation knows to associate the input
* value with the correct data row/columns
*
* Applies the unqique id/name for a component of the rows/column
* to the cell component.
*
* @param component The edit component to rename
* @param row Data row of this edit component
* @param col Data column of this edit component
*/
protected void nameRendererComponent(final SComponent component, final int row, final int col) {
nameBuffer.setLength(0);
nameBuffer.append(this.getName()).append('_');
if (row == -1)
nameBuffer.append('h');
else
nameBuffer.append(row);
nameBuffer.append('_').append(col);
component.setNameRaw(nameBuffer.toString());
}
/**
* Prepares and returns the renderer to render the column header
* @param col Column number to render. Starts with 0
. May be -1
for row selection column.
* @return The renderer to render the column header
*/
public SComponent prepareHeaderRenderer(STableCellRenderer headerRenderer, int col) {
Object headerValue;
if (col < 0) {
headerValue = null;
} else if (getColumnModel() != null && getColumnModel().getColumn(col) != null) {
headerValue = getColumnModel().getColumn(col).getHeaderValue();
} else {
headerValue = model.getColumnName(col);
}
return headerRenderer.getTableCellRendererComponent(this, headerValue, false, -1, col);
}
/**
* If this table is editable, clicks on table cells will be catched and interpreted as
* editor calls. Otherwise they may result in a selection event if {@link #isSelectable()}
*
Defaults to true
*
* @return true
if clicks on editable cell should trigger the cell editor,
* false
if never.
*/
public boolean isEditable() {
return editable;
}
/**
* If this table is editable, clicks on table cells will be catched
* as {@link org.wings.event.SMouseEvent}s and interpreted as
* editor calls. Otherwise they may result in a selection event if {@link #isSelectable()}.
*
Defaults to true
*
* @param editable true
if clicks on editable cell should trigger the cell editor,
* false
if never.
* @see #isEditable()
* @see #processLowLevelEvent(String, String[])
* @see #fireIntermediateEvents()
*/
public void setEditable(boolean editable) {
boolean oldVal = this.editable;
reloadIfChange(this.editable, editable);
this.editable = editable;
propertyChangeSupport.firePropertyChange("editable", oldVal, this.editable);
}
/**
* If this table is marked as selectable, clicks on a non-editable table cells
* {@link #setEditable(boolean)} must be false
) will be catched
* as {@link org.wings.event.SMouseEvent}s and interpreted as selection calls.
*
Defaults to true
*
* @return true
if table cell should catch clicks on non-editable tables.
*/
public boolean isSelectable() {
return selectable;
}
/**
* If this table is marked as selectable, clicks on a non-editable table cells
* {@link #setEditable(boolean)} must be false
) will be catched
* as {@link org.wings.event.SMouseEvent}s and interpreted as selection calls.
*
Defaults to true
*
* @param selectable true
if table cell should catch clicks on non-editable tables.
* @see #isEditable()
* @see #processLowLevelEvent(String, String[])
* @see #fireIntermediateEvents()
*/
public void setSelectable(boolean selectable) {
boolean oldVal = this.selectable;
reloadIfChange(this.selectable, selectable);
this.selectable = selectable;
propertyChangeSupport.firePropertyChange("selectable", oldVal, this.selectable);
}
/**
* Set a default editor to be used if no editor has been set in
* a TableColumn. If no editing is required in a table, or a
* particular column in a table, use the isCellEditable()
* method in the TableModel interface to ensure that the
* STable will not start an editor in these columns.
* If editor is null, remove the default editor for this
* column class.
*
* @see TableModel#isCellEditable
* @see #getDefaultEditor
* @see #setDefaultRenderer
*/
public void setDefaultEditor(Class columnClass, STableCellEditor r) {
if (editors != null) {
Object oldVal = this.editors.get(columnClass);
editors.remove(columnClass);
editors.put(columnClass, r);
propertyChangeSupport.firePropertyChange("defaultEditor", oldVal, this.editors.get(columnClass));
}
}
/*
* Returns the editor to be used when no editor has been set in
* a TableColumn. During the editing of cells the editor is fetched from
* a Map of entries according to the class of the cells in the column. If
* there is no entry for this columnClass the method returns
* the entry for the most specific superclass. The STable installs entries
* for Object, Number and Boolean all which can be modified
* or replaced.
*
* @see #setDefaultEditor
* @see #getColumnClass
*/
public STableCellEditor getDefaultEditor(Class columnClass) {
if (columnClass == null) {
return null;
} else {
Object r = editors.get(columnClass);
if (r != null) {
return (STableCellEditor) r;
} else {
return getDefaultEditor(columnClass.getSuperclass());
}
}
}
//
// Editing Support
//
/**
* Programmatically starts editing the cell at row and
* column, if the cell is editable.
*
* @param row the row to be edited
* @param column the column to be edited
* @return false if for any reason the cell cannot be edited.
* @throws IllegalArgumentException If row or column
* are not in the valid range
*/
public boolean editCellAt(int row, int column) {
return editCellAt(row, column, null);
}
/**
* Programmatically starts editing the cell at row and
* column, if the cell is editable.
* To prevent the STable from editing a particular table, column or
* cell value, return false from the isCellEditable() method in the
* TableModel interface.
*
* @param row the row to be edited
* @param column the column to be edited
* @param e event to pass into
* shouldSelectCell
* @return false if for any reason the cell cannot be edited.
* @throws IllegalArgumentException If row or column
* are not in the valid range
*/
public boolean editCellAt(int row, int column, EventObject e) {
if (isEditing()) {
// Try to stop the current editor
if (cellEditor != null) {
boolean stopped = cellEditor.stopCellEditing();
if (!stopped)
return false; // The current editor not resigning
}
}
if (!isCellEditable(row, column))
return false;
STableCellEditor editor = getCellEditor(row, column);
if (editor != null) {
// set up editor environment and make it possible for the editor, to
// stop/cancel editing on preparation
editor.addCellEditorListener(this);
setCellEditor(editor);
setEditingRow(row);
setEditingColumn(column);
// prepare editor
editorComp = prepareEditor(editor, row, column);
if (editor.isCellEditable(e) && editor.shouldSelectCell(e)) {
update(((TableCG)getCG()).getEditCellUpdate(this, row, column));
return true;
}
removeEditor();
}
return false;
}
/**
* Returns true if the cell at row and column
* is editable. Otherwise, setValueAt() on the cell will not change
* the value of that cell.
*
* @param row the row whose value is to be looked up
* @param col the column whose value is to be looked up
* @return true if the cell is editable.
* @see #setValueAt
*/
public boolean isCellEditable(int row, int col) {
if (col >= getColumnCount() || row == -1)
return false;
else {
return model.isCellEditable(row, convertColumnIndexToModel(col));
}
}
/**
* Returns true is the table is editing a cell.
*
* @return true is the table is editing a cell
* @see #editingColumn
* @see #editingRow
*/
public boolean isEditing() {
return (cellEditor != null);
}
/**
* If the receiver is currently editing this will return the Component
* that was returned from the CellEditor.
*
* @return SComponent handling editing session
*/
public SComponent getEditorComponent() {
return editorComp;
}
/**
* This returns the index of the editing column.
*
* @return the index of the column being edited
* @see #editingRow
*/
public int getEditingColumn() {
return editingColumn;
}
/**
* Returns the index of the editing row.
*
* @return the index of the row being edited
* @see #editingColumn
*/
public int getEditingRow() {
return editingRow;
}
/**
* Return the cellEditor.
*
* @return the STableCellEditor that does the editing
* @see #cellEditor
*/
public STableCellEditor getCellEditor() {
return cellEditor;
}
/**
* Set the cellEditor variable.
*
* @param anEditor the STableCellEditor that does the editing
* @see #cellEditor
*/
protected void setCellEditor(STableCellEditor anEditor) {
STableCellEditor oldVal = this.cellEditor;
cellEditor = anEditor;
propertyChangeSupport.firePropertyChange("cellEditor", oldVal, this.cellEditor);
}
/**
* Set the editingColumn variable.
*
* @see #editingColumn
*/
public void setEditingColumn(int editingColumn) {
int oldVal = this.editingColumn;
this.editingColumn = editingColumn;
propertyChangeSupport.firePropertyChange("editingColumn", oldVal, this.editingColumn);
}
/**
* Set the editingRow variable.
*
* @see #editingRow
*/
public void setEditingRow(int editingRow) {
int oldVal = this.editingRow;
this.editingRow = editingRow;
propertyChangeSupport.firePropertyChange("editingRow", oldVal, this.editingRow);
}
/**
* Return an appropriate editor for the cell specified by this row and
* column. If the TableColumn for this column has a non-null editor, return that.
* If not, find the class of the data in this column (using getColumnClass())
* and return the default editor for this type of data.
*
* @param row the row of the cell to edit, where 0 is the first
* @param col the column of the cell to edit, where 0 is the first
*/
public STableCellEditor getCellEditor( int row, int col ) {
STableColumnModel columnModel = getColumnModel();
if (columnModel != null) {
STableColumn column = columnModel.getColumn(col);
if (column != null) {
STableCellEditor editor = column.getCellEditor();
if (editor != null)
return editor;
}
}
return getDefaultEditor(getColumnClass(col));
}
/**
* Prepares the specified editor using the value at the specified cell.
*
* @param editor the TableCellEditor to set up
* @param row the row of the cell to edit, where 0 is the first
* @param col the column of the cell to edit, where 0 is the first
*/
protected SComponent prepareEditor(STableCellEditor editor, int row, int col) {
SComponent component = editor.getTableCellEditorComponent(this,
getValueAt(row, col),
isRowSelected(row), // true?
row, col);
nameEditorComponent(component, row, col);
cellEditorComponent = (component instanceof LowLevelEventListener) ? (LowLevelEventListener)component : null;
return component;
}
/**
* Generates the name (= id) of the editing component so that
* the STable implementation knows to associate the input
* value with the correct data row/columns
*
* Applies the unqique id/name for a component of the rows/column
* to the cell component.
*
* @param component The edit component to rename
* @param row Data row of this edit component
* @param col Data column of this edit component
*/
protected void nameEditorComponent(final SComponent component, final int row, final int col) {
nameBuffer.setLength(0);
nameBuffer.append(this.getName()).append("_e_");
nameBuffer.append(row);
nameBuffer.append('_').append(col);
component.setNameRaw(nameBuffer.toString());
}
/**
* Discard the editor object and return the real estate it used to
* cell rendering.
*/
public void removeEditor() {
STableCellEditor editor = cellEditor;
if (editor != null) {
editor.removeCellEditorListener(this);
//remove(editorComp);
setCellEditor(null);
int oldEditingColumn = editingColumn;
int oldEditingRow = editingRow;
setEditingColumn(-1);
setEditingRow(-1);
if (editorComp != null) {
editorComp.setParent(null);
} // end of if ()
editorComp = null;
update(((TableCG)getCG()).getRenderCellUpdate(this, oldEditingRow, oldEditingColumn));
}
}
//
// Implementing the CellEditorListener interface
//
/**
* Invoked when editing is finished. The changes are saved and the
* editor object is discarded.
*
* @see CellEditorListener
*/
@Override
public void editingStopped(ChangeEvent e) {
// Take in the new value
STableCellEditor editor = cellEditor;
if (editor != null) {
Object value = editor.getCellEditorValue();
setValueAt(value, editingRow, editingColumn);
removeEditor();
}
}
/**
* Invoked when editing is canceled. The editor object is discarded
* and the cell is rendered once again.
*
* @see CellEditorListener
*/
@Override
public void editingCanceled(ChangeEvent e) {
removeEditor();
}
/**
* Creates default cell editors for Objects, numbers, and boolean values.
*/
protected void createDefaultEditors() {
editors.clear();
// Objects
setDefaultEditor(Object.class, new SDefaultCellEditor(new STextField()));
setDefaultEditor(Number.class, new SDefaultCellEditor(new STextField()));
// Numbers
//STextField rightAlignedTextField = new STextField();
//rightAlignedTextField.setHorizontalAlignment(STextField.RIGHT);
//setDefaultEditor(Number.class, new SDefaultCellEditor(rightAlignedTextField));
// Booleans
SCheckBox centeredCheckBox = new SCheckBox();
//centeredCheckBox.setHorizontalAlignment(JCheckBox.CENTER);
setDefaultEditor(Boolean.class, new SDefaultCellEditor(centeredCheckBox));
}
/**
* Returns
the selection model.
*
* @return the selection model.
*/
public SListSelectionModel getSelectionModel() {
return selectionModel;
}
/**
* Sets the row selection model for this table to model
.
*
* @param model the new selection model
* @throws IllegalArgumentException if model
* is null
* @see #getSelectionModel
*/
public void setSelectionModel(SListSelectionModel model) {
if (model == null) {
throw new IllegalArgumentException("cannot set a null SListSelectionModel");
}
SListSelectionModel oldVal = this.selectionModel;
if (selectionModel != null) {
removeSelectionListener(fwdSelectionEvents);
}
selectionModel = model;
addSelectionListener(fwdSelectionEvents);
propertyChangeSupport.firePropertyChange("selectionModel", oldVal, this.selectionModel);
}
public int getSelectedRowCount() {
SListSelectionModel selectionModel = this.selectionModel;
int result = 0;
for (int i = selectionModel.getMinSelectionIndex();
i <= selectionModel.getMaxSelectionIndex(); i++) {
if (selectionModel.isSelectedIndex(i))
result++;
}
return result;
}
public int getSelectedRow() {
return selectionModel.getMinSelectionIndex();
}
public int[] getSelectedRows() {
int[] result = new int[getSelectedRowCount()];
SListSelectionModel selectionModel = this.selectionModel;
int index = 0;
for (int i = selectionModel.getMinSelectionIndex();
i <= selectionModel.getMaxSelectionIndex(); i++) {
if (selectionModel.isSelectedIndex(i))
result[index++] = i;
}
return result;
}
/**
* Deselects all selected columns and rows.
*/
public void clearSelection() {
if (!selectionModel.isSelectionEmpty()) {
selectionModel.clearSelection();
reload();
}
}
public boolean isRowSelected(int row) {
return selectionModel.isSelectedIndex(row);
}
/**
* Sets the selection mode. Use one of the following values:
*
* - {@link #NO_SELECTION}
*
- {@link javax.swing.ListSelectionModel#SINGLE_SELECTION} or
* {@link #SINGLE_SELECTION}
*
- {@link javax.swing.ListSelectionModel#SINGLE_INTERVAL_SELECTION} or
* {@link #SINGLE_INTERVAL_SELECTION}
*
- {@link javax.swing.ListSelectionModel#MULTIPLE_INTERVAL_SELECTION} or
* {@link #MULTIPLE_SELECTION}
*
*/
public void setSelectionMode(int s) {
int oldVal = selectionModel.getSelectionMode();
reloadIfChange(selectionModel.getSelectionMode(), s);
selectionModel.setSelectionMode(s);
propertyChangeSupport.firePropertyChange("selectionMode", oldVal, selectionModel.getSelectionMode());
}
/**
* @return
* - {@link #NO_SELECTION}
*
- {@link javax.swing.ListSelectionModel#SINGLE_SELECTION} or
* {@link #SINGLE_SELECTION}
*
- {@link javax.swing.ListSelectionModel#SINGLE_INTERVAL_SELECTION} or
* {@link #SINGLE_INTERVAL_SELECTION}
*
- {@link javax.swing.ListSelectionModel#MULTIPLE_INTERVAL_SELECTION} or
* {@link #MULTIPLE_SELECTION}
*
*/
public int getSelectionMode() {
return selectionModel.getSelectionMode();
}
public void addSelectionListener(ListSelectionListener listener) {
selectionModel.addListSelectionListener(listener);
}
public void removeSelectionListener(ListSelectionListener listener) {
selectionModel.removeListSelectionListener(listener);
}
/**
* Adds the specified mouse listener to receive mouse events from
* this component.
* If l is null, no exception is thrown and no action is performed.
*
* @param l the component listener.
* @see org.wings.event.SMouseEvent
* @see org.wings.event.SMouseListener
* @see org.wings.STable#removeMouseListener
*/
public final void addMouseListener(SMouseListener l) {
addEventListener(SMouseListener.class, l);
}
/**
* Removes the specified mouse listener so that it no longer
* receives mouse events from this component. This method performs
* no function, nor does it throw an exception, if the listener
* specified by the argument was not previously added to this component.
* If l is null, no exception is thrown and no action is performed.
*
* @param l the component listener.
* @see org.wings.event.SMouseEvent
* @see org.wings.event.SMouseListener
* @see org.wings.STable#addMouseListener
*/
public final void removeMouseListener(SMouseListener l) {
removeEventListener(SMouseListener.class, l);
}
/**
* Reports a mouse click event.
*
* @param event report this event to all listeners
* @see org.wings.event.SMouseListener
*/
protected void fireMouseClickedEvent(SMouseEvent event) {
Object[] listeners = getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == SMouseListener.class) {
((SMouseListener)listeners[i + 1]).mouseClicked(event);
}
}
}
private int lastSelectedIndex;
protected void addSelectionEvent(int row, boolean ctrlKey, boolean shiftKey) {
if(model != null && row >= model.getRowCount())
return;
if ( ctrlKey == true && shiftKey == false && selectionModel.isSelectedIndex(row)) {
selectionModel.removeSelectionInterval(row, row);
lastSelectedIndex = row;
} else {
if ( shiftKey == true ) {
selectionModel.setSelectionInterval( lastSelectedIndex, row );
} else if ( ctrlKey == true ) {
selectionModel.addSelectionInterval( row, row );
lastSelectedIndex = row;
} else {
selectionModel.setSelectionInterval(row, row);
lastSelectedIndex = row;
}
}
}
protected void addSelectionEvent(int row, int col, boolean ctrlKey, boolean shiftKey) {
addSelectionEvent(row, ctrlKey, shiftKey);
}
@Override
public void fireIntermediateEvents() {
if (lastReceivedLowLevelEvents == null)
return;
// delay events...
selectionModel.setDelayEvents(true);
selectionModel.setValueIsAdjusting(true);
for (String lastReceivedLowLevelEvent : lastReceivedLowLevelEvents) {
String value = lastReceivedLowLevelEvent;
if (value.length() > 1) {
char modus = value.charAt(0);
value = value.substring(1);
if (value.indexOf(':') == -1)
continue;
String[] values = value.split(";");
value = values[0];
try {
SPoint point = new SPoint(value);
int row = rowAtPoint(point);
int col = columnAtPoint(point);
SMouseEvent event = new SMouseEvent(this, 0, point);
fireMouseClickedEvent(event);
if (event.isConsumed())
continue;
// editor event
switch (modus) {
case 'e':
editCellAt(row, col, null);
break;
case 't':
boolean shiftKey = false;
boolean ctrlKey = false;
for (String val : values) {
String[] parts = val.split("=");
if (parts.length != 2)
continue;
if ("shiftKey".equals(parts[0]))
shiftKey = Boolean.parseBoolean(parts[1]);
else if ("ctrlKey".equals(parts[0]))
ctrlKey = Boolean.parseBoolean(parts[1]);
}
addSelectionEvent(row, col, ctrlKey, shiftKey);
break;
case 's':
selectionModel.addSelectionInterval(row, row);
lastSelectedIndex = row;
break;
case 'd':
selectionModel.removeSelectionInterval(row, row);
lastSelectedIndex = row;
break;
}
} catch (NumberFormatException ex) {
log.warn("Number format exception while parsing low level events", ex);
}
}
}
lastReceivedLowLevelEvents = null;
selectionModel.setValueIsAdjusting(false);
selectionModel.setDelayEvents(false);
selectionModel.fireDelayedIntermediateEvents();
}
/**
* Returns the table row for the passed SPoint
* instance received via {@link #addMouseListener(org.wings.event.SMouseListener)}.
* @param point The pointed retuned by the mouse event.
* @return The row index
*/
public static int rowAtPoint(SPoint point) {
String coordinates = point.getCoordinates();
int colonIndex = coordinates.indexOf(':');
if (colonIndex < 0)
return - 1;
return Integer.parseInt(coordinates.substring(0, colonIndex));
}
/**
* Returns the table column for the passed SPoint
* instance received via {@link #addMouseListener(org.wings.event.SMouseListener)}.
* @param point The pointed retuned by the mouse event.
* @return The column index
* @see #rowAtPoint(SPoint)
*/
public static int columnAtPoint(SPoint point) {
String coordinates = point.getCoordinates();
int colonIndex = coordinates.indexOf(':');
if (colonIndex < 0)
return - 1;
return Integer.parseInt(coordinates.substring(colonIndex + 1));
}
@Override
public void fireFinalEvents() {
super.fireFinalEvents();
// fire selection events...
selectionModel.fireDelayedFinalEvents();
}
/**
* @see LowLevelEventListener#isEpochCheckEnabled()
*/
@Override
public boolean isEpochCheckEnabled() {
return epochCheckEnabled;
}
/**
* @see LowLevelEventListener#isEpochCheckEnabled()
*/
public void setEpochCheckEnabled(boolean epochCheckEnabled) {
boolean oldVal = this.epochCheckEnabled;
this.epochCheckEnabled = epochCheckEnabled;
propertyChangeSupport.firePropertyChange("epochCheckEnabled", oldVal, this.epochCheckEnabled);
}
@Override
public void tableChanged(TableModelEvent e) {
// kill active editors
//editingCanceled(null);
if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
// The whole thing changed
clearSelectionAndLeadAnchor();
if (autoCreateColumnsFromModel)
createDefaultColumnsFromModel();
} else {
switch (e.getType()) {
case TableModelEvent.INSERT:
if (e.getFirstRow() >= 0) {
selectionModel.insertIndexInterval(e.getFirstRow(), e.getLastRow(), true);
}
break;
case TableModelEvent.DELETE:
if (e.getFirstRow() >= 0) {
selectionModel.removeIndexInterval(e.getFirstRow(), e.getLastRow());
}
break;
case TableModelEvent.UPDATE:
/* event fire on javax.swing.table.AbstractTableModel.fireTableDataChanged() */
if (e.getLastRow() == Integer.MAX_VALUE)
clearSelectionAndLeadAnchor();
break;
}
}
if (model.getRowCount() != rowCountBackUp) {
rowCountBackUp = model.getRowCount();
fireViewportChanged(false);
}
if (columnModel.getColumnCount() != columnCountBackUp) {
columnCountBackUp = columnModel.getColumnCount();
fireViewportChanged(true);
}
if (e != null &&
e.getFirstRow() == e.getLastRow() &&
e.getFirstRow() != TableModelEvent.HEADER_ROW &&
e.getColumn() != TableModelEvent.ALL_COLUMNS &&
e.getType() == TableModelEvent.UPDATE) {
if (isUpdatePossible() && STable.class.isAssignableFrom(getClass()))
update(((TableCG) getCG()).getRenderCellUpdate(this, e.getFirstRow(), e.getColumn()));
else
reload();
}
/* else if (e != null &&
e.getFirstRow() == 0 &&
e.getLastRow() == Integer.MAX_VALUE &&
e.getColumn() == TableModelEvent.ALL_COLUMNS &&
e.getType() == TableModelEvent.UPDATE) {
update(((TableCG)getCG()).getDataUpdate(this));
}*/
else {
reload();
}
}
private void clearSelectionAndLeadAnchor() {
selectionModel.setValueIsAdjusting(true);
clearSelection();
selectionModel.setAnchorSelectionIndex(-1);
selectionModel.setLeadSelectionIndex(-1);
selectionModel.setValueIsAdjusting(false);
}
/**
* Return the background color.
*
* @return the background color
*/
public Color getSelectionBackground() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_SELECTED) == null ? null : CSSStyleSheet.getBackground((CSSAttributeSet) dynamicStyles.get(SELECTOR_SELECTED));
}
/**
* Set the foreground color.
*
* @param color the new foreground color
*/
public void setSelectionBackground(Color color) {
Color oldVal = this.getSelectionBackground();
setAttribute(SELECTOR_SELECTED, CSSProperty.BACKGROUND_COLOR, CSSStyleSheet.getAttribute(color));
propertyChangeSupport.firePropertyChange("selectionBackground", oldVal, this.getSelectionBackground());
}
/**
* Return the foreground color.
*
* @return the foreground color
*/
public Color getSelectionForeground() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_SELECTED) == null ? null : CSSStyleSheet.getForeground((CSSAttributeSet) dynamicStyles.get(SELECTOR_SELECTED));
}
/**
* Set the foreground color.
*
* @param color the new foreground color
*/
public void setSelectionForeground(Color color) {
Color oldVal = this.getSelectionForeground();
setAttribute(SELECTOR_SELECTED, CSSProperty.COLOR, CSSStyleSheet.getAttribute(color));
propertyChangeSupport.firePropertyChange("selectionForeground", oldVal, this.getSelectionForeground());
}
/**
* Set the font.
*
* @param font the new font
*/
public void setSelectionFont(SFont font) {
SFont oldVal = this.getSelectionFont();
setAttributes(SELECTOR_SELECTED, CSSStyleSheet.getAttributes(font));
propertyChangeSupport.firePropertyChange("selectionFont", oldVal, this.getSelectionFont());
}
/**
* Return the font.
*
* @return the font
*/
public SFont getSelectionFont() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_SELECTED) == null ? null : CSSStyleSheet.getFont((CSSAttributeSet) dynamicStyles.get(SELECTOR_SELECTED));
}
/**
* Return the background color.
*
* @return the background color
*/
public Color getHeaderBackground() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_HEADER) == null ? null : CSSStyleSheet.getBackground((CSSAttributeSet) dynamicStyles.get(SELECTOR_HEADER));
}
/**
* Set the foreground color.
*
* @param color the new foreground color
*/
public void setHeaderBackground(Color color) {
Color oldVal = this.getHeaderBackground();
setAttribute(SELECTOR_HEADER, CSSProperty.BACKGROUND_COLOR, CSSStyleSheet.getAttribute(color));
propertyChangeSupport.firePropertyChange("headerBackground", oldVal, this.getHeaderBackground());
}
/**
* Return the foreground color.
*
* @return the foreground color
*/
public Color getHeaderForeground() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_HEADER) == null ? null : CSSStyleSheet.getForeground((CSSAttributeSet) dynamicStyles.get(SELECTOR_HEADER));
}
/**
* Set the foreground color.
*
* @param color the new foreground color
*/
public void setHeaderForeground(Color color) {
Color oldVal = this.getHeaderForeground();
setAttribute(SELECTOR_HEADER, CSSProperty.COLOR, CSSStyleSheet.getAttribute(color));
propertyChangeSupport.firePropertyChange("headerForeground", oldVal, this.getHeaderForeground());
}
/**
* Set the font.
*
* @param font the new font
*/
public void setHeaderFont(SFont font) {
SFont oldVal = this.getHeaderFont();
setAttributes(SELECTOR_HEADER, CSSStyleSheet.getAttributes(font));
propertyChangeSupport.firePropertyChange("headerFont", oldVal, this.getHeaderFont());
}
/**
* Return the font.
*
* @return the font
*/
public SFont getHeaderFont() {
return dynamicStyles == null || dynamicStyles.get(SELECTOR_HEADER) == null ? null : CSSStyleSheet.getFont((CSSAttributeSet) dynamicStyles.get(SELECTOR_HEADER));
}
/**
* Sets Determines whether the header is visible or not.
By
* default the header is visible.
CAVEAT:The
* header is not (yet) implemented like in Swing. But maybe
* someday. So you can disable it if you like.
*
* @param hv Determines whether the header is visible or not.
By
*/
public void setHeaderVisible(boolean hv) {
boolean oldHeaderVisible = headerVisible;
headerVisible = hv;
if (oldHeaderVisible != headerVisible) {
reload();
}
propertyChangeSupport.firePropertyChange("headerVisible", oldHeaderVisible, this.headerVisible);
}
/**
* Returns
Determines whether the header is visible or not.
By
* default the header is visible.
CAVEAT:The
* header is not (yet) implemented like in Swing. But maybe
* someday. So you can disable it if you like.
*
* @return Determines whether the header is visible or not.
By
*/
public boolean isHeaderVisible() {
return headerVisible;
}
public void setShowGrid(boolean b) {
setShowHorizontalLines(b);
setShowVerticalLines(b);
}
/**
* Sets
Determines if horizontal lines in the table should be
* painted.
This is off by default.
*
* @param b Determines if horizontal lines in the table should be
*/
public void setShowHorizontalLines(boolean b) {
boolean oldShowHorizontalLines = showHorizontalLines;
showHorizontalLines = b;
if (showHorizontalLines != oldShowHorizontalLines) {
reload();
}
propertyChangeSupport.firePropertyChange("showHorizontalLines", oldShowHorizontalLines, this.showHorizontalLines);
}
/**
* Returns
Determines if horizontal lines in the table should be
* painted.
This is off by default.
*
* @return Determines if horizontal lines in the table should be
*/
public boolean getShowHorizontalLines() {
return showHorizontalLines;
}
/**
* Sets
Determines if vertical lines in the table should be
* painted.
This is off by default.
*
* @param b Determines if vertical lines in the table should be
*/
public void setShowVerticalLines(boolean b) {
boolean oldShowVerticalLines = showVerticalLines;
showVerticalLines = b;
if (showVerticalLines != oldShowVerticalLines) {
reload();
}
propertyChangeSupport.firePropertyChange("showVerticalLines", oldShowVerticalLines, this.showVerticalLines);
}
/**
* Returns
Determines if vertical lines in the table should be
* painted.
This is off by default.
*
* @return Determines if vertical lines in the table should be
*/
public boolean getShowVerticalLines() {
return showVerticalLines;
}
/*
* Implementiert das cellspacing Attribut des HTML Tables. Da dieses
* nur eindimensional ist, wird nur der width Wert der Dimension in
* den HTML Code uebernommen.
*/
public void setIntercellSpacing(SDimension d) {
SDimension oldIntercellSpacing = intercellSpacing;
intercellSpacing = d;
if ((intercellSpacing == null && oldIntercellSpacing != null) ||
intercellSpacing != null && !intercellSpacing.equals(oldIntercellSpacing))
reload();
propertyChangeSupport.firePropertyChange("intercellSpacing", oldIntercellSpacing, this.intercellSpacing);
}
public SDimension getIntercellSpacing() {
return intercellSpacing;
}
/*
* Implementiert das cellpadding Attribut des HTML Tables. Da dieses
* nur eindimensional ist, wird nur der width Wert der Dimension in
* den HTML Code uebernommen.
*/
public void setIntercellPadding(SDimension d) {
SDimension oldIntercellPadding = intercellPadding;
intercellPadding = d;
if ((intercellPadding == null && oldIntercellPadding != null) ||
intercellPadding != null && !intercellPadding.equals(oldIntercellPadding))
reload();
propertyChangeSupport.firePropertyChange("intercellPadding", oldIntercellPadding, this.intercellPadding);
}
public SDimension getIntercellPadding() {
return intercellPadding;
}
/**
* wingS internal method used to create specific HTTP request parameter names.
*/
public static String getEditParameter(int row, int col) {
return "e" + row + ':' + col;
}
/**
* wingS internal method used to create specific HTTP request parameter names.
*/
public static String getToggleSelectionParameter(int row, int col) {
return "t" + row + ':' + col;
}
/**
* wingS internal method used to create specific HTTP request parameter names.
*/
public static String getSelectionParameter(int row, int col) {
return "s" + row + ':' + col;
}
/**
* wingS internal method used to create specific HTTP request parameter names.
*/
public static String getDeselectionParameter(int row, int col) {
return "d" + row + ':' + col;
}
/**
* The size of the component in respect to scrollable units.
*/
@Override
public Rectangle getScrollableViewportSize() {
return new Rectangle(0, 0, getVisibleColumnCount(), getRowCount());
}
/**
* Returns the actual visible part of a scrollable.
*/
@Override
public Rectangle getViewportSize() {
return viewport;
}
/**
* Sets the actual visible part of a scrollable.
*/
@Override
public void setViewportSize(Rectangle newViewport) {
Rectangle oldViewport = viewport;
viewport = newViewport;
if (isDifferent(oldViewport, newViewport)) {
if (oldViewport == null || newViewport == null) {
fireViewportChanged(true);
fireViewportChanged(false);
} else {
if (newViewport.x != oldViewport.x || newViewport.width != oldViewport.width) {
fireViewportChanged(true);
}
if (newViewport.y != oldViewport.y || newViewport.height != oldViewport.height) {
fireViewportChanged(false);
}
}
update(((TableCG)getCG()).getTableScrollUpdate(this, newViewport, oldViewport));
}
propertyChangeSupport.firePropertyChange("viewportSize", oldViewport, this.viewport);
}
/**
* Adds the given SViewportChangeListener
to the scrollable.
*
* @param l the listener to be added
*/
@Override
public void addViewportChangeListener(SViewportChangeListener l) {
addEventListener(SViewportChangeListener.class, l);
}
/**
* Removes the given SViewportChangeListener
from the scrollable.
*
* @param l the listener to be removed
*/
@Override
public void removeViewportChangeListener(SViewportChangeListener l) {
removeEventListener(SViewportChangeListener.class, l);
}
/**
* Notifies all listeners that have registered interest for notification
* on changes to this scrollable's viewport in the specified direction.
*
* @see javax.swing.event.EventListenerList
*/
protected void fireViewportChanged(boolean horizontal) {
Object[] listeners = getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == SViewportChangeListener.class) {
SViewportChangeEvent event = new SViewportChangeEvent(this, horizontal);
((SViewportChangeListener) listeners[i + 1]).viewportChanged(event);
}
}
}
public void setSelectedRow(int selectedIndex) {
int oldVal = getSelectedRow();
selectionModel.setSelectionInterval(selectedIndex, selectedIndex);
propertyChangeSupport.firePropertyChange("selectedRow", oldVal, getSelectedRow());
}
/**
* Sets this table's autoCreateColumnsFromModel
flag.
* This method calls createDefaultColumnsFromModel
if
* autoCreateColumnsFromModel
changes from false to true.
*
* @param autoCreateColumnsFromModel true if JTable
should automatically create columns
* @see #getAutoCreateColumnsFromModel
* @see #createDefaultColumnsFromModel
*/
public void setAutoCreateColumnsFromModel(boolean autoCreateColumnsFromModel) {
if (this.autoCreateColumnsFromModel != autoCreateColumnsFromModel) {
boolean oldVal = this.autoCreateColumnsFromModel;
this.autoCreateColumnsFromModel = autoCreateColumnsFromModel;
if (autoCreateColumnsFromModel) {
createDefaultColumnsFromModel();
}
propertyChangeSupport.firePropertyChange("autoCreateColumnsFromModel", oldVal, this.autoCreateColumnsFromModel);
}
}
/**
* Determines whether the table will create default columns from the model.
* If true, setModel
will clear any existing columns and
* create new columns from the new model. Also, if the event in
* the tableChanged
notification specifies that the
* entire table changed, then the columns will be rebuilt.
* The default is true.
*
* @return the autoCreateColumnsFromModel of the table
* @see #setAutoCreateColumnsFromModel
* @see #createDefaultColumnsFromModel
*/
public boolean getAutoCreateColumnsFromModel() {
return autoCreateColumnsFromModel;
}
/**
* Returns a STableColumnModel
that contains information
* about all columns of this table.
*
* @return the object that provides the column state of the table
* @see #setColumnModel
*/
public STableColumnModel getColumnModel() {
return columnModel;
}
/**
* Sets the model holding information about the columns for this table.
*
* @param newColumnModel the new data source for this table
* @see #getColumnModel
*/
public void setColumnModel(STableColumnModel newColumnModel) {
if (newColumnModel == null)
throw new IllegalArgumentException("Column model must not be null");
STableColumnModel oldVal = this.columnModel;
if (columnModel != newColumnModel) {
if (tableColumnModelListener == null)
tableColumnModelListener = createTableColumnModelListener();
if (columnModel != null)
columnModel.removeColumnModelListener(tableColumnModelListener);
columnModel = newColumnModel;
columnModel.addColumnModelListener(tableColumnModelListener);
reload();
propertyChangeSupport.firePropertyChange("columnModel", oldVal, this.columnModel);
}
}
/**
* Creates the default columns of the table from the table model.
*/
public void createDefaultColumnsFromModel() {
TableModel tm = model;
if (tm != null) {
STableColumnModel columnModel = getColumnModel();
while (columnModel.getColumnCount() > 0)
columnModel.removeColumn(columnModel.getColumn(0));
for ( int i = 0; i < tm.getColumnCount(); i++ ) {
STableColumn column = new STableColumn( i );
String columnName = tm.getColumnName( i );
column.setHeaderValue( columnName );
this.columnModel.addColumn( column );
}
}
}
/**
* Returns the default column model object, which is
* a SDefaultTableColumnModel
.
* A subclass can override this method to return a different column model object.
*
* @return the default column model object
*/
protected STableColumnModel createDefaultColumnModel() {
return new SDefaultTableColumnModel();
}
/**
* Returns a default table model object.
* Subclasses can override this method to return a different table model objects
*
* @return the default table model object
* @see javax.swing.table.DefaultTableModel
*/
protected static TableModel createDefaultDataModel() {
return new DefaultTableModel();
}
/**
* Creates an instance of TableColumnModelHandler.
*/
protected TableColumnModelHandler createTableColumnModelListener() {
return new TableColumnModelHandler();
}
/**
* Handler that listens to the table's column model.
*/
protected class TableColumnModelHandler implements STableColumnModelListener {
@Override
public void columnAdded(STableColumnModelEvent e) {
removeEditor();
fireViewportChanged(true);
reload();
}
@Override
public void columnHidden(ChangeEvent e) {
removeEditor();
fireViewportChanged(true);
reload();
}
@Override
public void columnMarginChanged(ChangeEvent e) {
reload();
}
@Override
public void columnMoved(STableColumnModelEvent e) {
removeEditor();
reload();
}
@Override
public void columnRemoved(STableColumnModelEvent e) {
removeEditor();
fireViewportChanged(true);
reload();
}
@Override
public void columnShown(ChangeEvent e) {
removeEditor();
fireViewportChanged(true);
reload();
}
}
/**
* Drag and Drop stuff
*/
private SDropMode dropMode = null;
private boolean dragEnabled = false;
protected void createActionMap() {
ActionMap map = getActionMap();
map.put(STransferHandler.getCutAction().getValue(Action.NAME), STransferHandler.getCutAction());
map.put(STransferHandler.getCopyAction().getValue(Action.NAME), STransferHandler.getCopyAction());
map.put(STransferHandler.getPasteAction().getValue(Action.NAME), STransferHandler.getPasteAction());
}
public static class DropLocation extends STransferHandler.DropLocation {
private int row, col;
public DropLocation(STable table, SPoint point) {
super(point);
try {
String[] vals = point.getCoordinates().split(":");
row = Integer.parseInt(vals[0]);
col = Integer.parseInt(vals[1]);
Rectangle viewport = table.getViewportSize();
if(viewport != null) {
row += viewport.y;
col += viewport.x;
}
} catch(Exception e) {
row = -1;
col = -1;
}
}
public int getRow() {
return row;
}
public int getCol() {
return col;
}
}
public void setDropMode(SDropMode dropMode) {
this.dropMode = dropMode;
getSession().getSDragAndDropManager().addDropTarget(this);
}
public SDropMode getDropMode() {
return this.dropMode;
}
@Override
protected DropLocation dropLocationForPoint(SPoint p) {
if(p.getCoordinates() == null)
return null;
return new DropLocation(this, p);
}
private void installTransferHandler() {
if(getTransferHandler() == null) {
setTransferHandler(new DefaultTransferHandler());
}
}
public void setDragEnabled(boolean dragEnabled) {
if(selectionModel == null && dragEnabled == true) {
throw new IllegalStateException("no selection model in stable " + this);
}
if(dragEnabled != this.dragEnabled) {
if(dragEnabled) {
this.getSession().getSDragAndDropManager().addDragSource(this);
} else {
this.getSession().getSDragAndDropManager().removeDragSource(this);
}
this.dragEnabled = dragEnabled;
}
}
public static class DefaultTransferHandler extends STransferHandler implements CustomDragHandler {
public DefaultTransferHandler() {
super(null);
}
@Override
protected Transferable createTransferable(SComponent component) {
STable table = (STable)component;
String htmlText = "
";
String plainText = "";
for(int row:table.getSelectedRows()) {
htmlText += "";
for(int col=0; col" + object.toString() + "< td>";
if(col == (table.getColumnCount()-1))
plainText += object.toString();
else
plainText += object.toString() + '\t';
}
plainText += "\n";
htmlText += " ";
}
htmlText += "
";
return new TextAndHTMLTransferable(plainText, htmlText);
}
@Override
public int getSourceActions(SComponent component) {
return COPY;
}
@Override
public boolean dragStart(SComponent source, SComponent target, int action, SMouseEvent event) {
try {
String[] coords = event.getPoint().getCoordinates().split(":");
int row = Integer.parseInt(coords[0]);
int col = Integer.parseInt(coords[1]);
if(coords.length < 4)
return false;
boolean ctrlKey = false;
boolean shiftKey = false;
for(int i=2; i