package org.openswing.swing.table.renderers.client;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import org.openswing.swing.table.client.*;
import org.openswing.swing.table.model.client.*;
import org.openswing.swing.util.client.*;
import org.openswing.swing.client.ImagePanel;
import org.openswing.swing.util.java.Consts;
import org.openswing.swing.table.columns.client.Column;
import org.openswing.swing.table.columns.client.ComboColumn;
import org.openswing.swing.table.columns.client.ButtonColumn;
import org.openswing.swing.table.columns.client.CheckBoxColumn;
import org.openswing.swing.table.columns.client.ComboVOColumn;
import org.openswing.swing.table.columns.client.CurrencyColumn;
import org.openswing.swing.table.columns.client.DateColumn;
import org.openswing.swing.table.columns.client.DecimalColumn;
import org.openswing.swing.table.columns.client.FormattedTextColumn;
import org.openswing.swing.table.columns.client.IntegerColumn;
import org.openswing.swing.table.columns.client.CodLookupColumn;
import org.openswing.swing.table.columns.client.PercentageColumn;
import org.openswing.swing.table.columns.client.TextColumn;
/**
* <p>Title: OpenSwing Framework</p>
* <p>Description: cell renderer used to show within a cell renderer in case of expandable rows.</p>
* <p>Copyright: Copyright (C) 2006 Mauro Carniel</p>
*
* <p> This file is part of OpenSwing Framework.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the (LGPL) Lesser General Public
* License as published by the Free Software Foundation;
*
* GNU LESSER GENERAL PUBLIC LICENSE
* Version 2.1, February 1999
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* The author may be contacted at:
* maurocarniel@tin.it</p>
*
* @author Mauro Carniel
* @version 1.0
*/
public class ExpandableRenderer extends DefaultTableCellRenderer {
/** cell content */
private JPanel mainPanel = new JPanel();
/** default cell content */
private TableCellRenderer defaultCellRenderer = null;
/** JTable component */
private Grid grid = null;
/** JTable container */
private Grids grids = null;
/** TableModel adapter, used to link ValueObjects to TableModel */
private VOListAdapter modelAdapter = null;
/** expandable column index */
private int expandableColumn;
/** plus image to used when expanding row */
private Image plusImage = null;
/** minus image to used when expanding row */
private Image minusImage = null;
/** tree lines image panel to used when expanding row */
private TreeLinesPanel treeLinesPanel = new TreeLinesPanel();
/** current editing row*/
private int row = -1;
/** tree panel used in renderer */
private PlusPanel rendTreePanel = new PlusPanel();
/** tree panel used in nested component container */
private MinusPanel expTreePanel = new MinusPanel();
/** current expanded row; -1 if no row is currently expanded */
private int currentExpandedRow = -1;
/** variable used to store the old value of "resizingAllowed" grid property */
private Boolean oldResizingAllowed = null;
public ExpandableRenderer(Grid grid,Grids grids,int expandableColumn,VOListAdapter modelAdapter) {
this.grid = grid;
this.grids = grids;
this.expandableColumn = expandableColumn;
this.modelAdapter = modelAdapter;
// mainPanel.setBackground(defaultColor);
rendTreePanel.setOpaque(false);
rendTreePanel.setMinimumSize(new Dimension(13,13));
treeLinesPanel.setSize(12,12);
treeLinesPanel.setMinimumSize(new Dimension(12,12));
treeLinesPanel.setMaximumSize(new Dimension(12,12));
modelAdapter.getGrids().getVOListTableModel().addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
if (e.getType()==e.DELETE || e.getType()==e.INSERT) {
if (getCurrentExpandedRow()!=-1)
collapseRow(getCurrentExpandedRow());
}
}
});
grid.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
VOListTableModel model = (VOListTableModel)ExpandableRenderer.this.grid.getModel();
Point p1 = e.getPoint();
int width = 0;
for(int i=0;i<ExpandableRenderer.this.expandableColumn;i++)
width += ExpandableRenderer.this.grid.getColumnModel().getColumn(i).getWidth();
row = ExpandableRenderer.this.grid.getSelectedRow();
if (ExpandableRenderer.this.grid.isOverwriteRowWhenExpanding()) {
// show inner component in this row
if (SwingUtilities.isLeftMouseButton(e) &&
e.getClickCount()==1 &&
p1.x>=width && p1.x<=width+ExpandableRenderer.this.grid.getColumnModel().getColumn(ExpandableRenderer.this.expandableColumn).getWidth()) {
if (row!=-1 &&
ExpandableRenderer.this.grid.getExpandableRowController().isRowExpandable(model,row)) {
// current cell is expandable...
if (ExpandableRenderer.this.grids.isRowExpanded(row)) {
collapseRow(row);
}
else {
expandRow(row);
}
ExpandableRenderer.this.grid.repaint();
}
}
}
else {
// show inner component in next row
if (SwingUtilities.isLeftMouseButton(e) &&
e.getClickCount()==1 &&
p1.x>=width && p1.x<=width+rendTreePanel.getWidth()) {
if (row!=-1 &&
ExpandableRenderer.this.grid.getExpandableRowController().isRowExpandable((VOListTableModel)ExpandableRenderer.this.grid.getModel(),row)) {
// current cell is expandable...
if (ExpandableRenderer.this.grids.isRowExpanded(row)) {
collapseRow(row);
}
else {
expandRow(row);
}
ExpandableRenderer.this.grid.repaint();
}
}
}
}
});
}
/**
* @return current expanded row; -1 if no row is currently expanded
*/
public int getCurrentExpandedRow() {
return currentExpandedRow;
}
/**
* If expandableRenderer attribute is not null and
* there is an expanded row, then refresh it.
* This method can be invoked by Form when updating a nested Form panel,
* in order to refresh expanded row content.
*/
public final void refreshExpandableRenderer() {
if (currentExpandedRow!=-1) {
int row = currentExpandedRow;
collapseRow(row);
expandRow(row);
}
}
private void collapseAllRowsExcept(int row) {
for(int i=0;i<grids.getVOListTableModel().getRowCount();i++)
if (i!=row && grids.isRowExpanded(i))
collapseRow(i);
}
public final void collapseRow(int row) {
currentExpandedRow = -1;
ExpandablePanel c = (ExpandablePanel)grids.getComponentInCache(row);
if (c!=null)
grid.remove(c);
grids.collapseRow(row);
if (grid.getRowHeight(row)!=grid.getRowHeight())
grid.setRowHeight(row,grid.getRowHeight());
int[] cols = new int[grid.getColumnModel().getColumnCount()-expandableColumn];
for(int i=0;i<cols.length;i++)
cols[i] = i+expandableColumn;
grid.removeMergedCells(new int[]{row},cols);
if (grid.getExpandableRowController().removeShowedComponent(grids.getVOListTableModel(),row,(JComponent)c.getNestedComponent()))
grids.removeComponentInCache(row);
if (!grids.isAnyRowExpanded() && oldResizingAllowed!=null) {
grid.setResizingAllowed(oldResizingAllowed.booleanValue());
}
}
public final Component expandRow(final int row) {
ClientUtils.fireBusyEvent(true);
try {
if (ExpandableRenderer.this.grid.isSingleExpandableRow()) {
collapseAllRowsExcept(row);
}
grids.expandRow(row);
currentExpandedRow = row;
if (oldResizingAllowed==null)
oldResizingAllowed = new Boolean(grid.getTableHeader().getResizingAllowed());
grid.setResizingAllowed(false);
ExpandablePanel expPanel = (ExpandablePanel)grids.getComponentInCache(row);
Component c = null;
if (expPanel==null)
c = grid.getExpandableRowController().getComponentToShow(grids.getVOListTableModel(),row);
else
c = expPanel.getNestedComponent();
if (c!=null) {
// c is the nested component
// now the size of the container of nested component is determined (as <width,height>)
int width = 0;
for(int i=expandableColumn;i<grid.getColumnModel().getColumnCount();i++) {
width += grid.getColumnModel().getColumn(i).getWidth();
}
int height = grid.getRowHeight(row);
Dimension dim = c.getSize();
if (dim.width==0 && dim.height==0)
dim = c.getPreferredSize();
int delta = 0;
if (!grid.isOverwriteRowWhenExpanding())
delta = grid.getRowHeight(row);
dim.height += delta;
if (dim.getHeight()>height) {
height = dim.height;
if (grid.getRowHeight(row)!=height)
grid.setRowHeight(row,height);
}
// now nested component size is defined
((JComponent)c).setMaximumSize(new Dimension(width-13,height-delta));
((JComponent)c).setSize(new Dimension(width-13,height-delta));
((JComponent)c).setPreferredSize(new Dimension(width-13,height-delta));
// grid cell spans are changed, according to nested component dimension
int[] cols = new int[grid.getColumnModel().getColumnCount()-expandableColumn];
for(int i=0;i<cols.length;i++)
cols[i] = i+expandableColumn;
grid.mergeCells(new int[]{row},cols);
// according to "isOverwriteRowWhenExpanding" value,
// nested component is added to grid or
// expanded row + nested component are added to grid
if (!grid.isOverwriteRowWhenExpanding()) {
JPanel nestedCompContainer = new JPanel();
nestedCompContainer.setLayout(null);
JPanel aux = new JPanel();
aux.setLayout(null);
// aux.setBackground(grid.getGridColor());
Component comp = null;
// JPanel rendPanel = null;
int w = 0;
JLabel lb = null;
JLabel cc = null;
int leftMargin = 0;
int rightMargin = 0;
int topMargin = 0;
int bottomMargin = 0;
Column col = null;
for(int i=expandableColumn;i<grid.getColumnModel().getColumnCount();i++) {
comp = modelAdapter.getCellRenderer(grid.convertColumnIndexToModel(i)).getTableCellRendererComponent(grid, grid.getValueAt(row,i), false, false, row, i);
col = modelAdapter.getFieldColumn(grid.convertColumnIndexToModel(i));
leftMargin = 0;
rightMargin = 0;
topMargin = 0;
bottomMargin = 0;
if (col.getColumnType()==col.TYPE_COMBO) {
leftMargin = ((ComboColumn)col).getLeftMargin();
rightMargin = ((ComboColumn)col).getRightMargin();
topMargin = ((ComboColumn)col).getTopMargin();
bottomMargin = ((ComboColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_COMBO_VO) {
leftMargin = ((ComboVOColumn)col).getLeftMargin();
rightMargin = ((ComboVOColumn)col).getRightMargin();
topMargin = ((ComboVOColumn)col).getTopMargin();
bottomMargin = ((ComboVOColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_CURRENCY) {
leftMargin = ((CurrencyColumn)col).getLeftMargin();
rightMargin = ((CurrencyColumn)col).getRightMargin();
topMargin = ((CurrencyColumn)col).getTopMargin();
bottomMargin = ((CurrencyColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_DEC) {
leftMargin = ((DecimalColumn)col).getLeftMargin();
rightMargin = ((DecimalColumn)col).getRightMargin();
topMargin = ((DecimalColumn)col).getTopMargin();
bottomMargin = ((DecimalColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_FORMATTED_TEXT) {
leftMargin = ((FormattedTextColumn)col).getLeftMargin();
rightMargin = ((FormattedTextColumn)col).getRightMargin();
topMargin = ((FormattedTextColumn)col).getTopMargin();
bottomMargin = ((FormattedTextColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_INT) {
leftMargin = ((IntegerColumn)col).getLeftMargin();
rightMargin = ((IntegerColumn)col).getRightMargin();
topMargin = ((IntegerColumn)col).getTopMargin();
bottomMargin = ((IntegerColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_LOOKUP) {
leftMargin = ((CodLookupColumn)col).getLeftMargin();
rightMargin = ((CodLookupColumn)col).getRightMargin();
topMargin = ((CodLookupColumn)col).getTopMargin();
bottomMargin = ((CodLookupColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_PERC) {
leftMargin = ((PercentageColumn)col).getLeftMargin();
rightMargin = ((PercentageColumn)col).getRightMargin();
topMargin = ((PercentageColumn)col).getTopMargin();
bottomMargin = ((PercentageColumn)col).getBottomMargin();
}
else if (col.getColumnType()==col.TYPE_TEXT) {
leftMargin = ((TextColumn)col).getLeftMargin();
rightMargin = ((TextColumn)col).getRightMargin();
topMargin = ((TextColumn)col).getTopMargin();
bottomMargin = ((TextColumn)col).getBottomMargin();
}
if (i==expandableColumn && col.getColumnType()==col.TYPE_COMBO ||
comp.getWidth()==0 && comp.getHeight()==0) {
if (comp instanceof JLabel) {
lb = ((JLabel)comp);
cc = new JLabel(lb.getText());
cc.setBackground(lb.getBackground());
cc.setForeground(lb.getForeground());
cc.setFont(lb.getFont());
cc.setHorizontalAlignment(lb.getHorizontalAlignment());
cc.setHorizontalTextPosition(lb.getHorizontalTextPosition());
comp = cc;
}
comp.setSize(
grid.getColumnModel().getColumn(i + expandableColumn).getWidth() - 1 - (i == expandableColumn ? 12 : 0)-leftMargin-rightMargin,
delta
);
}
if (i==expandableColumn) {
aux.add(expTreePanel);
expTreePanel.setBackground(comp.getBackground());
expTreePanel.setBounds(0,0,13,delta);
}
aux.add(comp);
comp.setBounds(
w+(i==expandableColumn?+12:0)+leftMargin,
0+topMargin,
grid.getColumnModel().getColumn(i+expandableColumn).getWidth()-1-(i==expandableColumn?12:0)-leftMargin-rightMargin,
delta-topMargin-bottomMargin
);
w += grid.getColumnModel().getColumn(i+expandableColumn).getWidth();
JSeparator sep = new JSeparator(JSeparator.VERTICAL);
aux.add(sep);
sep.setBounds(w-1,0,1,delta);
aux.setBackground(comp.getBackground());
}
aux.setSize(w,delta);
aux.setMinimumSize(new Dimension(w,delta));
nestedCompContainer.setMinimumSize(new Dimension(width,height));
nestedCompContainer.setSize(new Dimension(width,height));
nestedCompContainer.setPreferredSize(new Dimension(width,height));
treeLinesPanel.setBackground(expTreePanel.getBackground());
nestedCompContainer.add(aux);
aux.setBounds(0,0,w,delta);
nestedCompContainer.add(treeLinesPanel);
treeLinesPanel.setBounds(1,delta,treeLinesPanel.getWidth(),treeLinesPanel.getHeight());
nestedCompContainer.add(c);
c.setBounds(treeLinesPanel.getWidth()+1,delta,c.getWidth(),c.getHeight());
nestedCompContainer.setFocusable(true);
expPanel = new ExpandablePanel(grid,c,nestedCompContainer);
nestedCompContainer.setBackground(comp.getBackground());
expPanel.setBackground(comp.getBackground());
grids.putComponentInCache(row,expPanel);
grid.add(expPanel);
int y = 0;
for(int i=0;i<row;i++)
y += grid.getRowHeight(i);
int x = 0;
for(int i=0;i<expandableColumn;i++)
x += grid.getColumnModel().getColumn(i).getWidth();
expPanel.setBounds(x,y,width,height);
}
else {
expPanel = new ExpandablePanel(grid,c,c);
grids.putComponentInCache(row,expPanel);
grid.add(expPanel);
int y = grid.getRowHeight();
for(int i=0;i<row;i++)
y += grid.getRowHeight(i);
int x = 13;
for(int i=0;i<expandableColumn;i++)
x += grid.getColumnModel().getColumn(i).getWidth();
expPanel.setBounds(x,y,width,height);
}
grids.setCurrentNestedComponent(row,expPanel);
final ExpandablePanel aux = expPanel;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
grid.repaint();
final Component comp = grid.getExpandableRowController().getFocusableComponent((JComponent)aux.getNestedComponent());
if (comp!=null) {
comp.requestFocus();
if (comp instanceof JComponent) {
Action exitAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
((JComponent)comp).getInputMap().remove(ClientSettings.COLLAPSE_CELL_KEY);
((JComponent)comp).getActionMap().remove("exitAction");
collapseRow(row);
grid.requestFocus();
}
};
((JComponent)comp).getInputMap().put(ClientSettings.COLLAPSE_CELL_KEY,"exitAction");
((JComponent)comp).getActionMap().put("exitAction",exitAction);
}
}
else
aux.requestFocus();
}
});
return expPanel;
}
else
return null;
}
catch(Throwable t) {
t.printStackTrace();
return null;
}
finally {
ClientUtils.fireBusyEvent(false);
}
}
public void setDefaultCellRenderer(TableCellRenderer rend) {
this.defaultCellRenderer = rend;
}
/**
*
* Returns the default table cell renderer.
*
* @param table the <code>JTable</code>
* @param value the value to assign to the cell at
* <code>[row, column]</code>
* @param isSelected true if cell is selected
* @param hasFocus true if cell has focus
* @param row the row of the cell to render
* @param column the column of the cell to render
* @return the default table cell renderer
*/
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
this.row = row;
if (!grid.getExpandableRowController().isRowExpandable((VOListTableModel)grid.getModel(),row)) {
if (column==expandableColumn) {
Component comp = defaultCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
rendTreePanel.setBackground(comp.getBackground());
mainPanel.setBackground(comp.getBackground());
mainPanel.removeAll();
mainPanel.setLayout(new BorderLayout(2,0));
mainPanel.add(comp,BorderLayout.CENTER);
if (!grid.isOverwriteRowWhenExpanding())
mainPanel.add(rendTreePanel,BorderLayout.WEST);
mainPanel.revalidate();
return mainPanel;
}
else
return defaultCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
// current row is expandable...
if (column<expandableColumn)
return defaultCellRenderer.getTableCellRendererComponent(table, value, isSelected && !grids.isRowExpanded(row), !grids.isRowExpanded(row) && hasFocus, row, column);
else {
// column>=expandableColumn
if (!grids.isRowExpanded(row)) {
// row is expandable and not yet expanded...
if (column==expandableColumn) {
mainPanel.removeAll();
mainPanel.setLayout(new BorderLayout(2,0));
Component comp = defaultCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
mainPanel.add(comp,BorderLayout.CENTER);
mainPanel.setBackground(comp.getBackground());
rendTreePanel.setBackground(comp.getBackground());
if (!grid.isOverwriteRowWhenExpanding())
mainPanel.add(rendTreePanel,BorderLayout.WEST);
mainPanel.revalidate();
return mainPanel;
}
else
// column>expandableColumn
return defaultCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
else {
// column>=expandableColumn and
// row is expandable and
// already expanded...
Component c = (Component)grids.getComponentInCache(row);
if (c!=null) {
// c.setBounds(
// 0,
// 0,
// defaultCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column).getWidth(),
// grid.getRowHeight(row)
// );
c.setFocusable(true);
if (grid.isOverwriteRowWhenExpanding())
return c;
else {
mainPanel.removeAll();
return mainPanel;
}
}
else
// this is an error...
return defaultCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}
/**
* <p>Title: OpenSwing Framework</p>
* <p>Description: Inner class that draw a vertical line</p>
* @version 1.0
*/
class LinePanel extends JPanel {
public LinePanel() {
setMaximumSize(new Dimension(1,grid.getRowHeight()));
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(grid.getGridColor());
g.drawLine(0,0,0,getHeight());
}
}
/**
* <p>Title: OpenSwing Framework</p>
* <p>Description: Inner class that draw the tree lines.</p>
* @version 1.0
*/
class TreeLinesPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
g.setColor(new Color(128,128,128));
g.drawLine(getWidth()/2-2,0,getWidth()/2-2,10);
g.drawLine(getWidth()/2-2,10,getWidth(),10);
}
}
/**
* <p>Title: OpenSwing Framework</p>
* <p>Description: Inner class that draw the expansion box (used on renderering cells).</p>
* @version 1.0
*/
class PlusPanel extends JPanel {
public final void paintComponent(Graphics g) {
super.paintComponent(g);
if (!ExpandableRenderer.this.grid.isOverwriteRowWhenExpanding()) {
if (row!=-1 &&
grid.getExpandableRowController().isRowExpandable((VOListTableModel)grid.getModel(),row)) {
if (plusImage==null)
plusImage = ClientUtils.getImage("plus.gif");
g.drawImage(plusImage,0,getHeight()/2-plusImage.getHeight(this)/2,plusImage.getWidth(this),plusImage.getHeight(this),this);
}
}
}
}
/**
* <p>Title: OpenSwing Framework</p>
* <p>Description: Inner class that draw the tree collapsed box (used by the container of nested component).</p>
* @version 1.0
*/
class MinusPanel extends JPanel {
public final void paintComponent(Graphics g) {
super.paintComponent(g);
if (!ExpandableRenderer.this.grid.isOverwriteRowWhenExpanding()) {
if (minusImage==null)
minusImage = ClientUtils.getImage("minus.gif");
g.setColor(new Color(128,128,128));
g.drawLine(minusImage.getWidth(this)/2,getHeight()/2+minusImage.getHeight(this)/2-1,minusImage.getWidth(this)/2,getHeight());
g.drawImage(minusImage,0,getHeight()/2-minusImage.getHeight(this)/2,minusImage.getWidth(this),minusImage.getHeight(this),this);
}
}
}
}