Custom JComponent: add to contentPane  
Author Message
Christian Vogt





PostPosted: 2003-9-16 22:06:00 Top

java-programmer, Custom JComponent: add to contentPane Hello,

I created a custom JComponent which consists of two JTables on a
JScrollPane.
My problem is when I add a JDividedTable instance to the content pain of a
JFrame,
I only see a grey frame with the preferredSize of the scrollpane of my
custom component.
When I pass the contentPane of the Frame to the constructor of
JCustomComponent
and contentPane.add(scrollPane) call at the end everything works fine.

The solution with the contentPane as parameter in the constructor is not
desirable in thougths of
reusability and flexibility.

Can anybody tell me what is wrong?

I want to use it like this from a JFrame, for example:

public class MyApp extends JFrame {

public MyApp() {
Container cp = getContentPane();
....

JDividedTable dividedTable = new JDividedTable(someModel,3);
cp.add(dividedTable);
...
}

public static void main ....


In JCustomComponent I override set getter and setter methods for the
preferred, maximum and minimum
size of JComponent.

Here is the source of my Component:

-------------------------------------------------------------------------
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.accessibility.AccessibleContext;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**This swing UI class provides a
* {@link javax.swing.JTable JTable} in a {@link javax.swing.JScrollPane
* JScrollPane} with fixed columns on the left side and
* horizontal scrollable columns on the right side. The number of fixed
* columns is user defined within the constructor.<br><br>
*
* The divided table is constructed out of two JTable's with the same
* TableModel. The fixed table is installed as RowHeaderView on the
* JScrollPane. From the scrollable table's TableColumnModel the
* first <code>fixedCols</code> are removed.
*
*/
public class JDividedTable extends JComponent {


private static final Log log = LogFactory.getLog(JDividedTable.class);

/* ==================================================================== */
/**
* Cronstructs a <code>JDividedTable</code> in a <code>JScrollPane</code>
* with a number of <code>fixedCols</code> on the left.
*
* @param dm The underlying tableModel of this table.
* @param fixedCols The number of fixed columns on the left side.
* @param contentPane The container the table should be installed on.
*/
public JDividedTable(TableModel dm, int fixedCols, Container contentPane)
{
fixedColumns = fixedCols;

initDividedTable(dm, null);

removeFixedColsFromScrollableTable();

synchronizeSelection();

putTablesOnContainer(contentPane);
}

/* ==================================================================== */
/**
* Cronstructs a <code>JDividedTable</code> in a <code>JScrollPane</code>
* with a number of <code>fixedCols</code> on the left. The data are
* arranged according to <code>sorter</code> if a sorter is provided.
*
* @param dm The underlying tableModel of this table.
* @param fixedCols The number of fixed columns on the left side.
* @param sorter A custom sorter (optional).
* @param contentPane The container the table should be installed on.
*/
public JDividedTable(TableModel dm, int fixedCols, AbstractSortDecorator
sorter,
Container contentPane) {
fixedColumns = fixedCols;

initDividedTable(dm, sorter);

removeFixedColsFromScrollableTable();

synchronizeSelection();

putTablesOnContainer(contentPane);
}



/* ==================================================================== */
/**Gets the actual sort decorator.
* @return The sort decorator.
*/
public AbstractSortDecorator getSortDecorator () {
return customSorter;
}

/* ==================================================================== */
/**Adds a new <code>AbstractSortDecorator</code> to the divided table.
* @param sorter The new sort decorator.
* @throws IllegalArgumentException If sorter is null.
*/
public void setSortDecorator(AbstractSortDecorator sorter)
throws IllegalArgumentException{

if (sorter == null) {
log.error("AbstractSortDecorator can't be set to 'null'");
throw new IllegalArgumentException("AbstractSortDecorator " +
"can't be set to 'null'");
} else {

if (log.isInfoEnabled()) {
log.info("Adding a sorter to the divided table: '" +
sorter + "'");
}

customSorter = sorter;
fixedTable.setModel(customSorter);
scrollableTable.setModel(customSorter);

removeFixedColsFromScrollableTable();

setMouseListenerToHeader(false);
}
}

/* ==================================================================== */
/**Allows to reorder interactively the columns in the scrollable table
* if <code>allowed</code> is true.
* @param allowed Allow reordering of columns? Per default it is set to
true.
*/
public void setHeaderReorderingAllowed(boolean allowed) {
scrollableTable.getTableHeader().setReorderingAllowed(allowed);
}

/* ==================================================================== */
/**Sets the mouse listeners for the table headers of both tables for the
* sorter and synchronizes both models. When the listener is set to true
* the user can sort the table interactively by clicking on the table
* headers. If it is set to false, the table is sorted according
* to the fixed sort criteria defined in the sorter.<br>
* The default for the divided table is 'listeners off'.<br>
* After a mouseClicked event both tables needs to be repainted because
* otherwise their were problems encountered with updating of the two
* table components.
*
* @param mouseListenerOn Set or remove the listener to/from the headers,
* per default it is set to false. It only adds a
* listener if the divided table has a sorter.
*/
public void setMouseListenerToHeader(boolean mouseListenerOn) {
JTableHeader fixedHdr = (JTableHeader) fixedTable.getTableHeader();
JTableHeader scrollableHdr = (JTableHeader)
scrollableTable.getTableHeader();
MouseListener fixedListener = null;
MouseListener scrollableListener = null;

if ((mouseListenerOn == true) && (customSorter != null)) {
fixedListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
TableColumnModel tcm = fixedTable.getColumnModel();
int vc = tcm.getColumnIndexAtX(e.getX());
int mc = fixedTable.convertColumnIndexToModel(vc);
customSorter.sort(mc);
fixedTable.repaint();
scrollableTable.repaint();
}
};
fixedHdr.addMouseListener(fixedListener);

scrollableListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
TableColumnModel tcm = scrollableTable
.getColumnModel();
int vc = tcm.getColumnIndexAtX(e.getX());
int mc = scrollableTable
.convertColumnIndexToModel(vc);
customSorter.sort(mc);
fixedTable.repaint();
scrollableTable.repaint();
}
};
scrollableHdr.addMouseListener(scrollableListener);
} else if (customSorter != null) {
// If the listener is null, no action is performed by
removeMouseListener().
fixedHdr.removeMouseListener(fixedListener);
scrollableHdr.removeMouseListener(scrollableListener);
customSorter.sort();
}
}

