JDK-4178367 : JTable header height and row height problems
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.1.5
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_95
  • CPU: x86
  • Submitted: 1998-10-02
  • Updated: 1999-04-14
  • Resolved: 1999-04-14
Related Reports
Relates :  
Description

Name: tb29552			Date: 10/02/98


/*
In Swing 1.02 JTable:
- Header height is zero until setVisble(true)
- Header height + (rowCount * rowHeight) does
    not give correct preferred height
- Pixel fudge factor must be added to preferred
    width to get correct appearance in scroller

A test class duplicating these bugs follows.
Sorry it's not very concise, since it was my
main JTable test class.

package org.jcon.test;
*/

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.table.*;

/**
 * Test JTable for technique required to size columns,
 * provide data, make splitter work, etc.
 *      java TestJTable
 *
 * @author Jack Harich
 */
public class TestJTable implements ActionListener, MouseListener {

//---------- Private Fields ------------------------------
    private Frame frame = new Frame("Test JTable");

    private EntityModel entityModel = new EntityModel();
    private JTable entityTable = new JTable(entityModel);
    private int entityRowCount = 3;
    private String entityData = "Data";

    private StructureModel structureModel = new StructureModel();
    private JTable structureTable = new JTable(structureModel);

// false for Windows mostly, true for metal
    private static boolean useCrossPlatformLookAndFeel = false;

//---------- Initialization ------------------------------
    public TestJTable () {
        print(" - Please wait....");
        installLookAndFeel();
        frame.setBackground(Color.lightGray);

        //----- Configure panes
        // Entity pane
        // JScrollPane entityScrollPane =
        // JTable.createScrollPaneForTable(entityTable);
        JScrollPane entityScrollPane = new JScrollPane(entityTable);


        entityScrollPane.setMinimumSize(new Dimension(0, 0));
        // Structure pane
        // *** createScrollPaneForTable() DEPRECATED ***
        // JScrollPane structureScrollPane =
        // JTable.createScrollPaneForTable(structureTable);
        JScrollPane structureScrollPane = new JScrollPane(structureTable);

        structureScrollPane.setMinimumSize(new Dimension(0, 0));
        // Put panes in splitter
        JSplitPane splitPane = new JSplitPane(JSplitPane.
                   HORIZONTAL_SPLIT, entityScrollPane, structureScrollPane);
        // splitPane.setDividerLocation(200); // NO EFFECT ***
        splitPane.setContinuousLayout(false);   // Until buffer
        splitPane.setDoubleBuffered(true);
        frame.add("Center", splitPane);

        //----- Configure other
        Panel buttonBar = new Panel();
        buttonBar.add(createButton("Reload Entity Data", "Reload"));
        buttonBar.add(createButton("Select 2", "Select2"));
        buttonBar.add(createButton("Select 3", "Select3"));
        frame.add("South", buttonBar);

        //----- Configure tables
        // entityTable
        entityTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        addColumn(entityTable, 0, 15, "Entity Name");
        addColumn(entityTable, 1, 18, "Status");
        // structureTable
        structureTable.addMouseListener(this);
        structureTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        addColumn(structureTable, 0, 15, "Column");
        addColumn(structureTable, 1, 10, "Type");
        addColumn(structureTable, 2, 25, "Properties");
//frame.setVisible(true);
        // Now that columns are defined setPreferredSize()
        // extra for Swing bug.
        // Metal - use 3, 2 will cause horizontal scroller to appear
        // Windows - use 5.
        int prefWidth = entityTable.getPreferredSize().width;
        int prefHeight = calcPreferredTableHeight(6);
        entityScrollPane.setPreferredSize(new Dimension(
                                                prefWidth + 5, prefHeight));

        prefWidth = structureTable.getPreferredSize().width;
        structureScrollPane.setPreferredSize(new Dimension(
                                                prefWidth + 7, prefHeight));    // 5 not enough

        frame.pack();           // Works since preferredSize set
        frame.setVisible(true);
    }
    private int calcPreferredTableHeight(int rowCount) {        // Was 400
        Dimension headerSize = entityTable.getTableHeader().getSize();
        print(" - Preferred header height = " + headerSize.height);
        print(" - Row height = " + entityTable.getRowHeight());

        int height = headerSize.height +
        (rowCount * (entityTable.getRowHeight()));
        print(" - Calculated preferred height for " + rowCount + " rows = " + height);

        return height;
    }
    private Button createButton(String text, String command) {
        Button button = new Button(text);
        button.setActionCommand(command);
        button.addActionListener(this);
        return button;
    }
    public static void main(String args[]) {
        new TestJTable ();
    }
//---------- ActionListener Implementation ---------------
    public void actionPerformed(ActionEvent evt) {
        String command = evt.getActionCommand().intern();
        if (command == "Reload") {
            entityRowCount = entityRowCount + 2;
            entityData += "X";
            entityModel.fireTableDataChanged();

        } else if (command == "Select2") {
            // structureTable.getSelectionModel()
            // .setAnchorSelectionIndex(2);
            structureTable.setEditingRow(2);

        } else if (command == "Select3") {
            structureTable.getSelectionModel()
                .setLeadSelectionIndex(3);
        }
    }
//---------- MouseListener Implementation ----------------
    public void mouseClicked(MouseEvent evt) {
        if (evt.getSource() == structureTable) {
            int index = getSelectedIndex();
            print(".mouseClicked() - index = " + index);

            if (index < 0)
                return;         // Clicked off grid

            if (evt.getClickCount() == 1) {
                print(".mouseClicked() single " + index);

            } else if (evt.getClickCount() == 2) {
                print(".mouseClicked() double " + index);
            }
        }
    }
    public void mousePressed(MouseEvent evt) {
    }
    public void mouseReleased(MouseEvent evt) {
    }
    public void mouseEntered(MouseEvent evt) {
    }
    public void mouseExited(MouseEvent evt) {
    }

// Returns -1 if none
    public int getSelectedIndex() {
        int index = structureTable.getSelectedRow();
        // Swing bug, index can be > items.size() - 1
        if (index < 0 || index > 10) {
            return -1;          // Clicked on whitespace
        } else {
            return index;
        }
    }
//---------- Public Methods ------------------------------
/**
 * Installs the LookAndFeel as defined by the current
 * constant. Call this once in system startup. (from WindowLib)
 */
    public static void installLookAndFeel() {
        try {
            // Could use UIManager.setLookAndFeel(
            // "com.sun.java.swing.plaf.metal.MetalLookAndFeel");
            if (useCrossPlatformLookAndFeel) {
                UIManager.setLookAndFeel(UIManager
                                   .getCrossPlatformLookAndFeelClassName());
            } else {
                UIManager.setLookAndFeel(UIManager
                                         .getSystemLookAndFeelClassName());
            }
        } catch(Exception ex) {
            print(".installLookAndFeel() - Cannot set look and feel");
            ex.printStackTrace();
        }
    }
//---------- Private Methods -----------------------------
    private void addColumn(JTable table, int index,
                            int columns, String label) {

        int calcWidth = getColumnsWidth(columns, (Component) table);
        TableColumn column = new TableColumn();
        column.setModelIndex(index);
        column.setHeaderValue(label);
        column.setWidth(calcWidth);
        table.addColumn(column);
    }
/**
 * Returns the number of pixels required for the number of
 * columns to draw "average" text on the Component.
 * Useful for setting TableColumn's width. (from VisualLib)
 */
    // Note that the technique in JTextField.getColumnWidth()
    // produces about 50% too wide a result
    // This is return columns * metrics.charWidth('m');
    private static int getColumnsWidth(int columns, Component comp) {
        FontMetrics metrics = comp.getToolkit().getFontMetrics(
                                                            comp.getFont());
        String text = "AbCdEfGhIjKlMnOpQrStUvWxYzAbCd"; // 30
        int textWidth = metrics.stringWidth(text);
        return (textWidth * columns) / 30;
    }

//--- Std
    private static void print(String text) {
        System.out.println("TestJTable" + text);
    }
//========== Inner Classes ===============================
    class EntityModel extends AbstractTableModel {
        //----- Abstract implementation  -----
        // ColumnCount = 0 since we supply TableColumns above
        public int getColumnCount() {
            return 0;
        }
        public int getRowCount() {
            return entityRowCount;
        }
        public Object getValueAt(int row, int col) {
            return entityData + (++row * ++col);
        }
        //----- Superclass Overrides -----
        // Had to override since protected
        protected void fireTableDataChanged() {
            super.fireTableDataChanged();
        }
    }                           // End inner class
    class StructureModel extends AbstractTableModel {
        //----- Abstract implementation  -----
        // ColumnCount = 0 since we supply TableColumns above
        public int getColumnCount() {
            return 0;
        }
        public int getRowCount() {
            return 10;
        }
        public Object getValueAt(int row, int col) {
            switch (col) {
            case 0:
                return "UserID";
            case 1:
                return "String";
            case 2:
                return "Soft and cuddly";
            default:
                return "#Error#";
            }
        }
    }                           // End inner class

}                               // End outer class

(Review ID: 34396)
======================================================================

Comments
WORK AROUND Name: tb29552 Date: 10/02/98 None ======================================================================
11-06-2004

EVALUATION The code apears to neglect the interCellSpacing. See the getPreferredSize() methods in the BasicTableUI for code to calcualte the table height. philip.milne@eng 1999-04-14
14-04-1999