/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.bearsoft.gui.grid.header;
import com.bearsoft.gui.grid.header.cell.HeaderCell;
import com.eas.gui.ScriptColor;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.util.*;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.event.RowSorterListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
/**
*
* @author mg
*/
public class MultiLevelHeader extends JPanel {
public static final int PICK_MARGIN_SIZE = 2;
// design data
protected TableColumnModel columnModel;
protected RowSorter<? extends TableModel> rowSorter;
//protected InnerColumnsListener columnModelListner;
protected RowSorterListener sorterListener;
protected JTable table;
// calculated data
protected MultiLevelHeader neightbour;
protected List<GridColumnsNode> roots;
protected Map<GridColumnsNode, GridBagConstraints> group2Constraints = new HashMap<>();
protected GridColumnsNode pressed4ResizeColGroup;
protected GridColumnsNode resizingColGroup;
protected GridColumnsNode movingColGroup;
protected JTableHeader[] slaveHeaders;
public MultiLevelHeader() {
super();
}
public void setSlaveHeaders(JTableHeader... aSlaveHeaders) {
slaveHeaders = aSlaveHeaders;
}
public MultiLevelHeader getNeightbour() {
return neightbour;
}
public void setNeightbour(MultiLevelHeader aValue) {
neightbour = aValue;
}
@Override
public void scrollRectToVisible(Rectangle aRect) {
}
public TableColumnModel getColumnModel() {
return columnModel;
}
public final void setColumnModel(TableColumnModel aModel) {
columnModel = aModel;
/*
columnModelListner = new InnerColumnsListener(this);
if (columnModel != null) {
columnModel.addColumnModelListener(columnModelListner);
}
*/
}
public RowSorter<? extends TableModel> getRowSorter() {
return rowSorter;
}
public void setRowSorter(RowSorter<? extends TableModel> aSorter) {
rowSorter = aSorter;
sorterListener = new SorterListener(this);
if (rowSorter != null) {
rowSorter.addRowSorterListener(sorterListener);
}
}
public JTable getTable() {
return table;
}
public void setTable(JTable aValue) {
table = aValue;
}
/*
public InnerColumnsListener getColumnModelListener() {
return columnModelListner;
}
*/
public GridColumnsNode getPressed4ResizeColGroup() {
return pressed4ResizeColGroup;
}
public void setPressed4ResizeColGroup(GridColumnsNode aColGroup) {
if (aColGroup == null || aColGroup.isResizable()) {
pressed4ResizeColGroup = aColGroup;
}
}
public GridColumnsNode getResizingColGroup() {
return resizingColGroup;
}
public void setResizingColGroup(GridColumnsNode aColGroup) {
if (aColGroup == null || aColGroup.isResizable()) {
resizingColGroup = aColGroup;
}
}
public GridColumnsNode getMovingColGroup() {
return movingColGroup;
}
public void setMovingColGroup(GridColumnsNode aColGroup) {
if (aColGroup == null || aColGroup.isMovable()) {
movingColGroup = aColGroup;
}
}
@Override
public void doLayout() {
if (table != null) {
table.setSize(getSize().width, table.getSize().height);
table.doLayout();
}
super.doLayout();
}
@Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.width = columnModel.getTotalColumnWidth();
if (neightbour != null) {
MultiLevelHeader oldNeightbour = neightbour.getNeightbour();
neightbour.setNeightbour(null);
try {
Dimension neightbourD = neightbour.getPreferredSize();
d.height = Math.max(neightbourD.height, d.height);
} finally {
neightbour.setNeightbour(oldNeightbour);
}
}
return d;
}
private int processGroups1(List<GridColumnsNode> aGroups, int aLevel, int maxLevel) {
int deepChildrenCount = 0;
for (GridColumnsNode group : aGroups) {
GridBagConstraints constraints = group2Constraints.get(group);
if (constraints == null) {
constraints = new GridBagConstraints();
constraints.weightx = 0;
constraints.weighty = 1;
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.BOTH;
group2Constraints.put(group, constraints);
}
constraints.gridy = aLevel;
if (group.hasChildren()) {
constraints.gridheight = 1;
} else {
constraints.gridheight = maxLevel - constraints.gridy + 1;
++deepChildrenCount;
}
int childrenCount = processGroups1(group.getChildren(), aLevel + 1, maxLevel);
deepChildrenCount += childrenCount;
constraints.gridwidth = Math.max(1, childrenCount);
}
return deepChildrenCount;
}
private void processGroups2(List<GridColumnsNode> aGroups) {
if (!aGroups.isEmpty()) {
GridColumnsNode group = aGroups.get(0);
GridBagConstraints constraints = group2Constraints.get(group);
assert constraints != null;
if (group.getParent() != null) {
GridBagConstraints parentConstraints = group2Constraints.get(group.getParent());
assert parentConstraints != null;
constraints.gridx = parentConstraints.gridx;
} else {
constraints.gridx = 0;
}
processGroups2(group.getChildren());
}
for (int i = 1; i < aGroups.size(); i++) {
GridColumnsNode groupPrev = aGroups.get(i - 1);
GridColumnsNode group = aGroups.get(i);
GridBagConstraints constraints = group2Constraints.get(group);
assert constraints != null;
GridBagConstraints prevConstraints = group2Constraints.get(groupPrev);
assert prevConstraints != null;
constraints.gridx = prevConstraints.gridx + prevConstraints.gridwidth;
processGroups2(group.getChildren());
}
}
private int getMaxLevel(int aLevel, List<GridColumnsNode> aRoots) {
int maxLevel = aLevel;
for (GridColumnsNode aRoot : aRoots) {
if (aRoot.hasChildren()) {
int level = getMaxLevel(aLevel + 1, aRoot.getChildren());
if (level > maxLevel) {
maxLevel = level;
}
}
}
return maxLevel;
}
protected GridColumnsNode getRoot(GridColumnsNode aGroup) {
GridColumnsNode cGroup = aGroup;
GridColumnsNode rGroup = aGroup;
while (cGroup.getParent() != null) {
cGroup = cGroup.getParent();
rGroup = cGroup;
}
return rGroup;
}
public void regenerate() {
removeAll();
if (columnModel == null) {
throw new NullPointerException("TreedTableHeader needs a column model, but it is absent.");
}
group2Constraints.clear();
if (roots == null) {
setRoots(wrapColumnsCalculateRoots());
}
int maxLevel = getMaxLevel(0, roots);
processGroups1(roots, 0, maxLevel);
processGroups2(roots);
fillControl();
invalidate();
repaint();
}
public List<GridColumnsNode> getRoots() {
return roots;
}
private void clearTableColumn(List<GridColumnsNode> aForest) {
for (GridColumnsNode node : aForest) {
node.setTableColumn(null);
node.setStyleSource(null);
clearTableColumn(node.getChildren());
}
}
public final void setRoots(List<GridColumnsNode> aValue) {
if (roots != aValue) {
if (roots != null) {
clearTableColumn(roots);// avoid GridColumnsNode leak
}
roots = aValue;
if (roots != null) {
List<GridColumnsNode> leaves = new ArrayList<>();
MultiLevelHeader.achieveLeaves(roots, leaves);
}
}
}
protected List<GridColumnsNode> wrapColumnsCalculateRoots() {
List<GridColumnsNode> roots = new ArrayList<>();
Set<GridColumnsNode> met = new HashSet<>();
for (int i = 0; i < columnModel.getColumnCount(); i++) {
TableColumn col = columnModel.getColumn(i);
GridColumnsNode group = new GridColumnsNode(col);
GridColumnsNode rootGroup = getRoot(group);
if (!met.contains(rootGroup)) {
met.add(rootGroup);
roots.add(rootGroup);
}
}
return roots;
}
private void fillControl() {
setFocusCycleRoot(false);
setLayout(new GridBagLayout());
int maxRow = -1;
int maxColumn = -1;
for (Map.Entry<GridColumnsNode, GridBagConstraints> entry : group2Constraints.entrySet()) {
GridBagConstraints gbc = entry.getValue();
if (maxRow < gbc.gridy) {
maxRow = gbc.gridy;
}
if (maxColumn < gbc.gridx) {
maxColumn = gbc.gridx;
}
add(new HeaderCell(entry.getKey(), this), gbc);
}
GridBagConstraints goatGbc = new GridBagConstraints();
goatGbc.gridx = maxColumn + 1;
goatGbc.gridy = maxRow + 1;
goatGbc.weightx = 1;
goatGbc.weighty = 1;
add(new HeaderCell(new GridColumnsNode(), this), goatGbc);
}
public static void achieveLeaves(GridColumnsNode aGroup, List<GridColumnsNode> aLeaves) {
if (aGroup.isLeaf()) {
aLeaves.add(aGroup);
}
for (int i = 0; i < aGroup.getChildren().size(); i++) {
achieveLeaves(aGroup.getChildren().get(i), aLeaves);
}
}
public static void achieveLeaves(List<GridColumnsNode> aRoots, List<GridColumnsNode> aLeaves) {
for (GridColumnsNode node : aRoots) {
if (node.isLeaf()) {
aLeaves.add(node);
} else {
achieveLeaves(node.getChildren(), aLeaves);
}
}
}
public void checkStructure() {
List<GridColumnsNode> leaves = new ArrayList<>();
MultiLevelHeader.achieveLeaves(roots, leaves);
assert leaves.size() == columnModel.getColumnCount();
for (int i = 0; i < leaves.size(); i++) {
assert leaves.get(i).getTableColumn() == columnModel.getColumn(i);
}
}
public static void simulateMouseEntered(HeaderCell aCell, MouseEvent e) {
Point pt = e.getPoint();
Component releasedComponent = aCell.getHeader().getComponentAt(new Point(aCell.getX() + pt.x, aCell.getY() + pt.y));
if (releasedComponent != null
&& releasedComponent instanceof HeaderCell
&& releasedComponent != aCell) {
aCell.getHeader().setResizingColGroup(null);
aCell.getHeader().setPressed4ResizeColGroup(null);
aCell.getHeader().setMovingColGroup(null);
MouseListener[] ml = releasedComponent.getMouseListeners();
for (MouseListener l : ml) {
l.mouseEntered(e);
}
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (isOpaque() && g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
Color backColor = getBackground();
Color ltBackColor = ScriptColor.brighter(backColor, 0.95);
Color dkBackColor = ScriptColor.darker(backColor, 0.95);
Dimension size = getSize();
Paint gradient1 = new GradientPaint(new Point2D.Float(0, 0), ltBackColor, new Point2D.Float(0, size.height / 2), backColor);
Paint gradient2 = new GradientPaint(new Point2D.Float(0, size.height / 2 + 1), dkBackColor, new Point2D.Float(0, size.height), backColor);
g2d.setPaint(gradient1);
g2d.fillRect(0, 0, size.width, size.height / 2);
g2d.setPaint(gradient2);
g2d.fillRect(0, size.height / 2, size.width, size.height / 2);
}
}
}