/* ==================================================================== */
/**Creates and initializes the divided table.
*
* @param dm The TableModel.
* @param sorter The sort decorator for this table.
*/
private void initDividedTable(TableModel dm, AbstractSortDecorator
sorter) {

// Try to set sorter between model and view.
if (sorter != null) {

if (log.isInfoEnabled()) {
log.info("Creating a divided table with sorter '" +
sorter.toString() + "'");
}

customSorter = sorter;
fixedTable = new JTable(customSorter);
scrollableTable = new JTable(customSorter);

setMouseListenerToHeader(false);
} else {

if (log.isInfoEnabled()) {
log.info("Creating a divided table without sorter");
}
fixedTable = new JTable(dm);
scrollableTable = new JTable(dm);
}

// Reordering not allowed, because wanted columns can be substituted
with
// unwanted ones in the fixed table with this option set to true.
fixedTable.getTableHeader().setReorderingAllowed(false);
fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

scrollableTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}

/* ==================================================================== */
/**Removes the columns said to be fix from the scrollableTable
TableColumnModel,
* sets the column width of fixedTable-columns to fixed size and
* sets the viewport size of fixedTable to the width of the fixed columns.
*/
private void removeFixedColsFromScrollableTable() {
TableColumnModel tcm = scrollableTable.getColumnModel();
// columnMargin necessary for displaying the grid line.
Dimension dim = new
Dimension(fixedTable.getColumnModel().getColumnMargin(),0);

for(int i = 0;i < fixedColumns; i++) {
dim.width += tcm.getColumn(i).getPreferredWidth();
fixedTable.getColumnModel().getColumn(i).setResizable(false);

// always delete the first column because columns in tcm are reordered
// after deletion
tcm.removeColumn(tcm.getColumn(0));
}
fixedTable.setPreferredScrollableViewportSize(dim);
}

