/*
* $Id: MultiListExplorer.java,v 1.2 2006/09/25 08:52:36 acaproni Exp $
*
* $Date: 2006/09/25 08:52:36 $
* $Revision: 1.2 $
* $Author: acaproni $
*
* Copyright CERN, All Rights Reserved.
*/
package cern.gp.explorer;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerPanel;
import org.openide.explorer.view.ListView;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import cern.gp.nodes.GPNode;
import cern.gp.util.GPManager;
/**
* MultiListExplorer is an explorer showing several lists linked together allowing to show
* several levels of depth at once. The first list shows the children of the root node, the
* second list shows the children of the selected node in the first list, and so on.
* <p>
* The number of lists is parameterizable.
* </p><p>
* It is possible to register a <code>java.beans.PropertyChangeListener</code> to be notified
* of the selection in the different list. The method <code>getExplorerManager(int)</code>
* allow to register such a listener on one particular list.
* </p><p>
* It is possible to customize the top and bottom component of each list by setting a
* <code>ComponentCreator</code> for top or bottom before adding the explorer to the gui.
* </p>
*
* @version $Revision: 1.2 $ $Date: 2006/09/25 08:52:36 $
* @author Lionel Mestre
*/
public class MultiListExplorer extends ExplorerPanel {
public static final int DEFAULT_WIDTH = 600;
public static final int DEFAULT_HEIGHT = 400;
public static final int DEFAULT_LIST_COUNT = 4;
private ExplorerPanel[] _explorerPanels;
private int _listCount = DEFAULT_LIST_COUNT;
private boolean _twoRows;
private boolean _automaticSelection = true;
private ComponentCreator _bottomComponentCreator;
private ComponentCreator _topComponentCreator;
private SelectionListener _selectionListener;
//
// -- CONSTRUCTORS -----------------------------------------------
//
public MultiListExplorer() {
this(null, DEFAULT_LIST_COUNT, DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
}
public MultiListExplorer(SelectionListener selectionListener, int listCount) {
this(selectionListener, listCount, DEFAULT_WIDTH, DEFAULT_HEIGHT, false);
}
public MultiListExplorer(SelectionListener selectionListener, int width, int height) {
this(selectionListener, DEFAULT_LIST_COUNT, width, height, false);
}
public MultiListExplorer(SelectionListener selectionListener, int listCount, int width, int height, boolean twoRows) {
this._listCount = listCount;
this._twoRows = twoRows;
if (twoRows && listCount % 2 != 0) throw new IllegalArgumentException("listCount must be a multiple of 2 to be on two rows");
this._selectionListener = selectionListener;
setLayout(new java.awt.GridLayout(1, 1));
setSize(width, height);
setPreferredSize(new java.awt.Dimension(width, height));
// make the explorer non-persistent so that it does not leave bad .wstcref files in system dir
putClientProperty("PersistenceType", "Never");
}
//
// -- PUBLIC STATIC METHODS -----------------------------------------------
//
//
// -- PUBLIC METHODS -----------------------------------------------
//
public void addNotify() {
super.addNotify();
_explorerPanels = new ExplorerPanel[_listCount];
javax.swing.JComponent explorerComponent = null;
if (_twoRows) {
explorerComponent = createExplorerPanelRows(getWidth(), getHeight(), _listCount);
} else {
explorerComponent = createExplorerPanelRow(getWidth(), getHeight(), _listCount, 0, null);
}
add(explorerComponent, java.awt.BorderLayout.CENTER);
if (_automaticSelection) {
selectNthChildren(getExplorerManager(), 0);
}
}
public void removeNotify() {
_explorerPanels = null;
super.removeNotify();
}
/**
* set the root node of the hiearachy to be explored
*/
public void setRootNode(GPNode node) {
getExplorerManager().setRootContext(node.getPeerNode());
}
//
// -- Getters / Setters -----------------------------------------------
//
/**
* Returns the topComponentCreator that is used to create the top component of
* each list in the explorer or null if none is set.
* @return the current topComponentCreator or null
*/
public ComponentCreator getTopComponentCreator() {
return _topComponentCreator;
}
/**
* Sets the topComponentCreator that is used to create the top component of
* each list in the explorer. Null can be used to remove an existing creator.
* The topComponentCreator cannot be changed once the explorer has been included
* in a gui. It can only be changed after instantiation before adding the Explorer
* into a container.
* @param componentCreator the componentCreator to use to create the top components
*/
public void setTopComponentCreator(ComponentCreator componentCreator) {
this._topComponentCreator = componentCreator;
}
/**
* Returns the bottomComponentCreator that is used to create the top component of
* each list in the explorer or null if none is set.
* @return the current bottomComponentCreator or null
*/
public ComponentCreator getBottomComponentCreator() {
return _bottomComponentCreator;
}
/**
* Sets the bottomComponentCreator that is used to create the top component of
* each list in the explorer. Null can be used to remove an existing creator.
* The bottomComponentCreator cannot be changed once the explorer has been included
* in a gui. It can only be changed after instantiation before adding the Explorer
* into a container.
* @param componentCreator the componentCreator to use to create the bottom components
*/
public void setBottomComponentCreator(ComponentCreator componentCreator) {
this._bottomComponentCreator = componentCreator;
}
/**
* Sets the number of lists in the explorer. Cannot be inferior to 2.
* The number cannot be changed once the explorer has been included in a gui.
* The number can only be changed after instantiation before adding the Explorer
* into a container.
* @param listCount the number of ilst in the explorer
*/
public void setListCount(int listCount) {
if (_explorerPanels != null)
throw new IllegalStateException("addNotify already performed");
if (listCount < 2)
throw new IllegalArgumentException("The number of list in the Explorer cannot be < 2");
this._listCount = listCount;
}
/**
* Returns the number of lists in the explorer.
* @return the number of lists in the explorer.
*/
public int getListCount() {
return _listCount;
}
/**
* Sets if the selection in one list triggers the selection in the subsequent lists.
* This property is true by default.
* @param automaticSelection whether the selection in one list cascade to the others
*/
public void setAutomaticSelection(boolean automaticSelection) {
this._automaticSelection = automaticSelection;
}
/**
* Returns if the selection in one list triggers the selection in the subsequent lists.
* @return if the selection in one list triggers the selection in the subsequent lists.
*/
public boolean getAutomaticSelection() {
return _automaticSelection;
}
//
// -- PROTECTED METHODS -----------------------------------------------
//
/**
* Returns the explorer manager of the list of given index
* @return the explorer manager of the list of given index
*/
protected ExplorerManager getExplorerManager(int listIndex) {
if (_explorerPanels == null)
throw new IllegalStateException("addNotify not yet performed");
return _explorerPanels[listIndex].getExplorerManager();
}
protected static void selectNthChildren(ExplorerManager explorerManager, int n) {
Node exploredNode = explorerManager.getExploredContext();
if (exploredNode == null)
return;
Children children = exploredNode.getChildren();
try {
if (children == null || children.getNodesCount() == 0) {
explorerManager.setSelectedNodes(new Node[0]);
} else {
if (n >= children.getNodesCount()) {
explorerManager.setSelectedNodes(new Node[] { children.getNodes()[0] });
} else {
explorerManager.setSelectedNodes(new Node[] { children.getNodes()[n] });
}
}
} catch (java.beans.PropertyVetoException e) {
GPManager.notify(GPManager.WARNING, e);
}
}
//
// -- PRIVATE METHODS -----------------------------------------------
//
/**
* Creates all ExplorerPanels (one per list) making the MultiListExplorer.
* This method is recursive and creates list from right to left
* @param width the remaining available width in pixels
* @param height the available height in pixels
* @param listCount the counter giving how many list remain to be created
* @param prevRightPanel the possibly null previous right panel that was created on the previous call
* @return the new component containing all created explorers
*/
private javax.swing.JComponent createExplorerPanelRows(int width, int height, int listCount) {
// create right Panel
javax.swing.JComponent bottomComponent = createExplorerPanelRow(width, height / 2, listCount / 2, listCount / 2, null);
javax.swing.JComponent topComponent = createExplorerPanelRow(width, height / 2, listCount / 2, 0, _explorerPanels[listCount / 2]);
javax.swing.JSplitPane splitPane =
new javax.swing.JSplitPane(javax.swing.JSplitPane.VERTICAL_SPLIT, topComponent, bottomComponent);
splitPane.setPreferredSize(new java.awt.Dimension(width, height));
splitPane.setDividerLocation(height / 2);
splitPane.setResizeWeight(0.5);
splitPane.setOneTouchExpandable(false);
splitPane.setContinuousLayout(false);
splitPane.setDividerSize(3);
return splitPane;
}
/**
* Creates all ExplorerPanels (one per list) making the MultiListExplorer.
* This method is recursive and creates list from right to left
* @param width the remaining available width in pixels
* @param height the available height in pixels
* @param listCount the counter giving how many list remain to be created
* @param prevRightPanel the possibly null previous right panel that was created on the previous call
* @return the new component containing all created explorers
*/
private javax.swing.JComponent createExplorerPanelRow(
int width,
int height,
int listCount,
int preceedingExplorerCount,
ExplorerPanel prevRightPanel) {
// create right Panel
ExplorerPanel rightPanel = createExplorerPanel(preceedingExplorerCount + listCount - 1, prevRightPanel);
// create left Panel
double weight = (listCount - 1) / (double) listCount;
int panelWidth = (int) Math.round(weight * width);
javax.swing.JComponent leftComponent;
if (listCount > 2) {
leftComponent = createExplorerPanelRow(panelWidth, height, listCount - 1, preceedingExplorerCount, rightPanel);
} else {
leftComponent = createExplorerPanel(preceedingExplorerCount + listCount - 2, rightPanel);
}
javax.swing.JSplitPane splitPane =
new javax.swing.JSplitPane(javax.swing.JSplitPane.HORIZONTAL_SPLIT, leftComponent, rightPanel);
splitPane.setPreferredSize(new java.awt.Dimension(width, height));
splitPane.setDividerLocation(panelWidth);
splitPane.setResizeWeight(weight);
splitPane.setOneTouchExpandable(false);
splitPane.setContinuousLayout(false);
splitPane.setDividerSize(3);
return splitPane;
}
/**
* Creates one ExplorerPanel representing one list.
* @param listIndex the index of the list to create (from 0 to n-1)
* @param prevRightPanel the possibly null previous right panel displaying the content
* of the node selected in this list.
* @return the new ExplorerPanel
*/
private ExplorerPanel createExplorerPanel(
int listIndex,
ExplorerPanel prevRightPanel) {
if (listIndex > 0) {
_explorerPanels[listIndex] = new ListExplorerPanel(listIndex, prevRightPanel);
} else {
_explorerPanels[listIndex] = new ListExplorerPanel(getExplorerManager(), listIndex, prevRightPanel);
}
return _explorerPanels[listIndex];
}
//
// -- INNER CLASSES -----------------------------------------------
//
public interface SelectionListener {
/**
* Notifies that the selection changed in the list number listIndex. The
* new selection is given by nodes.
* @param listIndex the index of the list to create (from 0 to n-1)
* @param nodes the array representing the new selection
*/
public void selectionChanged(int listIndex, Node[] nodes);
}
public interface ComponentCreator {
/**
* Creates s component to be linked with one list of the explorer panel.
* The component can be position on the top or on the bottom of the list
* @param listIndex the index of the list to create (from 0 to n-1)
* @return the new component or null if no component should be used
*/
public javax.swing.JComponent createComponent(int listIndex);
}
private class NextListSelectionListener implements java.beans.PropertyChangeListener {
private ExplorerManager sourceManager;
private ExplorerManager targetManager;
private String name;
private boolean shouldUpdateSelection;
public NextListSelectionListener(ExplorerPanel source, ExplorerPanel target) {
this.sourceManager = source.getExplorerManager();
this.targetManager = target.getExplorerManager();
this.name = source.getName();
}
public void propertyChange(java.beans.PropertyChangeEvent e) {
String propName = e.getPropertyName();
if (propName.equals(ExplorerManager.PROP_SELECTED_NODES)) {
//
// selected node changed
//
updateTargetPanel();
if (shouldUpdateSelection) {
shouldUpdateSelection = false;
selectNthChildren(sourceManager, 0);
}
} else if (propName.equals(ExplorerManager.PROP_EXPLORED_CONTEXT)) {
//
// explored node changed
//
Object old = e.getOldValue();
if (old != null && (old instanceof Node)) {
Node oldNode = (Node) old;
Node currentNode = sourceManager.getExploredContext();
//log(name+" OLD context ="+oldNode);
//log(name+" NEW context ="+currentNode);
if (!oldNode.equals(currentNode)) {
shouldUpdateSelection = _automaticSelection;
}
}
} else if (propName.equals(ExplorerManager.PROP_NODE_CHANGE)) {
} else if (propName.equals(ExplorerManager.PROP_ROOT_CONTEXT)) {
}
}
private void updateTargetPanel() {
Node[] selectedNodes = sourceManager.getSelectedNodes();
if ((selectedNodes == null) || (selectedNodes.length == 0)) {
targetManager.setRootContext(Node.EMPTY);
return;
}
Node selectedNode = selectedNodes[0];
targetManager.setRootContext(selectedNode);
if (_automaticSelection) {
selectNthChildren(targetManager, 0);
}
}
} // end inner class NextListSelectionListener
private class ListExplorerPanel extends ExplorerPanel {
private java.beans.PropertyChangeListener nextListSelectionListener;
private java.beans.PropertyChangeListener listSelectionListener;
//
// -- CONSTRUCTORS -----------------------------------------------
//
/** Creates a new instance of ListPanel */
public ListExplorerPanel(int index, ExplorerPanel nextExplorer) {
initialize(index, nextExplorer);
}
/** Creates a new instance of ListPanel */
public ListExplorerPanel(ExplorerManager explorerManager, int index, ExplorerPanel nextExplorer) {
super(explorerManager);
initialize(index, nextExplorer);
}
private void initialize(int index, ExplorerPanel nextExplorer) {
setName("List" + index);
ListView view = new ListView();
view.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
view.setPopupAllowed(true);
add(view, java.awt.BorderLayout.CENTER);
if (_topComponentCreator != null) {
javax.swing.JComponent top = _topComponentCreator.createComponent(index);
if (top != null) {
add(top, java.awt.BorderLayout.NORTH);
}
}
if (_bottomComponentCreator != null) {
javax.swing.JComponent bottom = _bottomComponentCreator.createComponent(index);
if (bottom != null) {
add(bottom, java.awt.BorderLayout.SOUTH);
}
}
if (nextExplorer != null) {
nextListSelectionListener = new NextListSelectionListener(this, nextExplorer);
this.getExplorerManager().addPropertyChangeListener(nextListSelectionListener);
}
if (_selectionListener != null) {
listSelectionListener = new ListSelectionListener(index);
this.getExplorerManager().addPropertyChangeListener(listSelectionListener);
}
}
//
// -- PUBLIC METHODS -----------------------------------------------
//
public void addNotify() {
super.addNotify();
}
public void removeNotify() {
if (nextListSelectionListener != null)
this.getExplorerManager().removePropertyChangeListener(nextListSelectionListener);
if (listSelectionListener != null)
this.getExplorerManager().removePropertyChangeListener(listSelectionListener);
super.removeNotify();
}
//
// -- PROTECTED METHODS -----------------------------------------------
//
} // end inner class ListExplorerPanel
private class ListSelectionListener implements java.beans.PropertyChangeListener {
private int index;
public ListSelectionListener(int index) {
this.index = index;
}
public void propertyChange(java.beans.PropertyChangeEvent e) {
String propName = e.getPropertyName();
if (propName.equals(ExplorerManager.PROP_SELECTED_NODES)) {
Node[] nodes = (Node[]) e.getNewValue();
_selectionListener.selectionChanged(index, nodes);
}
}
} // end inner class ListSelectionListener
}