package com.onaro.util.jfc.collapsible; import javax.swing.table.AbstractTableModel; import javax.swing.*; import java.util.List; import java.util.LinkedList; import java.awt.*; /** * The {@link CollapsiblePane} is using this model to layout the components and to * provide the animation while collapsing/expandign. The layout is achieved by having * each component kept as a cell in a single column table. The animation effect is * performed by gradually removing/adding components to the model. */ public class CollapsibleTableModel extends AbstractTableModel { private static final long serialVersionUID = 1L; /** * The list of components currently visible. These components are accessible via * the {@link javax.swing.table.TableModel} interface. */ private List visibleComponents = new LinkedList(); /** * The list of components that are currently invisible. */ private List hiddenComponents = new LinkedList(); /** * The current state of the model. During the transition from expand to collapse and the opposite, * the value will be the final value. Therefore, it'll be true when the model is expanding or * completely expanded. */ private boolean expanded = false; /** * Gets the number of visible components. * @return the number of visible components (0 when collapsed) */ public int getRowCount() { return visibleComponents.size(); } /** * This model is using a single column. * @return 1 */ public int getColumnCount() { return 1; } /** * Gets the component at the given row (the column is always 1). * @param rowIndex the requested row * @param columnIndex always 1 * @return the component at the requested row */ public Object getValueAt(int rowIndex, int columnIndex) { if (rowIndex >= 0 && rowIndex < visibleComponents.size()) return visibleComponents.get(rowIndex); else return null; } /** * Tells the current state of the model. * @return true if is expanded or in the process of expanding */ public boolean isExpanded() { return expanded; } /** * Sets the desired expand/collapse state. Triggers the transition in which components are * gradually are hidden/displayed. * @param expanded true if the model should expand */ public void setExpanded(boolean expanded) { if (this.expanded != expanded) { this.expanded = expanded; if (expanded) expandStep(); else collapseStep(); } } /** * Tells if the pane is in process of expanding and is not fully expanded. * @return true if the pane is expanded but some items aren't visible yet. */ public boolean isExpanding() { return expanded && hiddenComponents.size() > 0; } /** * Tells if the pane is in process of collapsing and is not fully collapsed. * @return true if the pane is collapsed but some items are still visible. */ public boolean isCollapsing() { return !expanded && visibleComponents.size() > 0; } /** * In order for the {@link JTable} to forward mouse and keyboard events to a component, the * cells must always be editable. * @param rowIndex * @param columnIndex * @return */ public boolean isCellEditable(int rowIndex, int columnIndex) { return true; } /** * Appends a component to the layout. * @param comp the new component */ public void add(Component comp) { if (expanded) visibleComponents.add(comp); else hiddenComponents.add(comp); } /** * Recursively move components from the {@link CollapsibleTableModel#hiddenComponents} list to * the {@link CollapsibleTableModel#visibleComponents} list. Each step is scheduled to be invoked "later", providing * a short delay in which the table is capable of painting. */ private void expandStep() { if (isExpanding()) { visibleComponents.add(0, hiddenComponents.remove(hiddenComponents.size() - 1)); fireTableRowsInserted(0, 0); SwingUtilities.invokeLater(nextExpandStep); } } /** * A {@link Runnable} that when invoked, sleeps for a while and then issues the * next {@link CollapsibleTableModel#expandStep}. */ private Runnable nextExpandStep = new Runnable() { public void run() { try { Thread.sleep(40); } catch (InterruptedException e) { } expandStep(); } }; /** * Recursively move components from the {@link CollapsibleTableModel#visibleComponents} list to * the {@link CollapsibleTableModel#hiddenComponents} list. Each step is scheduled to be invoked "later", providing * a short delay in which the table is capable of painting. */ private void collapseStep() { if (isCollapsing()) { hiddenComponents.add(visibleComponents.remove(0)); fireTableRowsDeleted(0, 0); SwingUtilities.invokeLater(nextCollapseStep); } } /** * A {@link Runnable} that when invoked, sleeps for a while and then issues the * next {@link CollapsibleTableModel#collapseStep}. */ private Runnable nextCollapseStep = new Runnable() { public void run() { try { Thread.sleep(40); } catch (InterruptedException e) { } collapseStep(); } }; }