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

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

/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.swing;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

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

/**
 * A panel that shows the contents of an EventList containing JComponents.
 *
 * 

* * * * * * *
Extension: JGoodies Forms
This Glazed Lists extension requires the third party library JGoodies Forms.
Tested Version:1.0.5
Home page:http://www.jgoodies.com/freeware/forms/
License:BSD
* *

To use {@link JEventListPanel}: *

    *
  1. Create an {@link EventList} of {@link JComponent}s, or any * objects that reference a set of {@link JComponent}s. *
  2. Implement {@link JEventListPanel.Format} for the object's in your * {@link EventList}. This interface defines how a single cell is layed out. * Once the layout for a single cell is known, all cells can be tiled to * show all of the {@link JComponent}s in your {@link EventList}. *
  3. Create an {@link JEventListPanel} and add it in your application somewhere. * If the number of elements in the {@link EventList} may grow unbounded, * wrap your {@link JEventListPanel} in a {@link JScrollPane}. *
* * @author Jesse Wilson */ public final class JEventListPanel extends JPanel { /** the source contains all the JComponents */ private TransformedList swingSource; /** the components of the panel */ private List components = new ArrayList(); /** the layout supports a forms layout under the hood */ private final ListLayout listLayout; /** the format specifies what an element cell looks like */ private final Format format; /** handle changes to the source {@link EventList} */ private final SourceChangeHandler sourceChangeHandler = new SourceChangeHandler(); /** * Creates a new {@link JEventListPanel} hosting the * {@link JComponent}s from the specified source {@link EventList}. */ public JEventListPanel(EventList source, Format format) { this.swingSource = GlazedListsSwing.swingThreadProxyList(source); this.listLayout = new ListLayout(this, format); this.format = format; this.setLayout(listLayout); // populate the initial elements for(int i = 0; i < swingSource.size(); i++) { sourceChangeHandler.insert(i); } // listen for changes to the source swingSource.addListEventListener(sourceChangeHandler); } /** * Limit the number of list elements will be layed out along the X axis. * This is the number of logical columns, which may differ from the layout * columns used within each cell. * *

Note that when the columns are limited, rows are automatically unlimited. * * @param elementColumns the number of logical columns, between 1 and * {@link Integer#MAX_VALUE}. */ public void setElementColumns(int elementColumns) { if(elementColumns < 1) throw new IllegalArgumentException("elementColumns must be in the range [1, " + Integer.MAX_VALUE + "]"); listLayout.setElementColumns(elementColumns); } /** * Limit the number of list elements will be layed out along the Y axis. * This is the number of logical rows, which may differ from the layout * rows used within each cell. * *

Note that when the rows are limited, columns are automatically unlimited. * * @param elementRows the number of logical rows, between 1 and * {@link Integer#MAX_VALUE}. */ public void setElementRows(int elementRows) { if(elementRows < 1) throw new IllegalArgumentException("elementRows must be in the range [1, " + Integer.MAX_VALUE + "]"); listLayout.setElementRows(elementRows); } /** * Provide the binding between this panel and the source {@link EventList}. */ private class SourceChangeHandler implements ListEventListener { /** * Handle an inserted element. */ private void insert(int index) { // save the components E element = swingSource.get(index); JComponent[] elementComponents = new JComponent[format.getComponentsPerElement()]; for(int c = 0; c < elementComponents.length; c++) { elementComponents[c] = format.getComponent(element, c); } // remember these components components.add(index, elementComponents); listLayout.insertIndex(index); // insert the components for(int c = 0; c < elementComponents.length; c++) { if(elementComponents[c] == null) continue; add(elementComponents[c], new ListLayout.Constraints(c, index)); } } /** * Handle a deleted element. */ private void delete(int index) { // remove the components JComponent[] elementComponents = components.get(index); for(int c = 0; c < elementComponents.length; c++) { if(elementComponents[c] == null) continue; remove(elementComponents[c]); } // forget the components components.remove(index); // collapse the row from the layout listLayout.removeIndex(index); } /** * Handle an updated element. */ private void update(int index) { // get the old components JComponent[] oldElementComponents = components.get(index); // get the new components E element = swingSource.get(index); JComponent[] newElementComponents = new JComponent[format.getComponentsPerElement()]; for(int c = 0; c < newElementComponents.length; c++) { newElementComponents[c] = format.getComponent(element, c); } // swap as necessary for(int c = 0; c < oldElementComponents.length; c++) { if(oldElementComponents[c] == newElementComponents[c]) continue; if(oldElementComponents[c] != null) { remove(oldElementComponents[c]); } if(newElementComponents[c] != null) { add(newElementComponents[c], new ListLayout.Constraints(c, index)); } } // save the latest components components.set(index, newElementComponents); // update the row in the layout listLayout.updateIndex(index); } /** * When the components list changes, this updates the panel. */ @Override public void listChanged(ListEvent listChanges) { while(listChanges.next()) { int type = listChanges.getType(); int index = listChanges.getIndex(); if(type == ListEvent.INSERT) { insert(index); } else if(type == ListEvent.DELETE) { delete(index); } else if(type == ListEvent.UPDATE) { update(index); } } // repaint the panel revalidate(); repaint(); } } /** * Releases the resources consumed by this {@link JEventListPanel} so that it * may eventually be garbage collected. * *

Warning: It is an error * to call any method on a {@link JEventListPanel} after it has been disposed. */ public void dispose() { swingSource.removeListEventListener(sourceChangeHandler); swingSource.dispose(); } /** * Specify how the JComponents of an Object are layed out in a row. */ public interface Format { /** * Get the number of components for each row element. */ int getComponentsPerElement(); /** * Get the component from the specified list element. */ JComponent getComponent(E element, int component); /** * Get the constraints to lay out the specified component. * * @param component the component to fetch constraints for. * @return constraints that specify how to lay out the component. * The constraints' Y value must be between (baseRow + 1) and * (baseRow + getElementRows().length + 1) inclusive. * The constraints' X value must be between 1 and * (getElementColumns().length + 1) inclusive. */ CellConstraints getConstraints(int component); /** * Get the RowSpecs required for one element in the list. */ RowSpec[] getElementRows(); /** * Get the ColumnSpecs required for one element in the list. */ ColumnSpec[] getElementColumns(); /** * Get the RowSpec to separate two elements. * * @return the RowSpec for the spacer row, or null for * no gap. */ RowSpec getGapRow(); /** * Get the ColumnSpecs to separate two elements. * * @return the ColumnSpec for the spacer column, or null for * no gap. */ ColumnSpec getGapColumn(); } /** * A default implementation of the {@link Format} interface. */ public static abstract class AbstractFormat implements Format { private RowSpec[] elementRows; private ColumnSpec[] elementColumns; private RowSpec gapRow; private ColumnSpec gapColumn; private CellConstraints[] cellConstraints; /** * Construct a format using the specifications and constraints specified. */ protected AbstractFormat(RowSpec[] rowSpecs, ColumnSpec[] columnSpecs, RowSpec gapRow, ColumnSpec gapColumn, CellConstraints[] cellConstraints) { this.elementRows = rowSpecs; this.elementColumns = columnSpecs; this.gapRow = gapRow; this.gapColumn = gapColumn; this.cellConstraints = cellConstraints; } /** * Construct a format using the specifications and constraints specified. */ protected AbstractFormat(String rowSpecs, String columnSpecs, String gapRow, String gapColumn, CellConstraints[] cellConstraints) { this(RowSpec.decodeSpecs(rowSpecs), ColumnSpec.decodeSpecs(columnSpecs), new RowSpec(gapRow), new ColumnSpec(gapColumn), cellConstraints); } /** * Construct a format using the specifications and constraints specified. */ protected AbstractFormat(String rowSpecs, String columnSpecs, String gapRow, String gapColumn, String[] cellConstraints) { this(RowSpec.decodeSpecs(rowSpecs), ColumnSpec.decodeSpecs(columnSpecs), gapRow == null ? null : new RowSpec(gapRow), gapColumn == null ? null : new ColumnSpec(gapColumn), decode(cellConstraints)); } /** * Construct a bare format. Extending classes must populate all specs * or override the corresponding getters so that all appropriate * specifications are available. */ protected AbstractFormat() { // do nothing } /** {@inheritDoc} */ @Override public RowSpec[] getElementRows() { return elementRows; } /** * Set the rows used for a single element cell. */ public void setElementRows(RowSpec[] elementRows) { this.elementRows = elementRows; } /** {@inheritDoc} */ @Override public ColumnSpec[] getElementColumns() { return elementColumns; } /** * Set the columns used for a single element cell. */ public void setElementColumns(ColumnSpec[] elementColumns) { this.elementColumns = elementColumns; } /** * Set the rows and columns used for a single element cell. */ public void setElementCells(String rowSpecs, String columnSpecs) { this.setElementRows(RowSpec.decodeSpecs(rowSpecs)); this.setElementColumns(ColumnSpec.decodeSpecs(columnSpecs)); } /** {@inheritDoc} */ @Override public int getComponentsPerElement() { return cellConstraints.length; } /** {@inheritDoc} */ @Override public CellConstraints getConstraints(int component) { return cellConstraints[component]; } /** * Set the constraints used for the components of each element. */ public void setCellConstraints(CellConstraints[] cellConstraints) { this.cellConstraints = cellConstraints; } /** * Set the constraints used for the components of each element. */ public void setCellConstraints(String[] cellConstraints) { setCellConstraints(decode(cellConstraints)); } /** {@inheritDoc} */ @Override public RowSpec getGapRow() { return gapRow; } /** {@inheritDoc} */ @Override public ColumnSpec getGapColumn() { return gapColumn; } /** * Set the height of the vertical gap between element cells. */ public void setGapRow(RowSpec gapRow) { this.gapRow = gapRow; } /** * Set the width of the horizontal gap between element cells. */ public void setGapColumn(ColumnSpec gapColumn) { this.gapColumn = gapColumn; } /** * Set the size of the gaps between element cells. */ public void setGaps(String gapRow, String gapColumn) { this.setGapRow(gapRow != null ? new RowSpec(gapRow) : null); this.setGapColumn(gapColumn != null ? new ColumnSpec(gapColumn) : null); } /** * Decode the specified Strings into cell constraints. */ private static CellConstraints[] decode(String[] cellConstraints) { CellConstraints[] decoded = new CellConstraints[cellConstraints.length]; for(int c = 0; c < cellConstraints.length; c++) { decoded[c] = new CellConstraints(cellConstraints[c]); } return decoded; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy