/*
 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.