/* ==================================================================== */
/**Creates the scrollPane for scrollableTable and setting its upper
* left corner view to fixedTable. Finally the scrollpane is installed
* on the provided container.
*
* @param contentPane The container the tables should get installed on.
*/
private void putTablesOnContainer(Container contentPane) {
scrollPane = new JScrollPane(scrollableTable);

scrollPane.setRowHeaderView(fixedTable);
scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
fixedTable.getTableHeader());


//with this line everything is ok:
contentPane.add(scrollPane);
}

/* ==================================================================== */
/**Allows only the selection of one row and synchronizes the selection
* in both tables. Both tables should be represented to the user as one.
* Therefore the selection model of the tables must be synchronized.
*/
private void synchronizeSelection() {

fixedTable
.getSelectionModel()
.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
int selRowIndex;

selRowIndex = fixedTable.getSelectedRow();
// only one row can be selected
scrollableTable.setRowSelectionInterval(selRowIndex, selRowIndex);
fixedTable.setRowSelectionInterval(selRowIndex, selRowIndex);
}
});

scrollableTable
.getSelectionModel()
.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
int selRowIndex;

selRowIndex = scrollableTable.getSelectedRow();
// only one row can be selected
fixedTable.setRowSelectionInterval(selRowIndex, selRowIndex);
scrollableTable.setRowSelectionInterval(selRowIndex, selRowIndex);
}
});
}


/* ==================================================================== */
/* (non-Javadoc)
* @see java.awt.Component#getPreferredSize()
*/
public Dimension getPreferredSize() {
return scrollPane.getPreferredSize();
}

/* ==================================================================== */
/* (non-Javadoc)
* @see javax.swing.JComponent#getMaximumSize()
*/
public Dimension getMaximumSize() {
return scrollPane.getMaximumSize();
}

/* ==================================================================== */
/* (non-Javadoc)
* @see javax.swing.JComponent#getMinimumSize()
*/
public Dimension getMinimumSize() {
return scrollPane.getMinimumSize();
}

/* ==================================================================== */
/* (non-Javadoc)
* @see javax.swing.JComponent#setMaximumSize(java.awt.Dimension)
*/
public void setMaximumSize(Dimension maximumSize) {
scrollPane.setMaximumSize(maximumSize);
}

/* ==================================================================== */
/* (non-Javadoc)
* @see javax.swing.JComponent#setMinimumSize(java.awt.Dimension)
*/
public void setMinimumSize(Dimension minimumSize) {
scrollPane.setMinimumSize(minimumSize);
}

/* ==================================================================== */
/* (non-Javadoc)
* @see javax.swing.JComponent#setPreferredSize(java.awt.Dimension)
*/
public void setPreferredSize(Dimension preferredSize) {
scrollPane.setPreferredSize(preferredSize);
}


/* ==================================================================== */
/** The JTable with fixed columns shown on the left. Not horizontally
scrollable. */
private JTable fixedTable;
/** The JTable with horizontally scrollable columns. */
private JTable scrollableTable;
/** The JScrollPane the JTables are installed on. */
private JScrollPane scrollPane;
/** The sorter for this divided table. */
private AbstractSortDecorator customSorter = null;
/** Stores the number of fixed columns on the left. */
private int fixedColumns;
}
 
Christian Vogt





PostPosted: 2003-9-17 15:39:00 Top

java-programmer >> Custom JComponent: add to contentPane Hi all!

Perhaps my posting was a little bit to lenthy and not concrete enough.

So here is the short version of my problem: (SKD 1.4.2, WinXP)

I have a custom JComponent I clearly want to use like any other JComponent.
This custom component is composited out of two JTables and a JScrollPane.

The problem is: how do I add this component to the contentPane of the
JFrame?
Which Methods of JComponent do I have to override? (getPreferredSize() is
one of them, I figured out,
but thats apparently not enought)
When I do it like I wants it to be in the frame class:
getContentPane().add(dividedTable)
the tables of the component aren't displayed. I dont want to call the
constructor with the current contentPane!




