package org.jcon.test;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

/**
 * Test JTable for technique required to size columns,
 * provide data, make splitter work, etc.
 *      java org.jcon.test.TestJTable
 * Filed bug report on header height, row height and
 * preferred width bugs 6/26/98 on Swing 1.02. **********
 *
 * @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(
        //    "javax.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 
    public 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
