/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.tools.gui.slideabletree; import org.apache.log4j.Logger; import org.jdesktop.swingx.JXCollapsiblePane; import org.jdesktop.swingx.JXTaskPaneContainer; import org.jdesktop.swingx.VerticalLayout; import org.openide.util.Exceptions; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.EventQueue; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Enumeration; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.JViewport; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeWillExpandListener; import javax.swing.plaf.TreeUI; import javax.swing.text.Position.Bias; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import de.cismet.tools.gui.StaticSwingTools; /** * DOCUMENT ME! * * <p>SlideableTree visualize the children of the trees root node with an JXTaskpane. The TaskPanes contains a * SlideablesubTree, which represents the tree under this child.</p> * * <p>To use this specialized View, you have to use the constructor with an boolean flag e.g</p> * * <pre> {@Code * final JTree t = new SlideableTree(true); * } * </pre> * * @author dmeiers * @version $Revision$, $Date$ */ public class SlideableTree extends JTree implements TreeExpansionListener, TreeSelectionListener, TreeWillExpandListener { //~ Instance fields -------------------------------------------------------- protected boolean useSlideableTreeView = false; private final Logger logger; private JXTaskPaneContainer container; private ArrayList<SlideableSubTree> trees; private ArrayList<SubTreePane> panes; private JScrollPane containerScrollPane; private JPanel panel; //~ Constructors ----------------------------------------------------------- /** * Creates a new SlideableTree object. */ public SlideableTree() { this(false); } /** * Creates a new SlideableTree object. * * @param useSlideableView a flag for using a specialized view with JXTaskPanes */ public SlideableTree(final boolean useSlideableView) { this.useSlideableTreeView = useSlideableView; logger = Logger.getLogger(getClass()); if (useSlideableTreeView) { trees = new ArrayList<SlideableSubTree>(); panes = new ArrayList<SubTreePane>(); container = new JXTaskPaneContainer(); /* * Je nach LookAndFeel wird der BackgroundPainter benutzt um den Hintergrund eines TaskPanecontainers zu * zeichen. In diesem Fall funktioniert setBackground nicht mehr... */ container.setBackgroundPainter(null); // change titlebar background of JXTaskPanes UIManager.getDefaults().put("TaskPane.titleBackgroundGradientStart", new Color(222, 222, 222)); UIManager.getDefaults().put("TaskPane.titleBackgroundGradientEnd", new Color(244, 244, 244)); this.setLayout(new BorderLayout()); final VerticalLayout verticalLayout = new VerticalLayout(); verticalLayout.setGap(7); container.setLayout(verticalLayout); container.setBorder(new EmptyBorder(0, 5, 0, 5)); container.setBackground(new Color(255, 255, 255)); containerScrollPane = new JScrollPane(container); // fuer niftyScrollBar StaticSwingTools.setNiftyScrollBars(containerScrollPane); /* * Create for all children of rootNode a new SlideableSubTree and add it to a JXTaskPane */ createSubTrees(this.getModel()); addToTreeContainer(panes); this.setDragEnabled(true); this.setEditable(false); this.add(containerScrollPane, BorderLayout.CENTER); } } /** * Creates a new SlideableTree object. * * @param model DOCUMENT ME! */ public SlideableTree(final TreeModel model) { this(false); this.setModel(model); } /** * Creates a new SlideableTree object. * * @param node DOCUMENT ME! */ public SlideableTree(final TreeNode node) { this(new DefaultTreeModel(node)); } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isUseSlideableTreeView() { return useSlideableTreeView; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public JXTaskPaneContainer getContainer() { return container; } @Override public void addSelectionPath(final TreePath path) { if (useSlideableTreeView) { if (trees != null) { final SlideableSubTree t = getSubTreeForPath(path); final TreePath subTreePath = getPathForSubTree(path); t.addSelectionPath(path); } } else { super.addSelectionPath(path); } } @Override public void addSelectionPaths(final TreePath[] paths) { if (useSlideableTreeView) { if (trees != null) { for (int i = 0; i < paths.length; i++) { addSelectionPath(paths[i]); } } super.addSelectionPaths(paths); } else { super.addSelectionPaths(paths); } } @Override public void addSelectionRow(final int row) { final TreePath path = getPathForRow(row); addSelectionPath(path); super.addSelectionRow(row); } @Override public void addSelectionRows(final int[] rows) { if (useSlideableTreeView) { for (int i = 0; i < rows.length; i++) { addSelectionRow(rows[i]); } super.addSelectionRows(rows); } else { super.addSelectionRows(rows); } } /* * das Intervall wird auf die einzelnen SubTrees umgelenkt. Dabei kann durch index0 und die Anzahl der jeweils * sichtbaren Elemente im Subtree, der Subtree herausgefunden werden, in dem die Selektion beginnt. (index0>=anzahl * sichtbarer Elemente im Subtree) Das Ende der Selektion kann auf gleiche Weise hereausgefunden werden * Dazwischenliegenede Subtrees sind komplett selektiert. */ @Override public void addSelectionInterval(final int index0, final int index1) { if (useSlideableTreeView) { if (index1 < index0) { return; } else { for (int i = index0; i <= index1; i++) { final TreePath path = getPathForRow(i); addSelectionPath(path); } } } super.addSelectionInterval(index0, index1); } @Override public void cancelEditing() { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.cancelEditing(); } } } else { super.cancelEditing(); } } @Override public void clearSelection() { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { panes.get(trees.indexOf(t)).setSelected(false); t.clearSelection(); } } } else { super.clearSelection(); } } @Override protected void clearToggledPaths() { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.clearToggledPaths(); } } } super.clearToggledPaths(); } @Override public void expandRow(final int row) { if (useSlideableTreeView) { if ((row < 0) || (row > getRowCount())) { return; } if ((trees != null) && (panes != null)) { final SlideableSubTree t = getSubTreeForRow(row); final int index = trees.indexOf(t); final SubTreePane pane = panes.get(index); pane.setCollapsed(false); final TreePath subTreePath = getPathForSubTree(getPathForRow(row)); t.expandPath(subTreePath); } } else { super.expandRow(row); } } @Override public Enumeration<TreePath> getExpandedDescendants(final TreePath parent) { if (useSlideableTreeView) { final Vector<TreePath> paths = new Vector<TreePath>(); final Object lastPathElement = parent.getLastPathComponent(); final Object origRoot = this.getModel().getRoot(); if (trees != null) { // falls der durch parent representierte knoten der Rootnode des // original baum ist, alle SubTreeRoots falls das JXTaskPane aufgeklappt zurueckgeben if (lastPathElement.equals(origRoot)) { for (final SlideableSubTree t : trees) { final SubTreePane pane = panes.get(trees.indexOf(t)); if (!(pane.isCollapsed())) { final Object subTreeRoot = t.getModel().getRoot(); paths.add(getPathforOriginalTree(new TreePath(subTreeRoot))); } } } // sonst lediglich fuer den Subtree der den durch parent representierten // knoten enthaelt else { final SlideableSubTree subTree = getSubTreeForPath(parent); final Enumeration<TreePath> newPaths = subTree.getExpandedDescendants(getPathForSubTree(parent)); if (newPaths != null) { while (newPaths.hasMoreElements()) { paths.add(getPathforOriginalTree(newPaths.nextElement())); } } } } return paths.elements(); } else { return super.getExpandedDescendants(parent); } } @Override public TreePath getEditingPath() { if (useSlideableTreeView) { for (final JTree t : trees) { final TreePath path = t.getEditingPath(); if (path != null) { return getPathforOriginalTree(path); } } } return super.getEditingPath(); } @Override public TreePath getPathForLocation(final int x, final int y) { if (useSlideableTreeView) { final TreePath closestPath = getClosestPathForLocation(x, y); // closest Path ist null wenn koordinaten nicht innerhalb des SlideableTree liegen.. if (closestPath == null) { return null; } else { final SubTreePane pane = panes.get(trees.indexOf(getSubTreeForPath(closestPath))); final int paneX = pane.getX(); final int paneY = pane.getY(); final int titleBarHeight = (pane.getHeight() - pane.getContentPane().getHeight()); // Sonderfall closest ist Rootnode eines subTrees if (closestPath.getPathCount() == 2) { if ((y >= paneY) && (y <= (paneY + titleBarHeight))) { return closestPath; } } final int treeX = getSubTreeForPath(closestPath).getX(); final int treeY = getSubTreeForPath(closestPath).getY(); final int newX = x - paneX - treeX; final int newY = y - paneY - treeY - titleBarHeight; final Rectangle r = getPathBounds(closestPath); double recX = 0; double recY = 0; double recWidth = 0; double recHeight = 0; if (r != null) { recX = r.getX(); recY = r.getY(); recWidth = r.getWidth(); recHeight = r.getHeight(); } // liegen Koordinaten innerhalb des closestPath? if ((newX >= recX) && (newX <= (recX + recWidth)) && (newY >= recY) && (newY <= (recY + recHeight))) { return closestPath; } return null; } } else { return super.getPathForLocation(x, y); } } @Override public TreePath getClosestPathForLocation(final int x, final int y) { if (useSlideableTreeView) { if (logger.isDebugEnabled()) { logger.debug("VisibleRect: " + this.getVisibleRect()); logger.debug("Tree X/Y von: " + this.getX() + "/" + this.getY()); logger.debug("Tree X/Y bis: " + this.getX() + this.getWidth() + "/" + this.getY() + this.getHeight()); } final Component c = container.getComponentAt(x, y); if (c instanceof SubTreePane) { final SubTreePane pane = (SubTreePane)c; final SlideableSubTree t = trees.get(panes.indexOf(pane)); final int titleBarHeight = (pane.getHeight() - pane.getContentPane().getHeight()); if (y <= (titleBarHeight + pane.getY())) { // der rootNode des SubTrees ist der closestNode return getPathforOriginalTree(new TreePath(t.getModel().getRoot())); } else if ((y > (titleBarHeight + pane.getY())) && (y < (pane.getY() + titleBarHeight + t.getY()))) { final int distanceToTitle = Math.abs(y - (titleBarHeight + pane.getY())); final int distanceToTree = Math.abs(y - (pane.getY() + titleBarHeight + t.getY())); if (distanceToTitle < distanceToTree) { return getPathforOriginalTree(new TreePath(t.getModel().getRoot())); } } final int newY = y - pane.getY() - t.getY() - titleBarHeight; final int newX = x - pane.getX() - t.getX(); TreePath subTreePath = t.getClosestPathForLocation(x, y); // ist null falls kein Knoten sichtbar ist, z.b wenn der rootNode des // subTree ein blatt ist if (subTreePath == null) { subTreePath = new TreePath(t.getModel().getRoot()); } return getPathforOriginalTree(subTreePath); // y liegt zwischen titlebar und tree, geringsten abstand bestimmen } else if (c instanceof JXTaskPaneContainer) { // falls berechne den nahestehendsten JXTaskpane.. SubTreePane closest = null; boolean lastComponent = false; for (final SubTreePane p : panes) { final int paneY = p.getY(); // liegt y vor p? if (y < paneY) { if (panes.indexOf(p) == 0) { closest = p; lastComponent = false; } else { final int distance = Math.abs(y - paneY); final SubTreePane predecessor = panes.get(panes.indexOf(p) - 1); final int distanceToPredecessor = Math.abs(y - (predecessor.getY() + predecessor.getHeight())); if (distance <= distanceToPredecessor) { closest = p; lastComponent = false; } else { closest = predecessor; lastComponent = true; } } } } if (closest == null) { closest = panes.get(panes.size() - 1); lastComponent = true; } final SlideableSubTree t = trees.get(panes.indexOf(closest)); if (lastComponent) { final int newY = closest.getY() + closest.getHeight(); TreePath subTreePath = t.getClosestPathForLocation(0, newY); if (subTreePath == null) { subTreePath = new TreePath(t.getModel().getRoot()); } return getPathforOriginalTree(subTreePath); } return getPathforOriginalTree(new TreePath(t.getModel().getRoot())); } // falls x/y nicht innerhalb des container liegen return null else { return null; } } else { return super.getClosestPathForLocation(x, y); } } @Override protected Enumeration<TreePath> getDescendantToggledPaths(final TreePath parent) { if (useSlideableTreeView) { final Vector<TreePath> toggledPaths = new Vector<TreePath>(); final Object lastPathComponent = parent.getLastPathComponent(); final Object parentRoot = this.getModel().getRoot(); if (trees != null) { if (lastPathComponent.equals(parentRoot)) { // falls parent gleich dem RootNode des ParentTree ist // was tun ?? } else { final SlideableSubTree t = getSubTreeForPath(parent); final TreePath subTreePath = getPathForSubTree(parent); final Enumeration toggledSubPaths = t.getDescendantToggledPaths(subTreePath); while (toggledSubPaths.hasMoreElements()) { final TreePath originPath = getPathforOriginalTree((TreePath)toggledSubPaths.nextElement()); toggledPaths.add(getPathforOriginalTree(originPath)); } } return toggledPaths.elements(); } } return super.getDescendantToggledPaths(parent); } @Override public int getMaxSelectionRow() { if (useSlideableTreeView) { if (trees != null) { int offset = 0; final int maxSelection = 0; int indexOfTree = 0; /* * letzen Baum mit selektion herausfinden */ for (int i = trees.size() - 1; i >= 0; i--) { final SlideableSubTree t = trees.get(i); if (maxSelection != -1) { indexOfTree = i; break; } } if (maxSelection == -1) { return maxSelection; } else { /* * Offset berechnen */ for (int i = 0; i < indexOfTree; i++) { offset += trees.get(i).getRowCount() + 1; } return maxSelection + offset; } } } return super.getMaxSelectionRow(); } @Override public int getMinSelectionRow() { if (useSlideableTreeView) { if (trees != null) { int offset = 0; int minSelection = 0; final int indexOfTree = 0; for (final SlideableSubTree t : trees) { minSelection = t.getMinSelectionRow(); if (minSelection == -1) { offset += t.getRowCount() + 1; } else { break; } } if (minSelection == -1) { return minSelection; } else { return minSelection + offset; } } } return super.getMinSelectionRow(); } @Override public Rectangle getPathBounds(final TreePath path) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(path); // wenn durch path representierter Knoten die Wurzel des Originalbaum ist if (t == null) { return super.getPathBounds(path); } else { final Rectangle rec = t.getPathBounds(getPathForSubTree(path)); final SubTreePane pane = panes.get(trees.indexOf(t)); rec.setLocation((int)rec.getX() + pane.getX(), (int)rec.getY() + pane.getY()); return rec; } } else { return super.getPathBounds(path); } } @Override public int getRowCount() { if (useSlideableTreeView) { int sum = 0; if (trees != null) { for (final SlideableSubTree t : trees) { // Anzahl der sichtbaren Zeilen + 1 fuer den jeweiligen Root des SubTree sum += t.getRowCount(); if (!t.isRootVisible()) { sum++; } } } return sum; } else { return super.getRowCount(); } } @Override public TreePath getSelectionPath() { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { /*final SubTreePane pane = panes.get(trees.indexOf(t)); * if (pane.isSelected()) { return getPathforOriginalTree(new TreePath(t.getModel().getRoot())); } */ final TreePath firstSelection = t.getSelectionPath(); if (firstSelection != null) { return getPathforOriginalTree(firstSelection); } } } return null; } else { return super.getSelectionPath(); } } @Override public TreePath[] getSelectionPaths() { if (useSlideableTreeView) { final ArrayList<TreePath> paths = new ArrayList<TreePath>(); if (trees != null) { for (final SlideableSubTree t : trees) { final TreePath[] selections = t.getSelectionPaths(); if (selections != null) { for (int i = 0; i < selections.length; i++) { paths.add(getPathforOriginalTree(selections[i])); } } } if (paths.isEmpty()) { return null; } final TreePath[] path = new TreePath[paths.size()]; paths.toArray(path); return path; } return null; } else { return super.getSelectionPaths(); } } @Override public int getSelectionCount() { if (useSlideableTreeView) { int sum = 0; if (trees != null) { for (final SlideableSubTree t : trees) { sum += t.getSelectionCount(); } return sum; } } return super.getSelectionCount(); } @Override public TreePath getPathForRow(final int row) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForRow(row); final int subTreeRow = getRowForSubTree(row); if (t != null) { if ((subTreeRow < 0) && !t.isRootVisible()) { return getPathforOriginalTree(new TreePath(t.getModel().getRoot())); } else { final TreePath tmp = t.getPathForRow(subTreeRow); final TreePath path = getPathforOriginalTree(tmp); return path; } } return null; } else { return getPathForRow(row); } } @Override public int getRowForPath(final TreePath path) { if (useSlideableTreeView) { final SlideableSubTree subTree = getSubTreeForPath(path); final TreePath subPath = getPathForSubTree(path); int offset = 0; int row = -1; if (trees != null) { for (final SlideableSubTree t : trees) { if (t.equals(subTree)) { break; } else { offset += t.getRowCount() + 1; } } row = subTree.getRowForPath(subPath) + offset; } return row; } else { return super.getRowForPath(path); } } @Override public TreePath getNextMatch(final String prefix, final int startingRow, final Bias bias) { return super.getNextMatch(prefix, startingRow, bias); } @Override protected TreePath[] getPathBetweenRows(final int index0, final int index1) { if (useSlideableTreeView) { final ArrayList<TreePath> list = new ArrayList<TreePath>(); for (int i = index0 + 1; i <= index1; i++) { list.add(getPathForRow(i)); } final TreePath[] finalPaths = new TreePath[list.size()]; for (int i = 0; i < finalPaths.length; i++) { finalPaths[i] = list.get(i); } return finalPaths; } else { return getPathBetweenRows(index0, index1); } } @Override public boolean hasBeenExpanded(final TreePath path) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(path); // keinen SubTree gefunden, Knoten ist also die Wurzel oder nicht enthalten if (t == null) { return super.hasBeenExpanded(path); } else { return t.hasBeenExpanded(getPathForSubTree(path)); } } else { return super.hasBeenExpanded(path); } } @Override public boolean isEditing() { if (useSlideableTreeView) { boolean isEditing = false; if (trees != null) { for (final SlideableSubTree t : trees) { if (t.isEditing()) { isEditing = true; } } } return isEditing; } else { return super.isEditable(); } } @Override public boolean isExpanded(final TreePath path) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(path); if (t == null) { return super.isExpanded(path); } else { return t.isExpanded(getPathForSubTree(path)); } } else { return super.isExpanded(path); } } @Override public boolean isExpanded(final int row) { if (useSlideableTreeView) { final TreePath path = getPathForRow(row); return isExpanded(path); } else { return super.isExpanded(row); } } @Override public boolean isPathSelected(final TreePath path) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(path); if (t == null) { // was passiert falls der Knoten die wurzel ist? return super.isPathSelected(path); } else { return t.isPathSelected(getPathForSubTree(path)); } } else { return super.isPathSelected(path); } } @Override public boolean isRowSelected(final int row) { if (useSlideableTreeView) { final TreePath path = getPathForRow(row); if (path == null) { return false; } final boolean isSelected = isPathSelected(path); return isSelected; } else { return super.isRowSelected(row); } } @Override public boolean isSelectionEmpty() { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { if (!(t.isSelectionEmpty())) { return false; } } } return true; } else { return super.isSelectionEmpty(); } } @Override public boolean isVisible(final TreePath path) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(path); if (t == null) { return super.isVisible(path); } else { return t.isVisible(getPathForSubTree(path)); } } else { return super.isVisible(); } } @Override public void makeVisible(final TreePath path) { if (useSlideableTreeView) { final JTree t = getSubTreeForPath(path); if (t == null) { super.makeVisible(path); } else { t.makeVisible(getPathForSubTree(path)); } } else { super.makeVisible(path); } } @Override public void removeSelectionInterval(final int index0, final int index1) { if (useSlideableTreeView) { if (trees != null) { if (index1 < index0) { return; } else { for (int i = index0; i <= index1; i++) { final TreePath path = getPathForRow(i); removeSelectionPath(path); } } super.removeSelectionInterval(index0, index1); } } else { super.removeSelectionInterval(index0, index1); } } @Override public void removeSelectionPath(final TreePath path) { if (useSlideableTreeView) { if (trees != null) { final SlideableSubTree subTree = getSubTreeForPath(path); subTree.removeSelectionPath(getPathForSubTree(path)); } } super.removeSelectionPath(path); } @Override public void removeSelectionPaths(final TreePath[] paths) { if (useSlideableTreeView) { for (int i = 0; i < paths.length; i++) { removeSelectionPath(paths[i]); } } super.removeSelectionPaths(paths); } @Override public void removeSelectionRow(final int row) { if (useSlideableTreeView) { final TreePath path = getPathForRow(row); removeSelectionPath( path); } super.removeSelectionRow(row); } @Override public void removeSelectionRows(final int[] rows) { if (useSlideableTreeView) { for (int i = 0; i < rows.length; i++) { final TreePath path = getPathForRow(i); removeSelectionPath(path); } } super.removeSelectionRows(rows); } @Override protected boolean removeDescendantSelectedPaths(final TreePath path, final boolean includePath) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(path); final TreePath subTreePath = getPathForSubTree(path); if (trees != null) { return t.removeDescendantSelectedPaths(subTreePath, includePath); } else { return super.removeDescendantSelectedPaths(path, includePath); } } else { return super.removeDescendantSelectedPaths(path, includePath); } } @Override protected void removeDescendantToggledPaths(final Enumeration<TreePath> toRemove) { if (useSlideableTreeView) { if (trees != null) { while (toRemove.hasMoreElements()) { final TreePath path = toRemove.nextElement(); final SlideableSubTree t = getSubTreeForPath(path); final Vector<TreePath> subToRemove = new Vector<TreePath>(); subToRemove.add(getPathForSubTree(path)); t.removeDescendantToggledPaths(subToRemove.elements()); } } else { super.removeDescendantToggledPaths(toRemove); } } else { super.removeDescendantToggledPaths(toRemove); } } @Override public int[] getSelectionRows() { if (useSlideableTreeView) { final int[][] result = new int[trees.size()][]; int offset = 0; int count = 0; if (trees != null) { // getSelectionRows fuer jeden Subtree for (final SlideableSubTree t : trees) { result[trees.indexOf(t)] = t.getSelectionRows(); count += result[trees.indexOf(t)].length; // Offset aufaddieren fuer korrekte Inidzes for (int i = 0; i < result[trees.indexOf(t)].length; i++) { result[trees.indexOf(t)][i] += offset; } offset += t.getRowCount() + 1; } final int[] selectionRows = new int[count]; // Ergebnisse zusammenfassen for (int i = 0; i < selectionRows.length; i++) { for (int j = 0; j < result[i].length; j++) { selectionRows[i] = result[i][j]; } } return selectionRows; } } return super.getSelectionRows(); } @Override public void scrollPathToVisible(final TreePath path) { if (useSlideableTreeView) { if (trees != null) { // final TreePath selectionPath = path; if ((path == null) || (path.getPathCount() <= 2)) { return; } final SlideableSubTree t = getSubTreeForPath(path); if (t != null) { // path ist nicht die wurzel des baums, und Subroot ist vorhanden final SubTreePane pane = panes.get(trees.indexOf(t)); if (!pane.isSelected() && pane.isCollapsed()) { final PropertyChangeListener propChangedListener = new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent e) { if (e.getNewValue().equals("expanded")) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final TreePath tmpPath = path; final Rectangle r = SlideableTree.this.getPathBounds(tmpPath); final JViewport vp = containerScrollPane.getViewport(); if ((r != null) && ((r.getY() + pane.getTitleBarHeight()) > vp.getHeight())) { final double y = r.getY() + pane.getTitleBarHeight(); vp.setViewPosition(new Point(0, (int)y)); } } }); pane.removePropertyChangeListener(this); } } }; pane.addPropertyChangeListener(JXCollapsiblePane.ANIMATION_STATE_KEY, propChangedListener); pane.setCollapsed(false); return; } final Rectangle r = SlideableTree.this.getPathBounds(path); final JViewport vp = containerScrollPane.getViewport(); if ((r != null) && ((r.getY() + pane.getTitleBarHeight()) > vp.getHeight())) { final double y = r.getY() + pane.getTitleBarHeight(); vp.setViewPosition(new Point(0, (int)y)); } return; } } } super.scrollPathToVisible(path); } @Override public void scrollRowToVisible( final int row) { if (useSlideableTreeView) { final TreePath path = getPathForRow(row); scrollPathToVisible( path); } else { super.scrollRowToVisible(row); } } @Override public void setAnchorSelectionPath(final TreePath newPath) { if (useSlideableTreeView) { if (trees != null) { final SlideableSubTree t = getSubTreeForPath(newPath); final TreePath subTreePath = getPathForSubTree(newPath); if (t != null) { t.setAnchorSelectionPath(subTreePath); } } } super.setAnchorSelectionPath(newPath); } @Override public void setCellEditor( final TreeCellEditor cellEditor) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setCellEditor(cellEditor); } } } super.setCellEditor(cellEditor); } @Override public void setCellRenderer( final TreeCellRenderer x) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setCellRenderer(x); final DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)t.getModel().getRoot(); final SubTreePane pane = panes.get(trees.indexOf(t)); final DefaultTreeCellRenderer renderer; if (x instanceof DefaultTreeCellRenderer) { renderer = (DefaultTreeCellRenderer)t.getCellRenderer(); final JLabel l = (JLabel)renderer.getTreeCellRendererComponent( this, rootNode, false, !(pane.isCollapsed()), rootNode.isLeaf(), 0, false); pane.setIcon(l.getIcon()); } } } } super.setCellRenderer(x); } @Override public void setDragEnabled( final boolean b) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setDragEnabled(b); } } } super.setDragEnabled(b); } @Override public void setEditable( final boolean flag) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setEditable(flag); } } } super.setEditable(flag); } @Override public void setExpandsSelectedPaths( final boolean newValue) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setExpandsSelectedPaths(newValue); } } } super.setExpandsSelectedPaths(newValue); } @Override protected void setExpandedState( final TreePath path, final boolean state) { if (useSlideableTreeView) { if (trees != null) { final SlideableSubTree t = getSubTreeForPath(path); final TreePath subTreePath = getPathForSubTree(path); t.setExpandedState(subTreePath, state); } } else { super.setExpandedState(path, state); } } @Override public void setInvokesStopCellEditing( final boolean newValue) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setInvokesStopCellEditing(newValue); } } } super.setInvokesStopCellEditing(newValue); } @Override public void setLargeModel( final boolean newValue) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setLargeModel(newValue); } super.setLargeModel(newValue); } } else { super.setLargeModel(newValue); } } @Override public void setLeadSelectionPath( final TreePath newPath) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setLeadSelectionPath(newPath); } } } super.setLeadSelectionPath(newPath); } @Override public void setModel( final TreeModel newModel) { if (useSlideableTreeView) { final TreeModel oldModel = this.getModel(); treeModel = newModel; firePropertyChange( TREE_MODEL_PROPERTY, oldModel, newModel); if (trees != null) { createSubTrees(newModel); flushTreeContainer(); addToTreeContainer( panes); } } else { super.setModel(newModel); } } @Override public void setRootVisible( final boolean rootVisible) { if (useSlideableTreeView) { for (final SlideableSubTree t : trees) { t.setRootVisible(rootVisible); } } super.setRootVisible(rootVisible); } @Override public void setRowHeight(final int rowHeight) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setRowHeight(rowHeight); } } else { super.setRowHeight(rowHeight); } } else { super.setRowHeight(rowHeight); } } @Override public void setScrollsOnExpand( final boolean newValue) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setScrollsOnExpand(newValue); } } } else { super.setScrollsOnExpand(newValue); } } @Override public void setSelectionInterval( final int index0, final int index1) { if (useSlideableTreeView) { if (trees != null) { final ArrayList<TreePath> pathList = new ArrayList<TreePath>(); if (index1 < index0) { return; } else { for (int i = index0; i <= index1; i++) { final TreePath path = getPathForRow(i); pathList.add(path); } final TreePath[] finalPaths = new TreePath[pathList.size()]; for (int i = 0; i < finalPaths.length; i++) { finalPaths[i] = pathList.get(i); } setSelectionPaths(finalPaths); } } else { super.setSelectionInterval(index0, index1); } } else { super.setSelectionInterval(index0, index1); } } @Override public void setSelectionModel( final TreeSelectionModel selectionModel) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setSelectionModel(selectionModel); } } else { super.setSelectionModel(selectionModel); } } else { super.setSelectionModel(selectionModel); } } @Override public void setSelectionPath( final TreePath path) { if (useSlideableTreeView) { if (trees != null) { final SlideableSubTree t = getSubTreeForPath(path); for (final JTree tmpTree : trees) { if (t.equals(tmpTree)) { continue; } else { tmpTree.clearSelection(); } } if (t == null) { // was wenn der Root knoten selektiert werden soll } else { t.setSelectionPath(getPathForSubTree(path)); } } } else { super.setSelectionPath(path); } } @Override public void setSelectionPaths( final TreePath[] paths) { if (useSlideableTreeView) { for (int i = 0; i < paths.length; i++) { setSelectionPath(paths[i]); } } else { super.setSelectionPaths(paths); } } @Override public void setSelectionRow( final int row) { if (useSlideableTreeView) { final TreePath path = getPathForRow(row); final SlideableSubTree subTree = getSubTreeForPath(path); subTree.setSelectionPath(getPathForSubTree(path)); } else { super.setSelectionRow(row); } } @Override public void setSelectionRows(final int[] rows) { if (useSlideableTreeView) { for (int i = 0; i < rows.length; i++) { final TreePath path = getPathForRow(rows[i]); final SlideableSubTree subTree = getSubTreeForPath(path); subTree.setSelectionPath(getPathForSubTree(path)); } } else { super.setSelectionRows(rows); } } @Override public void setShowsRootHandles( final boolean newValue) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setShowsRootHandles(newValue); } } else { super.setShowsRootHandles(newValue); } } else { super.setShowsRootHandles(newValue); } } @Override public void setToggleClickCount( final int clickCount) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setToggleClickCount(clickCount); } } else { super.setToggleClickCount(clickCount); } } else { super.setToggleClickCount(clickCount); } } @Override public void setUI( final TreeUI ui) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setUI(ui); } } else { super.setUI(ui); } } else { super.setUI(ui); } } @Override public void setVisibleRowCount( final int newCount) { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.setVisibleRowCount(newCount); } } } super.setVisibleRowCount(newCount); } @Override public void startEditingAtPath( final TreePath path) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(path); if (t == null) { super.startEditingAtPath(path); } else { t.startEditingAtPath(getPathForSubTree(path)); } } else { super.startEditingAtPath(path); } } @Override public boolean stopEditing() { if (useSlideableTreeView) { boolean stopped = false; if (trees != null) { for (final SlideableSubTree t : trees) { if (t.stopEditing()) { stopped = true; } } } else { return super.stopEditing(); } return stopped; } else { return super.stopEditing(); } } @Override public void updateUI() { if (useSlideableTreeView) { if (trees != null) { for (final SlideableSubTree t : trees) { t.updateUI(); } } } super.updateUI(); } /** * creates for each child of the root node a JXTaskPane with an JTree and add it to the container. * * @param model the TreeModel which contains the root node */ private void createSubTrees(final TreeModel model) { final TreeModelListener listener = createTreeModelListener(); treeModel.addTreeModelListener(listener); flushTreeContainer(); trees = new ArrayList<SlideableSubTree>(); panes = new ArrayList<SubTreePane>(); final Object root = this.getModel().getRoot(); final int childCount = this.getModel().getChildCount(root); for (int i = 0; i < childCount; i++) { final Object child = model.getChild(root, i); createTreeNode( child); addToTreeContainer( panes); } } /** * creates the JXTaskPane and the SlideableSubTree for a tree node. * * @param node the root fo the SlideableSubTree */ private void createTreeNode(final Object node) { final TreeNode newRootNode = new DefaultMutableTreeNode(node); // use specialSelection as default... final SlideableSubTree subTree = new SlideableSubTree(newRootNode, true); final DelegatingModel modelDelegate = new DelegatingModel(node, this.getModel()); subTree.setModel(modelDelegate); subTree.setEditable(false); subTree.setRootVisible(false); subTree.addTreeExpansionListener(this); subTree.addTreeSelectionListener(this); subTree.addTreeWillExpandListener(this); subTree.setBorder(new EmptyBorder(1, 1, 1, 1)); trees.add(subTree); final SubTreePane tmpPane = new SubTreePane(); tmpPane.setCollapsed(true); final DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)subTree.getModel().getRoot(); final TreeCellRenderer treeCellRenderer = subTree.getCellRenderer(); final JLabel l = (JLabel)treeCellRenderer.getTreeCellRendererComponent( SlideableTree.this, (DefaultMutableTreeNode)subTree.getModel().getRoot(), false, (tmpPane.isCollapsed()), false, 0, false); tmpPane.setIcon(l.getIcon()); final Border border = BorderFactory.createLineBorder(new Color(234, 234, 234)); ((JComponent)tmpPane.getContentPane()).setBorder(border); tmpPane.getContentPane().setBackground(Color.white); tmpPane.setTitle(newRootNode.toString()); tmpPane.addMouseListener(new ClickAndSelectListener()); // ((JComponent)tmpPane.getContentPane()).setBorder(new EmptyBorder(1, 16, 1, 1)); tmpPane.add(subTree); panes.add(tmpPane); } /** * flushs the container which contains the SlideableSubTrees. */ private void flushTreeContainer() { if (container != null) { container.removeAll(); } } /** * auxiliary method to fill the container which contains the lideableSubTrees. * * @param list a list, that contains the panes */ private void addToTreeContainer(final ArrayList<SubTreePane> list) { for (final SubTreePane p : list) { p.setCollapsed(true); // p.setSelected(false); container.add(p); } } /** * auxiliary method, to locate the SlideableSubTree that contains the TreeNode repesents by path. * * @param path a TreePath which represents a TreeNode * * @return returns the SlideableSubTree which contains the TreeNode or null if there is no SubTree */ private SlideableSubTree getSubTreeForPath(final TreePath path) { if (path == null) { return null; } final Object pathRoot = path.getPathComponent(0); // Der Pfad entaehlt nur einen Knoten, also die Wurzel if (path.getPathCount() <= 1) { return null; } else { final Object pathSubRoot = path.getPathComponent(1); if (trees != null) { for (final JTree t : trees) { final Object subTreeRoot = t.getModel().getRoot(); if (pathSubRoot.equals(subTreeRoot)) { return (SlideableSubTree)t; } } } } return null; } /** * auxiliary method that turns a path of a SlideableSubTree to a path of the original tree. * * @param subTreePath a path of a SlideableSubTree * * @return the TreePath for the original Tree */ protected TreePath getPathforOriginalTree(final TreePath subTreePath) { final Object pathRoot = subTreePath.getPathComponent(0); final Object origRoot = this.getModel().getRoot(); // falls der Methode bereits ein pfad des original Baum uebergeben wird, diesen einfach zurueckgeben if (pathRoot.equals(origRoot)) { return subTreePath; } final Object[] oldPath = subTreePath.getPath(); final Object[] newPath = new Object[oldPath.length + 1]; newPath[ 0] = origRoot; System.arraycopy(oldPath, 0, newPath, 1, oldPath.length); return new TreePath(newPath); } /** * auxiliary method which turns a path of the original tree to a path of the SlideableSubTree. * * @param originPath a path of the original Tree * * @return returns the path for the SlideableSubTree or null if originPath represents the root of the original tree */ private TreePath getPathForSubTree(final TreePath originPath) { final Object pathRoot = originPath.getPathComponent(0); final Object originRoot = this.getModel().getRoot(); // wenn path nicht zum originalbaum gehoert if (!pathRoot.equals(originRoot)) { return null; } // der pfad enthaelt nur den Root knoten des Originalbaums else if (originPath.getPathCount() <= 1) { return null; } else { final Object[] oldPath = originPath.getPath(); final Object[] newPath = new Object[oldPath.length - 1]; System.arraycopy(oldPath, 1, newPath, 0, newPath.length); return new TreePath(newPath); } } /** * DOCUMENT ME! * * @param row DOCUMENT ME! * * @return DOCUMENT ME! */ private SlideableSubTree getSubTreeForRow(final int row) { int sum = 0; if (trees != null) { for (final SlideableSubTree t : trees) { sum += t.getRowCount(); if (!t.isRootVisible()) { sum += 1; } if (sum > row) { return t; } } } return null; } /** * DOCUMENT ME! * * @param originRow DOCUMENT ME! * * @return DOCUMENT ME! */ private int getRowForSubTree(final int originRow) { final SlideableSubTree tmpTree = getSubTreeForRow(originRow); int offset = 1; if (tmpTree != null) { for (final SlideableSubTree t : trees) { if (t.equals(tmpTree)) { break; } else { offset += t.getRowCount(); if (!t.isRootVisible()) { offset++; } } } } return originRow - offset; } @Override public Object getLastSelectedPathComponent() { final TreePath path = this.getSelectionPath(); if (path != null) { return path.getLastPathComponent(); } return null; } @Override public void addTreeExpansionListener(final TreeExpansionListener tel) { super.addTreeExpansionListener(tel); } @Override public void fireTreeExpanded(final TreePath path) { if (useSlideableTreeView) { final TreeExpansionListener[] listener = this.getTreeExpansionListeners(); for (int i = 0; i < listener.length; i++) { listener[i].treeExpanded(new TreeExpansionEvent(this, path)); } // scrollPathToVisible(path); } else { super.fireTreeExpanded(path); } } @Override public void treeExpanded(final TreeExpansionEvent event) { fireTreeExpanded(getPathforOriginalTree(event.getPath())); } @Override public void treeCollapsed(final TreeExpansionEvent event) { fireTreeCollapsed(getPathforOriginalTree(event.getPath())); } /* * wird aufgerufen wenn sich die Slektion innerhalb eines Subtrees aendert */ @Override public void valueChanged(final TreeSelectionEvent e) { if (useSlideableTreeView) { if (trees != null) { final TreePath path = getPathforOriginalTree(e.getPath()); if (e.isAddedPath()) { for (final JTree tmpTree : trees) { final SubTreePane pane = panes.get(trees.indexOf(tmpTree)); pane.setSelected(false); if (!tmpTree.equals((SlideableSubTree)e.getSource())) { tmpTree.clearSelection(); } } } fireValueChanged(e); } } } @Override public void treeWillExpand(final TreeExpansionEvent event) throws ExpandVetoException { fireTreeWillExpand(getPathforOriginalTree(event.getPath())); } @Override public void treeWillCollapse(final TreeExpansionEvent event) throws ExpandVetoException { fireTreeWillCollapse(getPathforOriginalTree(event.getPath())); } @Override public TreeModelListener createTreeModelListener() { if (useSlideableTreeView) { return new MyTreeModelHandler(this); } else { return super.createTreeModelListener(); } } @Override public void addMouseListener(final MouseListener l) { if (useSlideableTreeView) { if (trees != null) { for (final JTree t : trees) { t.addMouseListener(l); panes.get(trees.indexOf(t)).addMouseListener(l); } } } else { super.addMouseListener(l); } } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public ArrayList<SlideableSubTree> getTrees() { return trees; } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public ArrayList<SubTreePane> getPanes() { return panes; } //~ Inner Classes ---------------------------------------------------------- /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ protected class MyTreeModelHandler implements TreeModelListener { //~ Instance fields ---------------------------------------------------- private SlideableTree tree; //~ Constructors ------------------------------------------------------- /** * Creates a new MyTreeModelHandler object. */ public MyTreeModelHandler() { } /** * Creates a new MyTreeModelHandler object. * * @param t DOCUMENT ME! */ public MyTreeModelHandler(final SlideableTree t) { tree = t; } //~ Methods ------------------------------------------------------------ @Override public void treeNodesChanged(final TreeModelEvent e) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(e.getTreePath()); EventQueue.invokeLater(new Runnable() { @Override public void run() { // t.updateUI(); } }); tree.scrollPathToVisible(e.getTreePath()); } } @Override public void treeNodesInserted(final TreeModelEvent e) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(e.getTreePath()); EventQueue.invokeLater(new Runnable() { @Override public void run() { // t.updateUI(); } }); tree.scrollPathToVisible(e.getTreePath()); } } @Override public void treeStructureChanged(final TreeModelEvent e) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(e.getTreePath()); if (t == null) { if (tree.getModel().getRoot().equals(e.getTreePath().getLastPathComponent())) { // ein Knoten dirket unterhalb des Root Knoten hat sich geaendert.. final TreeNode treeRoot = (DefaultMutableTreeNode)tree.getModel().getRoot(); final int childCount = treeRoot.getChildCount(); if (childCount < panes.size()) { // nodes were removed... final int treesToRemove = panes.size() - childCount; for (int i = 0; i < treesToRemove; i++) { trees.remove((trees.size() - 1) - i); panes.remove((panes.size() - 1) - i); } } else if (childCount > panes.size()) { // nodes were added final int treesToAdd = childCount - panes.size(); for (int i = 0; i < treesToAdd; i++) { createTreeNode(treeRoot.getChildAt((childCount - 1) - i)); } } for (int i = 0; i < treeRoot.getChildCount(); i++) { final TreeNode child = treeRoot.getChildAt(i); final SlideableSubTree subTree = trees.get(i); if (subTree != null) { final TreeNode subRoot = (TreeNode)subTree.getModel().getRoot(); if (subRoot.equals(child)) { // this node doesnt changed continue; } else { // node has changed, modify pane and subtree final SubTreePane pane = panes.get(i); pane.setTitle(child.toString()); pane.removeAll(); final TreeModel newModel = new DelegatingModel(child, tree.getModel()); subTree.setModel(newModel); pane.add(subTree); } } } tree.flushTreeContainer(); tree.addToTreeContainer(panes); tree.updateUI(); } } else { EventQueue.invokeLater(new Runnable() { @Override public void run() { t.updateUI(); } }); tree.scrollPathToVisible(e.getTreePath()); } } } @Override public void treeNodesRemoved(final TreeModelEvent e) { if (useSlideableTreeView) { final SlideableSubTree t = getSubTreeForPath(e.getTreePath()); EventQueue.invokeLater(new Runnable() { @Override public void run() { // t.updateUI(); } }); tree.scrollPathToVisible(e.getTreePath()); } } } /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ protected class ClickAndSelectListener extends MouseAdapter { //~ Methods ------------------------------------------------------------ @Override public void mousePressed(final MouseEvent e) { final SubTreePane pane = (SubTreePane)e.getSource(); // liegen Koordianten innerhalb der Titlebar? if ((e.getX() < pane.getWidth()) && (e.getY() < pane.getTitleBarHeight())) { { if (!e.isPopupTrigger()) { final SlideableSubTree t = trees.get(panes.indexOf(pane)); final TreePath path = SlideableTree.this.getPathforOriginalTree(new TreePath( t.getModel().getRoot())); // setze RootKnoten des SubTrees als Selektion SlideableTree.this.clearSelection(); SlideableTree.this.setSelectionPath(path); // setze icon neu final TreeCellRenderer cellRenderer = t.getCellRenderer(); final JLabel l = (JLabel)cellRenderer.getTreeCellRendererComponent( SlideableTree.this, (DefaultMutableTreeNode)t.getModel().getRoot(), false, (pane.isCollapsed()), false, 0, false); pane.setIcon(l.getIcon()); if (!pane.isCollapsed()) { // pane soll geschlossen werden pane.setSelected(false); SlideableTree.this.fireTreeCollapsed(path); } else { // pane soll geoeffnet werden pane.setSelected(true); SlideableTree.this.fireTreeExpanded(path); } } } } } } }