> public class JDividedTable extends JComponent {
>
> public JDividedTable(TableModel dm, int fixedCols, Container
> contentPane) {
...

> //makes the composition.
> putTablesOnContainer(contentPane);
> }


> private void putTablesOnContainer(Container contentPane) {
> scrollPane = new JScrollPane(scrollableTable);
>
> scrollPane.setRowHeaderView(fixedTable);
> scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
> fixedTable.getTableHeader());
>
>
> //with this line everything is ok:
> contentPane.add(scrollPane);
> }
> public Dimension getPreferredSize() {
> return scrollPane.getPreferredSize();
> }


The members of this class:

> private JTable fixedTable;
> private JTable scrollableTable;
> private JScrollPane scrollPane;
> private AbstractSortDecorator customSorter = null;
> private int fixedColumns;
 
usenet





PostPosted: 2003-9-17 20:25:00 Top

java-programmer >> Custom JComponent: add to contentPane Christian Vogt <email***@***.com> wrote:

> I created a custom JComponent which consists of two JTables on a
> JScrollPane.
> My problem is when I add a JDividedTable instance to the content pain of a
> JFrame,
> I only see a grey frame with the preferredSize of the scrollpane of my
> custom component.
> When I pass the contentPane of the Frame to the constructor of
> JCustomComponent
> and contentPane.add(scrollPane) call at the end everything works fine.


You need to layout the JScrollPane (if it is supposed to be the root
component), by a LayoutManager or by doLayout directly.

public void doLayout()
{
Insets n = getInsets();

scrollPane.setBounds(n.left, n.top, getWidth() - n.left - n.right, getHeight() - n.top - n.bottom);
}

> /* ==================================================================== */
> /* (non-Javadoc)
> * @see java.awt.Component#getPreferredSize()

What kind of IDE produces such idiotic @see tags?


> */

> public Dimension getPreferredSize() {

if (isPreferredSizeSet())
return super.getPreferredSize();


> return scrollPane.getPreferredSize();

Incorrect if your component has a Border.

> }

[....]
> }
> */
> }
>
> /* ==================================================================== */
> /* (non-Javadoc)
> * @see javax.swing.JComponent#setPreferredSize(java.awt.Dimension)
> */
> public void setPreferredSize(Dimension preferredSize) {
> scrollPane.setPreferredSize(preferredSize);
> }

No need to override (maybe even wrong this way).





Christian
--
"When Mowgli drives Mowgli I will go," Mowgli answered.

Red Dog
 
 
Christian Vogt





PostPosted: 2003-9-17 21:49:00 Top

java-programmer >> Custom JComponent: add to contentPane Hallo,

thanks for your reply.
I tried your suggestions succesfully, after I found an error of mine:
I forgot to add the JScrollPane to my component in the constructor.
Now everything works fine.


>> /* ====================================================================
>> */
>> /* (non-Javadoc)
>> * @see java.awt.Component#getPreferredSize()
>
> What kind of IDE produces such idiotic @see tags?

It's Eclipse standard code generation for comments. Haven't customized it
yet.


Thank you very much,
Christian
 
 
Christian Vogt





PostPosted: 2003-9-17 22:41:00 Top

java-programmer >> Custom JComponent: add to contentPane Hallo,

one more question.
My doLayout() looks like this now (as you suggested):

public void doLayout()
{
Insets n = getInsets();

scrollPane.setBounds(n.left, n.top, getWidth() - n.left - n.right,
getHeight() - n.top - n.bottom);

super.doLayout();
}


The problem is, that the component doesn't resize vertically but
horizontally. I think this has to be fixed within doLayout() as this method
is called with every repaint, I think, and because the
horizontal resizing works quite fine.

Thanks in advance,
Christian
 
 
Christian Vogt





PostPosted: 2003-9-17 23:02:00 Top

java-programmer >> Custom JComponent: add to contentPane Ups, sorry.

> The problem is, that the component doesn't resize vertically but
> horizontally. I think this has to be >fixed within doLayout() as this
> method is called with every repaint, I think, and because the
> horizontal resizing works quite fine.

Stupid layout fault in my testing class, everything is fine.

Greets
Christian