All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.odell.glazedlists.swing.ListLayout Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.swing;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Layout list elements by delegating to a {@link FormLayout}. This layout manages
 * the {@link ListLayout} by responding to list changes and shifting components
 * around as elements are inserted or removed.
 *
 * 

Note that this class is subject to significant API changes and improvements, * and that only the {@link JEventListPanel} class should be referenced in * client source code. * * @author Jesse Wilson */ class ListLayout extends LayoutDecorator { /** all components for the entire grid */ private final List gridComponents = new ArrayList(); /** the formlayout does the actual legwork */ private final FormLayout formLayout; /** how to layout logical cells in the formlayout */ private final JEventListPanel.Format format; /** the row and column styles */ private RowSpec[] rowSpecs; private ColumnSpec[] columnSpecs; /** spacers between element columns */ private RowSpec gapRow; private ColumnSpec gapColumn; private Container container; private int elementColumns = 1; private int elementRows = Integer.MAX_VALUE; /** * Layout the specified container using the specified format to manage * rows, columns and constraints. */ public ListLayout(Container container, JEventListPanel.Format format) { this.container = container; this.format = format; this.formLayout = new FormLayout(""); // prepare the empty panel this.columnSpecs = format.getElementColumns(); this.rowSpecs = format.getElementRows(); this.gapRow = format.getGapRow(); this.gapColumn = format.getGapColumn(); // tell the superclass who to delegate methods to super.delegateLayout = formLayout; } /** * Set the number of columns the layout wraps over. If set, the layout will * wrap from left to right, adding rows as necessary. */ public void setElementColumns(int elementColumns) { if(elementColumns == this.elementColumns) return; setElementRowsAndColumns(Integer.MAX_VALUE, elementColumns); } /** * Set the number of rows the layout wraps over. If set, the layout will * wrap from top to bottom, adding rows as necessary. */ public void setElementRows(int elementRows) { if(elementRows == this.elementRows) return; setElementRowsAndColumns(elementRows, Integer.MAX_VALUE); } /** * Adjust the number of logical rows and columns and how elements are wrapped * into those cells. */ private void setElementRowsAndColumns(int elementRows, int elementColumns) { int logicalRowCountBefore = logicalRowCount(); int logicalColumnCountBefore = logicalColumnCount(); this.elementRows = elementRows; this.elementColumns = elementColumns; fixCellCount(logicalRowCountBefore, logicalColumnCountBefore); } /** * We can improve this method by replacing the before counts as parameters * by figuring out what the counts are by examining the layout. */ private void fixCellCount(int logicalRowCountBefore, int logicalColumnCountBefore) { int logicalColumnCountAfter = logicalColumnCount(); int logicalRowCountAfter = logicalRowCount(); // make sure we have enough cells for(int r = logicalRowCountBefore; r < logicalRowCountAfter; r++) { insertLogicalRow(r); } for(int c = logicalColumnCountBefore; c < logicalColumnCountAfter; c++) { insertLogicalColumn(c); } // we have too many cells in one dimension, the perfect time to // reassign constraints safely reassignConstraints(); // make sure we don't have too many cells for(int r = logicalRowCountBefore - 1; r >= logicalRowCountAfter; r--) { removeLogicalRow(r); } for(int c = logicalColumnCountBefore - 1; c >= logicalColumnCountAfter; c--) { removeLogicalColumn(c); } } private CellConstraints deriveCellConstraints(int component, int logicalColumn, int logicalRow) { CellConstraints constraints = format.getConstraints(component); constraints = (CellConstraints)constraints.clone(); constraints.gridX += logicalToLayoutColumn(logicalColumn) + (logicalColumn > 0 && gapColumn != null ? 1 : 0); constraints.gridY += logicalToLayoutRow(logicalRow) + (logicalRow > 0 && gapRow != null ? 1 : 0); return constraints; } /** {@inheritDoc} */ @Override public void addLayoutComponent(Component component, Object constraints) { Constraints listLayoutConstraints = (Constraints)constraints; int index = listLayoutConstraints.getIndex(); int formConstraints = listLayoutConstraints.getFormConstraints(); CellComponents cellComponents = gridComponents.get(index); cellComponents.components.add(component); cellComponents.constraints.add(listLayoutConstraints); CellConstraints cellConstraints = deriveCellConstraints(formConstraints, logicalColumn(index), logicalRow(index)); super.addLayoutComponent(component, cellConstraints); } private int logicalColumn(int index) { if(elementRows == Integer.MAX_VALUE) { return index % elementColumns; } else { return index / elementRows; } } private int logicalRow(int index) { if(elementRows == Integer.MAX_VALUE) { return index / elementColumns; } else { return index % elementRows; } } private int logicalRowCount() { if(elementRows == Integer.MAX_VALUE) { return (gridComponents.size() + elementColumns - 1) / elementColumns; } else { return Math.min(gridComponents.size(), elementRows); } } private int logicalColumnCount() { if(elementRows == Integer.MAX_VALUE) { return Math.min(gridComponents.size(), elementColumns); } else { return (gridComponents.size() + elementRows - 1) / elementRows; } } private void insertLogicalRow(int index) { int baseRow = logicalToLayoutRow(index); // insert the gap for all rows but the first if(index != 0 && gapRow != null) { insertRow(baseRow, gapRow); baseRow += 1; } // insert the element rows for(int r = 0; r < rowSpecs.length; r++) { insertRow(baseRow + r, rowSpecs[r]); } } private void insertLogicalColumn(int index) { int baseColumn = logicalToLayoutColumn(index); // insert the gap for all columns but the first if(index != 0 && gapColumn != null) { insertColumn(baseColumn, gapColumn); baseColumn += 1; } // insert the element columns for(int c = 0; c < columnSpecs.length; c++) { insertColumn(baseColumn + c, columnSpecs[c]); } } private void removeLogicalRow(int index) { int baseRow = logicalToLayoutRow(index); if(index != 0 && gapRow != null) { formLayout.removeRow(baseRow + 1); } for(int r = 0; r < rowSpecs.length; r++) { formLayout.removeRow(baseRow + 1); } } private void removeLogicalColumn(int index) { int baseColumn = logicalToLayoutColumn(index); if(index != 0 && gapColumn != null) { formLayout.removeColumn(baseColumn + 1); } for(int c = 0; c < columnSpecs.length; c++) { formLayout.removeColumn(baseColumn + 1); } } /** * @return the row that the preceding gap for this row should be shown on, * or the row that the element rows should be on if this is the first row. */ private int logicalToLayoutRow(int row) { if(row == 0) return 0; else return (row * rowSpecs.length) + (gapRow == null ? 0 : row - 1); } /** * @return the column that the preceding gap for this column should be shown on, * or the column that the element columns should be on if this is the first column. */ private int logicalToLayoutColumn(int column) { if(column == 0) return 0; else return (column * columnSpecs.length) + (gapColumn == null ? 0 : column - 1); } /** * Handle a list element being inserted by adjusting the layout. */ public void insertIndex(int index) { int logicalColumnCountBefore = logicalColumnCount(); int logicalRowCountBefore = logicalRowCount(); gridComponents.add(index, new CellComponents()); fixCellCount(logicalRowCountBefore, logicalColumnCountBefore); } /** * Handle a list element being removed by adjusting the layout. */ public void removeIndex(int index) { int logicalColumnCountBefore = logicalColumnCount(); int logicalRowCountBefore = logicalRowCount(); gridComponents.remove(index); fixCellCount(logicalRowCountBefore, logicalColumnCountBefore); } /** * Handle a list element being updated by adjusting the layout. */ public void updateIndex(int index) { // remove all cached component constraints that aren't in the layout anymore CellComponents cellComponents = gridComponents.get(index); List activeComponents = Arrays.asList(container.getComponents()); for(int i = 0; i < cellComponents.components.size(); ) { Component component = cellComponents.components.get(i); if(!activeComponents.contains(component)) { cellComponents.components.remove(i); cellComponents.constraints.remove(i); } else { i++; } } } private void reassignConstraints() { formLayout.invalidateLayout(container); for(int i = 0; i < gridComponents.size(); i++) { CellComponents cellComponents = gridComponents.get(i); for(int j = 0; j < cellComponents.components.size(); j++) { Component component = cellComponents.components.get(j); Constraints constraints = cellComponents.constraints.get(j); formLayout.addLayoutComponent(component, deriveCellConstraints(constraints.getFormConstraints(), logicalColumn(i), logicalRow(i))); } } } /** * Count the cells in the grid. */ public int size() { return gridComponents.size(); } /** * Insert the specified row into the layout. This accomodates * for the appendColumn/insertColumn API weakness in FormLayout. */ private void insertRow(int index, RowSpec rowSpec) { if(index == formLayout.getRowCount()) formLayout.appendRow(rowSpec); else formLayout.insertRow(index + 1, rowSpec); } /** * Insert the specified column into the layout. This accomodates * for the appendRow/insertRow API weakness in FormLayout. */ private void insertColumn(int index, ColumnSpec columnSpec) { if(index == formLayout.getColumnCount()) formLayout.appendColumn(columnSpec); else formLayout.insertColumn(index + 1, columnSpec); } /** * The components for a single cell. */ private static class CellComponents { private List components = new ArrayList(1); private List constraints = new ArrayList(1); } /** * Map a {@link Component} to a list element and a location in the container. */ public static class Constraints { private int formConstraints; private int index; public Constraints(int component, int index) { this.formConstraints = component; this.index = index; } /** * The form constraints specifies which {@link CellConstraints} * in the element cell belongs to this {@link Component}. */ public int getFormConstraints() { return formConstraints; } /** * The initial index of the element, it will become stale as other * elements are added before it. It's only used the first time a * component is used to find the appropriate {@link CellComponents} * object. */ public int getIndex() { return index; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy