/* * Copyright 2003-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.ide.ui.tree; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import java.awt.Rectangle; import java.awt.event.ActionEvent; /** * Ripped from IDEA */ public class TreeScrollingUtil { public static void selectPath(JTree tree, TreePath path) { tree.makeVisible(path); showRowCentred(tree, tree.getRowForPath(path)); } public static void moveDown(JTree tree) { int size = tree.getRowCount(); int row = getSelectedRow(tree); if (row < size - 1) { row++; showAndSelect(tree, row, row + 2, row, true); } } public static void moveUp(JTree tree) { int row = getSelectedRow(tree); if (row > 0) { row--; showAndSelect(tree, row - 2, row, row, true); } } public static void movePageUp(JTree tree) { int visible = getVisibleRowCount(tree); if (visible <= 0) { moveHome(tree); return; } int decrement = visible - 1; int row = Math.max(getSelectedRow(tree) - decrement, 0); int top = getFirstVisibleRow(tree) - decrement; int bottom = top + visible - 1; showAndSelect(tree, top, bottom, row, true); } public static void movePageDown(JTree tree) { int visible = getVisibleRowCount(tree); if (visible <= 0) { moveEnd(tree); return; } int size = tree.getRowCount(); int increment = visible - 1; int index = Math.min(getSelectedRow(tree) + increment, size - 1); int top = getFirstVisibleRow(tree) + increment; int bottom = top + visible - 1; showAndSelect(tree, top, bottom, index, true); } public static void moveHome(JTree tree) { showRowCentred(tree, 0); } public static void moveEnd(JTree tree) { showRowCentred(tree, tree.getRowCount() - 1); } private static void showRowCentred(JTree tree, int row) { showRowCentred(tree, row, true); } public static void showRowCentred(JTree tree, int row, boolean centerHorizontally) { int visible = getVisibleRowCount(tree); int top = visible > 0 ? row - (visible - 1) / 2 : row; int bottom = visible > 0 ? top + visible - 1 : row; showAndSelect(tree, top, bottom, row, centerHorizontally); } private static void showAndSelect(JTree tree, int top, int bottom, int row, boolean centerHorizontally) { int size = tree.getRowCount(); if (size == 0) { tree.clearSelection(); return; } if (top < 0) { top = 0; } if (bottom >= size) { bottom = size - 1; } Rectangle topBounds = tree.getRowBounds(top); Rectangle bottomBounds = tree.getRowBounds(bottom); Rectangle bounds; if (topBounds == null) { bounds = bottomBounds; } else if (bottomBounds == null) { bounds = topBounds; } else { bounds = topBounds.union(bottomBounds); } if (bounds != null) { TreePath path = tree.getPathForRow(row); if (path != null && path.getParentPath() != null) { Rectangle parentBounds = tree.getPathBounds(path.getParentPath()); if (parentBounds != null) { bounds.x = parentBounds.x; } } if (!centerHorizontally) { bounds.x = 0; bounds.width = tree.getWidth(); } else { bounds.width = Math.min(bounds.width, tree.getVisibleRect().width); } tree.scrollRectToVisible(bounds); } tree.setSelectionRow(row); } private static int getSelectedRow(JTree tree) { return tree.getRowForPath(tree.getSelectionPath()); } private static int getFirstVisibleRow(JTree tree) { Rectangle visible = tree.getVisibleRect(); int row = -1; for (int i = 0; i < tree.getRowCount(); i++) { Rectangle bounds = tree.getRowBounds(i); if (visible.y <= bounds.y && visible.y + visible.height >= bounds.y + bounds.height) { row = i; break; } } return row; } private static int getVisibleRowCount(JTree tree) { Rectangle visible = tree.getVisibleRect(); int count = 0; for (int i = 0; i < tree.getRowCount(); i++) { Rectangle bounds = tree.getRowBounds(i); if (visible.y <= bounds.y && visible.y + visible.height >= bounds.y + bounds.height) { count++; } } return count; } public static void installActions(final JTree tree) { tree.getActionMap().put("scrollUpChangeSelection", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { movePageUp(tree); } }); tree.getActionMap().put("scrollDownChangeSelection", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { movePageDown(tree); } }); tree.getActionMap().put("selectPrevious", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { moveUp(tree); } }); tree.getActionMap().put("selectNext", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { moveDown(tree); } }); Action action = tree.getActionMap().get("selectLast"); if (action != null) { tree.getActionMap().put("selectLastChangeLead", action); } } public static void collapseAll(JTree tree, int keepSelectionLevel) { TreePath leadSelectionPath = tree.getLeadSelectionPath(); // Collapse all int row = tree.getRowCount() - 1; while (row >= 0) { tree.collapseRow(row); row--; } DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot(); tree.expandPath(new TreePath(root)); if (leadSelectionPath != null) { Object[] path = leadSelectionPath.getPath(); Object[] pathToSelect = new Object[path.length > keepSelectionLevel ? keepSelectionLevel : path.length]; System.arraycopy(path, 0, pathToSelect, 0, pathToSelect.length); TreeScrollingUtil.selectPath(tree, new TreePath(pathToSelect)); } } }