0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: /**
0019: * @author Anton Avtamonov, Sergey Burlak
0020: * @version $Revision$
0021: */package javax.swing.plaf.basic;
0022:
0023: import java.awt.BasicStroke;
0024: import java.awt.Color;
0025: import java.awt.Component;
0026: import java.awt.Container;
0027: import java.awt.Dimension;
0028: import java.awt.Graphics;
0029: import java.awt.Graphics2D;
0030: import java.awt.Insets;
0031: import java.awt.Rectangle;
0032: import java.awt.Stroke;
0033: import java.awt.datatransfer.StringSelection;
0034: import java.awt.datatransfer.Transferable;
0035: import java.awt.event.ActionEvent;
0036: import java.awt.event.ActionListener;
0037: import java.awt.event.ComponentAdapter;
0038: import java.awt.event.ComponentEvent;
0039: import java.awt.event.ComponentListener;
0040: import java.awt.event.FocusEvent;
0041: import java.awt.event.FocusListener;
0042: import java.awt.event.InputEvent;
0043: import java.awt.event.KeyAdapter;
0044: import java.awt.event.KeyEvent;
0045: import java.awt.event.KeyListener;
0046: import java.awt.event.MouseAdapter;
0047: import java.awt.event.MouseEvent;
0048: import java.awt.event.MouseListener;
0049: import java.awt.event.MouseMotionListener;
0050: import java.beans.PropertyChangeEvent;
0051: import java.beans.PropertyChangeListener;
0052: import java.util.Enumeration;
0053: import java.util.Hashtable;
0054:
0055: import javax.swing.AbstractAction;
0056: import javax.swing.Action;
0057: import javax.swing.CellRendererPane;
0058: import javax.swing.Icon;
0059: import javax.swing.JComponent;
0060: import javax.swing.JScrollBar;
0061: import javax.swing.JScrollPane;
0062: import javax.swing.JTree;
0063: import javax.swing.JViewport;
0064: import javax.swing.LookAndFeel;
0065: import javax.swing.SwingUtilities;
0066: import javax.swing.Timer;
0067: import javax.swing.TransferHandler;
0068: import javax.swing.UIManager;
0069: import javax.swing.event.CellEditorListener;
0070: import javax.swing.event.ChangeEvent;
0071: import javax.swing.event.MouseInputListener;
0072: import javax.swing.event.TreeExpansionEvent;
0073: import javax.swing.event.TreeExpansionListener;
0074: import javax.swing.event.TreeModelEvent;
0075: import javax.swing.event.TreeModelListener;
0076: import javax.swing.event.TreeSelectionEvent;
0077: import javax.swing.event.TreeSelectionListener;
0078: import javax.swing.plaf.ComponentUI;
0079: import javax.swing.plaf.TreeUI;
0080: import javax.swing.text.Position;
0081: import javax.swing.tree.AbstractLayoutCache;
0082: import javax.swing.tree.DefaultTreeCellEditor;
0083: import javax.swing.tree.DefaultTreeCellRenderer;
0084: import javax.swing.tree.FixedHeightLayoutCache;
0085: import javax.swing.tree.TreeCellEditor;
0086: import javax.swing.tree.TreeCellRenderer;
0087: import javax.swing.tree.TreeModel;
0088: import javax.swing.tree.TreePath;
0089: import javax.swing.tree.TreeSelectionModel;
0090: import javax.swing.tree.VariableHeightLayoutCache;
0091:
0092: import org.apache.harmony.x.swing.TreeCommons;
0093: import org.apache.harmony.x.swing.Utilities;
0094:
0095: public class BasicTreeUI extends TreeUI {
0096:
0097: public class CellEditorHandler implements CellEditorListener {
0098: public void editingCanceled(final ChangeEvent e) {
0099: completeEditing(false, false, false);
0100: }
0101:
0102: public void editingStopped(final ChangeEvent e) {
0103: completeEditing(false, false, true);
0104: }
0105: };
0106:
0107: public class ComponentHandler extends ComponentAdapter implements
0108: ActionListener {
0109: protected Timer timer;
0110: protected JScrollBar scrollBar;
0111:
0112: public void componentMoved(final ComponentEvent e) {
0113: JScrollPane pane = getScrollPane();
0114: if (pane == null) {
0115: return;
0116: }
0117:
0118: if (pane.getHorizontalScrollBar().getValueIsAdjusting()) {
0119: scrollBar = pane.getHorizontalScrollBar();
0120: } else if (pane.getVerticalScrollBar()
0121: .getValueIsAdjusting()) {
0122: scrollBar = pane.getVerticalScrollBar();
0123: } else {
0124: return;
0125: }
0126: startTimer();
0127: }
0128:
0129: protected void startTimer() {
0130: if (timer == null) {
0131: timer = new Timer(200, this );
0132: timer.setRepeats(false);
0133: }
0134: timer.restart();
0135: }
0136:
0137: protected JScrollPane getScrollPane() {
0138: Container parent = tree.getParent();
0139: if (!(parent instanceof JViewport)) {
0140: return null;
0141: }
0142: parent = parent.getParent();
0143: return parent instanceof JScrollPane ? (JScrollPane) parent
0144: : null;
0145: }
0146:
0147: public void actionPerformed(final ActionEvent e) {
0148: if (scrollBar.getValueIsAdjusting()) {
0149: timer.restart();
0150: } else {
0151: timer.stop();
0152: updateSize();
0153: }
0154: }
0155: };
0156:
0157: public class FocusHandler implements FocusListener {
0158: public void focusGained(final FocusEvent e) {
0159: repaintFocusedRow();
0160: }
0161:
0162: public void focusLost(final FocusEvent e) {
0163: repaintFocusedRow();
0164: }
0165:
0166: private void repaintFocusedRow() {
0167: TreePath focusedPath = tree.getLeadSelectionPath();
0168: if (focusedPath == null) {
0169: return;
0170: }
0171: tree.repaint(getPathBounds(tree, focusedPath));
0172: }
0173: };
0174:
0175: public class KeyHandler extends KeyAdapter {
0176: protected Action repeatKeyAction;
0177: protected boolean isKeyDown;
0178:
0179: private StringBuffer searchPrefix = new StringBuffer();
0180: private Timer searchTimer = new Timer(1000,
0181: new AbstractAction() {
0182: public void actionPerformed(final ActionEvent e) {
0183: resetSearch();
0184: }
0185: });
0186:
0187: public void keyTyped(final KeyEvent e) {
0188: int rowCount = tree.getRowCount();
0189: if (rowCount == 0
0190: || (e.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) != 0
0191: || (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
0192:
0193: return;
0194: }
0195:
0196: int startRow = tree.getLeadSelectionRow()
0197: + (searchPrefix.length() == 0 ? 1 : 0);
0198: if (startRow >= rowCount || startRow == -1) {
0199: startRow = 0;
0200: }
0201:
0202: searchPrefix.append(e.getKeyChar());
0203:
0204: TreePath nextPath = tree.getNextMatch(searchPrefix
0205: .toString(), startRow, Position.Bias.Forward);
0206: if (nextPath == null) {
0207: if (searchPrefix.length() > 1) {
0208: resetSearch();
0209: searchPrefix.append(e.getKeyChar());
0210: startRow++;
0211: if (startRow >= rowCount) {
0212: startRow = 0;
0213: }
0214: nextPath = tree.getNextMatch(searchPrefix
0215: .toString(), startRow,
0216: Position.Bias.Forward);
0217: }
0218: }
0219: if (nextPath != null) {
0220: searchTimer.stop();
0221: tree.setSelectionPath(nextPath);
0222: searchTimer.restart();
0223: } else {
0224: resetSearch();
0225: }
0226: }
0227:
0228: public void keyPressed(final KeyEvent e) {
0229: }
0230:
0231: public void keyReleased(final KeyEvent e) {
0232: }
0233:
0234: private void resetSearch() {
0235: searchPrefix.delete(0, searchPrefix.length());
0236: searchTimer.stop();
0237: }
0238: };
0239:
0240: public class MouseHandler extends MouseAdapter implements
0241: MouseMotionListener {
0242: private DnDMouseHelper dndhelper = new DnDMouseHelper(tree);
0243:
0244: public void mouseDragged(final MouseEvent e) {
0245: if (!tree.isEnabled()) {
0246: return;
0247: }
0248: dndhelper.mouseDragged(e);
0249: }
0250:
0251: public void mousePressed(final MouseEvent e) {
0252: if (!tree.isEnabled()) {
0253: return;
0254: }
0255:
0256: tree.requestFocus();
0257:
0258: TreePath path = tree.getPathForLocation(e.getX(), e.getY());
0259: dndhelper.mousePressed(e, tree.getDragEnabled(),
0260: path != null, tree.isPathSelected(path));
0261:
0262: if (!tree.getDragEnabled()) {
0263: startEditing(path, e);
0264: processSelectionAndExpansion(path, e);
0265: } else if (path == null) {
0266: processSelectionAndExpansion(path, e);
0267: } else if (!tree.isPathSelected(path)) {
0268: selectPathForEvent(path, e);
0269: }
0270: }
0271:
0272: public void mouseReleased(final MouseEvent e) {
0273: if (!tree.isEnabled()) {
0274: return;
0275: }
0276:
0277: if (!tree.getDragEnabled()) {
0278: return;
0279: }
0280: dndhelper.mouseReleased(e);
0281: if (dndhelper.shouldProcessOnRelease()) {
0282: TreePath path = tree.getPathForLocation(e.getX(), e
0283: .getY());
0284: if (path != null) {
0285: selectPathForEvent(path, e);
0286: }
0287: }
0288: }
0289:
0290: public void mouseMoved(final MouseEvent e) {
0291: }
0292:
0293: private void processSelectionAndExpansion(final TreePath path,
0294: final MouseEvent e) {
0295: if (path != null) {
0296: selectPathForEvent(path, e);
0297: } else {
0298: TreePath closestPath = tree.getClosestPathForLocation(e
0299: .getX(), e.getY());
0300: if (closestPath != null) {
0301: checkForClickInExpandControl(closestPath, e.getX(),
0302: e.getY());
0303: }
0304: }
0305: }
0306: };
0307:
0308: // It is not clear where and how it should be used. Looks like some part of DnD support.
0309: public class MouseInputHandler implements MouseInputListener {
0310: protected Component source;
0311: protected Component destination;
0312:
0313: public MouseInputHandler(final Component source,
0314: final Component destination, final MouseEvent event) {
0315: }
0316:
0317: public void mouseClicked(final MouseEvent e) {
0318: }
0319:
0320: public void mouseEntered(final MouseEvent e) {
0321: }
0322:
0323: public void mouseExited(final MouseEvent e) {
0324: }
0325:
0326: public void mousePressed(final MouseEvent e) {
0327: }
0328:
0329: public void mouseReleased(final MouseEvent e) {
0330: }
0331:
0332: public void mouseDragged(final MouseEvent e) {
0333: }
0334:
0335: public void mouseMoved(final MouseEvent e) {
0336: }
0337:
0338: protected void removeFromSource() {
0339: }
0340: };
0341:
0342: public class NodeDimensionsHandler extends
0343: AbstractLayoutCache.NodeDimensions {
0344: public Rectangle getNodeDimensions(final Object value,
0345: final int row, final int depth, final boolean expanded,
0346: final Rectangle bounds) {
0347:
0348: if (value == null) {
0349: return bounds;
0350: }
0351:
0352: Rectangle result = bounds == null ? new Rectangle()
0353: : bounds;
0354:
0355: if (isEditing(tree) && editingRow == row) {
0356: result.setSize(editingComponent.getSize());
0357: } else {
0358: Component c = getCellRenderer()
0359: .getTreeCellRendererComponent(tree, value,
0360: false, expanded,
0361: tree.getModel().isLeaf(value), row,
0362: false);
0363: result.setSize(c.getPreferredSize());
0364: }
0365:
0366: int rowX = tree.getComponentOrientation().isLeftToRight() ? getRowX(
0367: row, depth)
0368: : tree.getWidth() - getRowX(row, depth)
0369: - result.width;
0370:
0371: result.setLocation(rowX, result.y);
0372:
0373: return result;
0374: }
0375:
0376: protected int getRowX(final int row, final int depth) {
0377: return BasicTreeUI.this .getRowX(row, depth);
0378: }
0379: };
0380:
0381: public class PropertyChangeHandler implements
0382: PropertyChangeListener {
0383: public void propertyChange(final PropertyChangeEvent e) {
0384: String changedProperty = e.getPropertyName();
0385: if (JTree.CELL_RENDERER_PROPERTY.equals(changedProperty)) {
0386: setCellRenderer((TreeCellRenderer) e.getNewValue());
0387: } else if (JTree.CELL_EDITOR_PROPERTY
0388: .equals(changedProperty)) {
0389: setCellEditor((TreeCellEditor) e.getNewValue());
0390: } else if (JTree.TREE_MODEL_PROPERTY
0391: .equals(changedProperty)) {
0392: setModel((TreeModel) e.getNewValue());
0393: } else if (JTree.ROOT_VISIBLE_PROPERTY
0394: .equals(changedProperty)) {
0395: setRootVisible(((Boolean) e.getNewValue())
0396: .booleanValue());
0397: } else if (JTree.SHOWS_ROOT_HANDLES_PROPERTY
0398: .equals(changedProperty)) {
0399: setShowsRootHandles(((Boolean) e.getNewValue())
0400: .booleanValue());
0401: } else if (JTree.ROW_HEIGHT_PROPERTY
0402: .equals(changedProperty)) {
0403: setRowHeight(((Integer) e.getNewValue()).intValue());
0404: } else if (JTree.EDITABLE_PROPERTY.equals(changedProperty)) {
0405: setEditable(((Boolean) e.getNewValue()).booleanValue());
0406: } else if (JTree.LARGE_MODEL_PROPERTY
0407: .equals(changedProperty)) {
0408: setLargeModel(((Boolean) e.getNewValue())
0409: .booleanValue());
0410: } else if (JTree.SELECTION_MODEL_PROPERTY
0411: .equals(changedProperty)) {
0412: setSelectionModel((TreeSelectionModel) e.getNewValue());
0413: } else if (JTree.LEAD_SELECTION_PATH_PROPERTY
0414: .equals(changedProperty)) {
0415: TreePath leadPath = (TreePath) e.getNewValue();
0416: lastSelectedRow = leadPath != null ? tree
0417: .getRowForPath(leadPath) : -1;
0418: }
0419: }
0420: };
0421:
0422: // In our implementation it's not required
0423: public class SelectionModelPropertyChangeHandler implements
0424: PropertyChangeListener {
0425: public void propertyChange(final PropertyChangeEvent event) {
0426: }
0427: };
0428:
0429: public class TreeExpansionHandler implements TreeExpansionListener {
0430: public void treeCollapsed(final TreeExpansionEvent e) {
0431: pathWasCollapsed(e.getPath());
0432: resetDrawingCache();
0433: }
0434:
0435: public void treeExpanded(final TreeExpansionEvent e) {
0436: pathWasExpanded(e.getPath());
0437: resetDrawingCache();
0438: }
0439: };
0440:
0441: public class TreeCancelEditingAction extends AbstractAction {
0442: public TreeCancelEditingAction(final String name) {
0443: super (name);
0444: }
0445:
0446: public void actionPerformed(final ActionEvent e) {
0447: BasicTreeKeyboardActions.cancelAction.actionPerformed(e);
0448: }
0449:
0450: public boolean isEnabled() {
0451: return BasicTreeKeyboardActions.cancelAction.isEnabled();
0452: }
0453: };
0454:
0455: public class TreeHomeAction extends AbstractAction {
0456: protected int direction;
0457:
0458: public TreeHomeAction(final int direction, final String name) {
0459: super (name);
0460: this .direction = direction;
0461: }
0462:
0463: public void actionPerformed(final ActionEvent e) {
0464: if (direction > 0) {
0465: BasicTreeKeyboardActions.selectLastAction
0466: .actionPerformed(e);
0467: } else {
0468: BasicTreeKeyboardActions.selectFirstAction
0469: .actionPerformed(e);
0470: }
0471: }
0472:
0473: public boolean isEnabled() {
0474: return direction > 0 ? BasicTreeKeyboardActions.selectLastAction
0475: .isEnabled()
0476: : BasicTreeKeyboardActions.selectFirstAction
0477: .isEnabled();
0478: }
0479: };
0480:
0481: public class TreeIncrementAction extends AbstractAction {
0482: protected int direction;
0483:
0484: public TreeIncrementAction(final int direction,
0485: final String name) {
0486: super (name);
0487: this .direction = direction;
0488: }
0489:
0490: public void actionPerformed(final ActionEvent e) {
0491: if (direction > 0) {
0492: BasicTreeKeyboardActions.selectNextAction
0493: .actionPerformed(e);
0494: } else {
0495: BasicTreeKeyboardActions.selectPreviousAction
0496: .actionPerformed(e);
0497: }
0498: }
0499:
0500: public boolean isEnabled() {
0501: return direction > 0 ? BasicTreeKeyboardActions.selectNextAction
0502: .isEnabled()
0503: : BasicTreeKeyboardActions.selectPreviousAction
0504: .isEnabled();
0505: }
0506: };
0507:
0508: public class TreePageAction extends AbstractAction {
0509: protected int direction;
0510:
0511: public TreePageAction(final int direction, final String name) {
0512: super (name);
0513: this .direction = direction;
0514: }
0515:
0516: public void actionPerformed(final ActionEvent e) {
0517: if (direction > 0) {
0518: BasicTreeKeyboardActions.scrollDownChangeSelectionAction
0519: .actionPerformed(e);
0520: } else {
0521: BasicTreeKeyboardActions.scrollUpChangeSelectionAction
0522: .actionPerformed(e);
0523: }
0524: }
0525:
0526: public boolean isEnabled() {
0527: return direction > 0 ? BasicTreeKeyboardActions.scrollDownChangeSelectionAction
0528: .isEnabled()
0529: : BasicTreeKeyboardActions.scrollUpChangeSelectionAction
0530: .isEnabled();
0531: }
0532: };
0533:
0534: public class TreeTraverseAction extends AbstractAction {
0535: protected int direction;
0536:
0537: public TreeTraverseAction(final int direction, final String name) {
0538: super (name);
0539: this .direction = direction;
0540: }
0541:
0542: public void actionPerformed(final ActionEvent e) {
0543: if (direction > 0) {
0544: BasicTreeKeyboardActions.selectChildAction
0545: .actionPerformed(e);
0546: } else {
0547: BasicTreeKeyboardActions.selectParentAction
0548: .actionPerformed(e);
0549: }
0550: }
0551:
0552: public boolean isEnabled() {
0553: return direction > 0 ? BasicTreeKeyboardActions.selectChildAction
0554: .isEnabled()
0555: : BasicTreeKeyboardActions.selectParentAction
0556: .isEnabled();
0557: }
0558: };
0559:
0560: public class TreeToggleAction extends AbstractAction {
0561: public TreeToggleAction(final String name) {
0562: super (name);
0563: }
0564:
0565: public void actionPerformed(final ActionEvent e) {
0566: TreePath path = tree.getLeadSelectionPath();
0567: if (path == null) {
0568: return;
0569: }
0570:
0571: toggleExpandState(path);
0572: }
0573:
0574: public boolean isEnabled() {
0575: return tree.getLeadSelectionPath() != null;
0576: }
0577: };
0578:
0579: public class TreeSelectionHandler implements TreeSelectionListener {
0580: public void valueChanged(final TreeSelectionEvent e) {
0581: completeEditing();
0582: TreePath path = e.getNewLeadSelectionPath();
0583: TreePath oldLeadPath = tree.getLeadSelectionPath();
0584: tree.setAnchorSelectionPath(path);
0585: tree.setLeadSelectionPath(path);
0586: if (tree.getExpandsSelectedPaths() && path != null) {
0587: tree.makeVisible(path);
0588: }
0589:
0590: repaintPaths(e.getPaths());
0591: repaintPath(oldLeadPath);
0592: repaintPath(path);
0593: }
0594:
0595: private void repaintPaths(final TreePath[] paths) {
0596: if (paths == null || paths.length == 0) {
0597: return;
0598: }
0599: for (int i = 0; i < paths.length; i++) {
0600: repaintPath(paths[i]);
0601: }
0602: }
0603:
0604: private void repaintPath(final TreePath path) {
0605: if (path == null) {
0606: return;
0607: }
0608: Rectangle pathBounds = tree.getPathBounds(path);
0609: if (pathBounds != null) {
0610: tree.repaint(pathBounds);
0611: }
0612: }
0613: };
0614:
0615: public class TreeModelHandler implements TreeModelListener {
0616: public void treeNodesChanged(final TreeModelEvent e) {
0617: treeState.treeNodesChanged(e);
0618: updateTreeSize(e);
0619: }
0620:
0621: public void treeNodesInserted(final TreeModelEvent e) {
0622: treeState.treeNodesInserted(e);
0623: updateTreeSize(e);
0624: }
0625:
0626: public void treeNodesRemoved(final TreeModelEvent e) {
0627: treeState.treeNodesRemoved(e);
0628: updateTreeSize(e);
0629: }
0630:
0631: public void treeStructureChanged(final TreeModelEvent e) {
0632: treeState.treeStructureChanged(e);
0633: updateTreeSize(e);
0634: }
0635:
0636: private void updateTreeSize(final TreeModelEvent e) {
0637: TreePath eventRootPath = e.getTreePath();
0638: if (eventRootPath == null) {
0639: return;
0640: }
0641: if (tree.isVisible(eventRootPath)) {
0642: updateSize();
0643: }
0644: resetDrawingCache();
0645: tree.repaint();
0646: }
0647: };
0648:
0649: private class TreeTransferHandler extends TransferHandler {
0650: private final String lineSeparator = System
0651: .getProperty("line.separator");
0652:
0653: public int getSourceActions(final JComponent c) {
0654: return COPY;
0655: }
0656:
0657: protected Transferable createTransferable(final JComponent c) {
0658: if (tree.getSelectionCount() == 0) {
0659: return null;
0660: }
0661:
0662: StringBuffer content = new StringBuffer();
0663: TreePath[] selectionPaths = tree.getSelectionPaths();
0664: for (int i = 0; i < selectionPaths.length; i++) {
0665: content
0666: .append(selectionPaths[i]
0667: .getLastPathComponent());
0668: if (i < selectionPaths.length - 1) {
0669: content.append(lineSeparator);
0670: }
0671: }
0672: return new StringSelection(content.toString());
0673: }
0674: }
0675:
0676: protected transient Icon collapsedIcon;
0677: protected transient Icon expandedIcon;
0678: protected Dimension preferredMinSize;
0679: protected JTree tree;
0680: protected transient TreeCellRenderer currentCellRenderer;
0681: protected transient TreeCellEditor cellEditor;
0682: protected CellRendererPane rendererPane;
0683: protected Dimension preferredSize;
0684: protected AbstractLayoutCache treeState;
0685: protected Hashtable<javax.swing.tree.TreePath, java.lang.Boolean> drawingCache;
0686: protected AbstractLayoutCache.NodeDimensions nodeDimensions;
0687: protected TreeModel treeModel;
0688: protected TreeSelectionModel treeSelectionModel;
0689: protected Component editingComponent;
0690: protected TreePath editingPath;
0691:
0692: protected int leftChildIndent;
0693: protected int rightChildIndent;
0694: protected int totalChildIndent;
0695: protected int lastSelectedRow;
0696: protected int depthOffset;
0697: protected int editingRow;
0698:
0699: protected boolean createdRenderer;
0700: protected boolean createdCellEditor;
0701: // Is not used in our implementation
0702: protected boolean stopEditingInCompleteEditing;
0703: protected boolean validCachedPreferredSize;
0704: protected boolean editorHasDifferentSize;
0705: protected boolean largeModel;
0706:
0707: private Color hashColor;
0708: private int rowHeight;
0709: private PropertyChangeListener propertyChangeHandler;
0710: private boolean isRootVisible;
0711: private boolean isEditable;
0712: private boolean showsRootHandles;
0713: private KeyListener keyHandler;
0714: private MouseListener mouseHandler;
0715: private FocusListener focusHandler;
0716: private TreeExpansionListener expansionHandler;
0717: private PropertyChangeListener selectionModelPropertyChangeHandler;
0718: private TreeModelListener modelHandler;
0719: private TreeSelectionListener selectionHandler;
0720: private CellEditorListener cellEditorHandler;
0721: private ComponentListener componentHandler;
0722:
0723: private TreeCommons.PaintTreeContext paintContext = new TreeCommons.PaintTreeContext() {
0724: public JTree getTree() {
0725: return tree;
0726: }
0727:
0728: public Hashtable getDrawingCache() {
0729: return drawingCache;
0730: }
0731:
0732: public AbstractLayoutCache getLayoutCache() {
0733: return treeState;
0734: }
0735:
0736: public boolean paintHandles() {
0737: return true;
0738: }
0739:
0740: public boolean paintHorizontalSeparators() {
0741: return false;
0742: }
0743:
0744: public boolean isEditing(final TreePath path) {
0745: return BasicTreeUI.this .isEditing(tree)
0746: && editingPath.equals(path);
0747: }
0748:
0749: public void paintVerticalPartOfLeg(final Graphics g,
0750: final Rectangle clipBounds, final Insets insets,
0751: final TreePath path) {
0752: BasicTreeUI.this .paintVerticalPartOfLeg(g, clipBounds,
0753: insets, path);
0754: }
0755:
0756: public void paintHorizontalPartOfLeg(final Graphics g,
0757: final Rectangle clipBounds, final Insets insets,
0758: final Rectangle bounds, final TreePath path,
0759: final int row, final boolean isExpanded,
0760: final boolean hasBeenExpanded, final boolean isLeaf) {
0761: BasicTreeUI.this .paintHorizontalPartOfLeg(g, clipBounds,
0762: insets, bounds, path, row, isExpanded,
0763: hasBeenExpanded, isLeaf);
0764: }
0765:
0766: public void paintExpandControl(final Graphics g,
0767: final Rectangle clipBounds, final Insets insets,
0768: final Rectangle bounds, final TreePath path,
0769: final int row, final boolean isExpanded,
0770: final boolean hasBeenExpanded, final boolean isLeaf) {
0771: BasicTreeUI.this .paintExpandControl(g, clipBounds, insets,
0772: bounds, path, row, isExpanded, hasBeenExpanded,
0773: isLeaf);
0774: }
0775:
0776: public void paintRow(final Graphics g,
0777: final Rectangle clipBounds, final Insets insets,
0778: final Rectangle bounds, final TreePath path,
0779: final int row, final boolean isExpanded,
0780: final boolean hasBeenExpanded, final boolean isLeaf) {
0781: BasicTreeUI.this .paintRow(g, clipBounds, insets, bounds,
0782: path, row, isExpanded, hasBeenExpanded, isLeaf);
0783: }
0784:
0785: public void paintHorizontalSeparators(final Graphics g,
0786: final JComponent tree) {
0787: }
0788: };
0789:
0790: public static ComponentUI createUI(final JComponent c) {
0791: return new BasicTreeUI();
0792: }
0793:
0794: protected Color getHashColor() {
0795: return hashColor;
0796: }
0797:
0798: protected void setHashColor(final Color color) {
0799: hashColor = color;
0800: }
0801:
0802: public void setLeftChildIndent(final int newIndent) {
0803: leftChildIndent = newIndent;
0804: totalChildIndent = leftChildIndent + rightChildIndent;
0805: updateSize();
0806: }
0807:
0808: public int getLeftChildIndent() {
0809: return leftChildIndent;
0810: }
0811:
0812: public void setRightChildIndent(final int newIndent) {
0813: rightChildIndent = newIndent;
0814: totalChildIndent = rightChildIndent + leftChildIndent;
0815: updateSize();
0816: }
0817:
0818: public int getRightChildIndent() {
0819: return rightChildIndent;
0820: }
0821:
0822: public void setExpandedIcon(final Icon newIcon) {
0823: expandedIcon = newIcon;
0824: }
0825:
0826: public Icon getExpandedIcon() {
0827: return expandedIcon;
0828: }
0829:
0830: public void setCollapsedIcon(final Icon newIcon) {
0831: collapsedIcon = newIcon;
0832: }
0833:
0834: public Icon getCollapsedIcon() {
0835: return collapsedIcon;
0836: }
0837:
0838: protected void setLargeModel(final boolean b) {
0839: boolean fixedHeightUsed = useFixedHeightLayoutCache();
0840: largeModel = b;
0841:
0842: if (fixedHeightUsed != useFixedHeightLayoutCache()) {
0843: updateComponentHandler();
0844: treeState = createLayoutCache();
0845: configureLayoutCache();
0846: updateSize();
0847: }
0848: }
0849:
0850: protected boolean isLargeModel() {
0851: return largeModel;
0852: }
0853:
0854: protected void setRowHeight(final int rowHeight) {
0855: boolean fixedHeightUsed = useFixedHeightLayoutCache();
0856: this .rowHeight = tree.getRowHeight();
0857: if (fixedHeightUsed != useFixedHeightLayoutCache()) {
0858: updateComponentHandler();
0859: treeState = createLayoutCache();
0860: configureLayoutCache();
0861: } else {
0862: treeState.setRowHeight(rowHeight);
0863: }
0864: updateSize();
0865: }
0866:
0867: protected int getRowHeight() {
0868: return rowHeight;
0869: }
0870:
0871: protected void setCellRenderer(final TreeCellRenderer renderer) {
0872: completeEditing();
0873: updateRenderer();
0874: if (createdCellEditor) {
0875: tree.setCellEditor(null);
0876: }
0877: updateSize();
0878: }
0879:
0880: protected TreeCellRenderer getCellRenderer() {
0881: return currentCellRenderer;
0882: }
0883:
0884: protected void setModel(final TreeModel model) {
0885: if (treeModel != null) {
0886: treeModel.removeTreeModelListener(modelHandler);
0887: }
0888:
0889: treeModel = model;
0890: if (treeModel != null) {
0891: treeModel.addTreeModelListener(modelHandler);
0892: }
0893:
0894: treeState.setModel(treeModel);
0895: updateSize();
0896: resetDrawingCache();
0897: }
0898:
0899: protected TreeModel getModel() {
0900: return treeModel;
0901: }
0902:
0903: protected void setRootVisible(final boolean visible) {
0904: treeState.setRootVisible(visible);
0905: isRootVisible = tree.isRootVisible();
0906:
0907: updateDepthOffset();
0908: updateSize();
0909: }
0910:
0911: protected boolean isRootVisible() {
0912: return isRootVisible;
0913: }
0914:
0915: protected void setShowsRootHandles(final boolean showRoot) {
0916: showsRootHandles = tree.getShowsRootHandles();
0917: updateDepthOffset();
0918: updateSize();
0919: }
0920:
0921: protected boolean getShowsRootHandles() {
0922: return showsRootHandles;
0923: }
0924:
0925: protected void setCellEditor(final TreeCellEditor editor) {
0926: completeEditing();
0927: updateCellEditor();
0928: }
0929:
0930: protected TreeCellEditor getCellEditor() {
0931: return tree.getCellEditor();
0932: }
0933:
0934: protected void setEditable(final boolean editable) {
0935: isEditable = tree.isEditable();
0936: completeEditing();
0937: updateCellEditor();
0938: }
0939:
0940: protected boolean isEditable() {
0941: return isEditable;
0942: }
0943:
0944: protected void setSelectionModel(
0945: final TreeSelectionModel newSelectionModel) {
0946: if (treeSelectionModel != null) {
0947: treeSelectionModel
0948: .removePropertyChangeListener(selectionModelPropertyChangeHandler);
0949: treeSelectionModel
0950: .removeTreeSelectionListener(selectionHandler);
0951: treeSelectionModel.setRowMapper(null);
0952:
0953: treeSelectionModel.clearSelection();
0954: }
0955: treeSelectionModel = newSelectionModel;
0956: if (treeSelectionModel != null) {
0957: treeSelectionModel
0958: .addPropertyChangeListener(selectionModelPropertyChangeHandler);
0959: treeSelectionModel
0960: .addTreeSelectionListener(selectionHandler);
0961: treeSelectionModel.setRowMapper(treeState);
0962: }
0963: resetDrawingCache();
0964: tree.repaint();
0965: }
0966:
0967: protected TreeSelectionModel getSelectionModel() {
0968: return treeSelectionModel;
0969: }
0970:
0971: public Rectangle getPathBounds(final JTree tree, final TreePath path) {
0972: Rectangle result = treeState.getBounds(path, null);
0973: if (result == null) {
0974: return null;
0975: }
0976:
0977: Insets insets = tree.getInsets();
0978: if (tree.getComponentOrientation().isLeftToRight()) {
0979: result.x += insets.left;
0980: } else {
0981: result.x -= insets.right;
0982: }
0983: result.y += insets.top;
0984:
0985: return result;
0986: }
0987:
0988: public TreePath getPathForRow(final JTree tree, final int row) {
0989: return treeState.getPathForRow(row);
0990: }
0991:
0992: public int getRowForPath(final JTree tree, final TreePath path) {
0993: return treeState.getRowForPath(path);
0994: }
0995:
0996: public int getRowCount(final JTree tree) {
0997: return treeState.getRowCount();
0998: }
0999:
1000: public TreePath getClosestPathForLocation(final JTree tree,
1001: final int x, final int y) {
1002: Insets insets = tree.getInsets();
1003: return treeState.getPathClosestTo(x - insets.left, y
1004: - insets.top);
1005: }
1006:
1007: public boolean isEditing(final JTree tree) {
1008: return editingComponent != null;
1009: }
1010:
1011: public boolean stopEditing(final JTree tree) {
1012: if (!isEditing(tree)) {
1013: return false;
1014: }
1015: boolean result = getCellEditor().stopCellEditing();
1016: completeEditing(false, false, true);
1017:
1018: return result;
1019: }
1020:
1021: public void cancelEditing(final JTree tree) {
1022: completeEditing(false, true, false);
1023: }
1024:
1025: public void startEditingAtPath(final JTree tree, final TreePath path) {
1026: startEditing(path, null);
1027: }
1028:
1029: public TreePath getEditingPath(final JTree tree) {
1030: return editingPath;
1031: }
1032:
1033: public void installUI(final JComponent c) {
1034: tree = (JTree) c;
1035: prepareForUIInstall();
1036:
1037: setSelectionModel(tree.getSelectionModel());
1038:
1039: installDefaults();
1040: installComponents();
1041: installListeners();
1042: installKeyboardActions();
1043:
1044: completeUIInstall();
1045: }
1046:
1047: public void uninstallUI(final JComponent c) {
1048: prepareForUIUninstall();
1049:
1050: uninstallKeyboardActions();
1051: uninstallListeners();
1052: uninstallComponents();
1053: uninstallDefaults();
1054:
1055: treeSelectionModel = null;
1056: treeModel = null;
1057:
1058: tree.setCellRenderer(null);
1059: tree.setCellEditor(null);
1060:
1061: completeUIUninstall();
1062: }
1063:
1064: public void paint(final Graphics g, final JComponent c) {
1065: TreeCommons.paintTree(g, paintContext);
1066: }
1067:
1068: public void setPreferredMinSize(final Dimension newSize) {
1069: preferredMinSize = newSize;
1070: }
1071:
1072: public Dimension getPreferredMinSize() {
1073: return preferredMinSize != null ? (Dimension) preferredMinSize
1074: .clone() : null;
1075: }
1076:
1077: public Dimension getPreferredSize(final JComponent c) {
1078: return getPreferredSize(c, true);
1079: }
1080:
1081: public Dimension getPreferredSize(final JComponent c,
1082: final boolean checkConsistancy) {
1083: if (!validCachedPreferredSize) {
1084: updateCachedPreferredSize();
1085: }
1086: return (Dimension) preferredSize.clone();
1087: }
1088:
1089: public Dimension getMinimumSize(final JComponent c) {
1090: Dimension minPrefSize = getPreferredMinSize();
1091: return minPrefSize != null ? (Dimension) minPrefSize.clone()
1092: : new Dimension();
1093: }
1094:
1095: public Dimension getMaximumSize(final JComponent c) {
1096: Dimension prefSize = getPreferredSize(c);
1097: return prefSize != null ? (Dimension) prefSize.clone() : null;
1098: }
1099:
1100: protected void prepareForUIInstall() {
1101: preferredSize = new Dimension();
1102: drawingCache = new Hashtable<javax.swing.tree.TreePath, java.lang.Boolean>();
1103:
1104: treeState = createLayoutCache();
1105: nodeDimensions = createNodeDimensions();
1106: rendererPane = createCellRendererPane();
1107: rendererPane.setVisible(false);
1108:
1109: setModel(tree.getModel());
1110: }
1111:
1112: protected void prepareForUIUninstall() {
1113: }
1114:
1115: protected void completeUIInstall() {
1116: isEditable = tree.isEditable();
1117: largeModel = tree.isLargeModel();
1118: isRootVisible = tree.isRootVisible();
1119: rowHeight = tree.getRowHeight();
1120: showsRootHandles = tree.getShowsRootHandles();
1121:
1122: updateRenderer();
1123: updateCellEditor();
1124: updateDepthOffset();
1125: configureLayoutCache();
1126: updateSize();
1127: }
1128:
1129: protected void completeUIUninstall() {
1130: drawingCache = null;
1131: treeModel = null;
1132:
1133: rendererPane = null;
1134: treeState = null;
1135: nodeDimensions = null;
1136: }
1137:
1138: protected void installDefaults() {
1139: stopEditingInCompleteEditing = true;
1140: lastSelectedRow = -1;
1141:
1142: LookAndFeel.installProperty(tree, "rowHeight", UIManager
1143: .get("Tree.rowHeight"));
1144: LookAndFeel.installProperty(tree, "rootVisible", Boolean.TRUE);
1145: LookAndFeel.installProperty(tree, "scrollsOnExpand", UIManager
1146: .get("Tree.scrollsOnExpand"));
1147: LookAndFeel.installProperty(tree, "expandsSelectedPaths",
1148: Boolean.TRUE);
1149: LookAndFeel.installProperty(tree, "toggleClickCount",
1150: new Integer(2));
1151: LookAndFeel.installProperty(tree, "visibleRowCount",
1152: new Integer(20));
1153:
1154: setHashColor(UIManager.getColor("Tree.hash"));
1155: setLeftChildIndent(UIManager.getInt("Tree.leftChildIndent"));
1156: setRightChildIndent(UIManager.getInt("Tree.rightChildIndent"));
1157:
1158: LookAndFeel.installColorsAndFont(tree, "Tree.background",
1159: "Tree.foreground", "Tree.font");
1160: LookAndFeel.installProperty(tree, "opaque", Boolean.TRUE);
1161:
1162: setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon"));
1163: setExpandedIcon(UIManager.getIcon("Tree.expandedIcon"));
1164:
1165: tree.setTransferHandler(new TreeTransferHandler());
1166: }
1167:
1168: protected void uninstallDefaults() {
1169: Utilities.uninstallColorsAndFont(tree);
1170: tree.setTransferHandler(null);
1171: }
1172:
1173: protected void installListeners() {
1174: propertyChangeHandler = createPropertyChangeListener();
1175: tree.addPropertyChangeListener(propertyChangeHandler);
1176:
1177: keyHandler = createKeyListener();
1178: tree.addKeyListener(keyHandler);
1179:
1180: mouseHandler = createMouseListener();
1181: tree.addMouseListener(mouseHandler);
1182: if (mouseHandler instanceof MouseMotionListener) {
1183: tree
1184: .addMouseMotionListener((MouseMotionListener) mouseHandler);
1185: }
1186:
1187: focusHandler = createFocusListener();
1188: tree.addFocusListener(focusHandler);
1189:
1190: expansionHandler = createTreeExpansionListener();
1191: tree.addTreeExpansionListener(expansionHandler);
1192:
1193: modelHandler = createTreeModelListener();
1194: if (treeModel != null) {
1195: treeModel.addTreeModelListener(modelHandler);
1196: }
1197:
1198: selectionModelPropertyChangeHandler = createSelectionModelPropertyChangeListener();
1199: selectionHandler = createTreeSelectionListener();
1200: if (treeSelectionModel != null) {
1201: treeSelectionModel
1202: .addPropertyChangeListener(selectionModelPropertyChangeHandler);
1203: treeSelectionModel
1204: .addTreeSelectionListener(selectionHandler);
1205: }
1206:
1207: cellEditorHandler = createCellEditorListener();
1208: }
1209:
1210: protected void uninstallListeners() {
1211: tree.removePropertyChangeListener(propertyChangeHandler);
1212: tree.removeKeyListener(keyHandler);
1213: tree.removeMouseListener(mouseHandler);
1214: if (mouseHandler instanceof MouseMotionListener) {
1215: tree
1216: .removeMouseMotionListener((MouseMotionListener) mouseHandler);
1217: }
1218: tree.removeFocusListener(focusHandler);
1219: tree.removeTreeExpansionListener(expansionHandler);
1220: if (treeModel != null) {
1221: treeModel.removeTreeModelListener(modelHandler);
1222: }
1223: if (treeSelectionModel != null) {
1224: treeSelectionModel
1225: .removePropertyChangeListener(selectionModelPropertyChangeHandler);
1226: treeSelectionModel
1227: .removeTreeSelectionListener(selectionHandler);
1228: }
1229:
1230: propertyChangeHandler = null;
1231: keyHandler = null;
1232: mouseHandler = null;
1233: focusHandler = null;
1234: expansionHandler = null;
1235: modelHandler = null;
1236: selectionModelPropertyChangeHandler = null;
1237: selectionHandler = null;
1238: cellEditorHandler = null;
1239: }
1240:
1241: protected void installComponents() {
1242: tree.add(rendererPane);
1243: }
1244:
1245: protected void uninstallComponents() {
1246: tree.remove(rendererPane);
1247: }
1248:
1249: protected void installKeyboardActions() {
1250: BasicTreeKeyboardActions.installKeyboardActions(tree);
1251: }
1252:
1253: protected void uninstallKeyboardActions() {
1254: BasicTreeKeyboardActions.uninstallKeyboardActions(tree);
1255: }
1256:
1257: protected AbstractLayoutCache.NodeDimensions createNodeDimensions() {
1258: return new NodeDimensionsHandler();
1259: }
1260:
1261: protected PropertyChangeListener createPropertyChangeListener() {
1262: return new PropertyChangeHandler();
1263: }
1264:
1265: protected MouseListener createMouseListener() {
1266: return new MouseHandler();
1267: }
1268:
1269: protected FocusListener createFocusListener() {
1270: return new FocusHandler();
1271: }
1272:
1273: protected KeyListener createKeyListener() {
1274: return new KeyHandler();
1275: }
1276:
1277: protected PropertyChangeListener createSelectionModelPropertyChangeListener() {
1278: return new SelectionModelPropertyChangeHandler();
1279: }
1280:
1281: protected TreeSelectionListener createTreeSelectionListener() {
1282: return new TreeSelectionHandler();
1283: }
1284:
1285: protected CellEditorListener createCellEditorListener() {
1286: return new CellEditorHandler();
1287: }
1288:
1289: protected ComponentListener createComponentListener() {
1290: return new ComponentHandler();
1291: }
1292:
1293: protected TreeExpansionListener createTreeExpansionListener() {
1294: return new TreeExpansionHandler();
1295: }
1296:
1297: protected AbstractLayoutCache createLayoutCache() {
1298: return rowHeight > 0 && largeModel ? (AbstractLayoutCache) new FixedHeightLayoutCache()
1299: : (AbstractLayoutCache) new VariableHeightLayoutCache();
1300: }
1301:
1302: protected CellRendererPane createCellRendererPane() {
1303: return new CellRendererPane();
1304: }
1305:
1306: protected TreeCellEditor createDefaultCellEditor() {
1307: DefaultTreeCellRenderer renderer = currentCellRenderer instanceof DefaultTreeCellRenderer ? (DefaultTreeCellRenderer) currentCellRenderer
1308: : null;
1309:
1310: return new DefaultTreeCellEditor(tree, renderer);
1311: }
1312:
1313: protected TreeCellRenderer createDefaultCellRenderer() {
1314: return new DefaultTreeCellRenderer();
1315: }
1316:
1317: protected TreeModelListener createTreeModelListener() {
1318: return new TreeModelHandler();
1319: }
1320:
1321: protected void paintRow(final Graphics g,
1322: final Rectangle clipBounds, final Insets insets,
1323: final Rectangle bounds, final TreePath path, final int row,
1324: final boolean isExpanded, final boolean hasBeenExpanded,
1325: final boolean isLeaf) {
1326:
1327: Component c = getCellRenderer().getTreeCellRendererComponent(
1328: tree,
1329: path.getLastPathComponent(),
1330: treeSelectionModel.isPathSelected(path),
1331: isExpanded,
1332: isLeaf,
1333: row,
1334: tree.isFocusOwner()
1335: && path.equals(tree.getLeadSelectionPath()));
1336: rendererPane.paintComponent(g, c, tree, bounds);
1337: }
1338:
1339: protected void paintExpandControl(final Graphics g,
1340: final Rectangle clipBounds, final Insets insets,
1341: final Rectangle bounds, final TreePath path, final int row,
1342: final boolean isExpanded, final boolean hasBeenExpanded,
1343: final boolean isLeaf) {
1344:
1345: if (!shouldPaintExpandControl(path, row, isExpanded,
1346: hasBeenExpanded, isLeaf)) {
1347: return;
1348: }
1349:
1350: Rectangle pathBounds = tree.getPathBounds(path);
1351: int x = tree.getComponentOrientation().isLeftToRight() ? pathBounds.x
1352: - rightChildIndent
1353: : pathBounds.x + pathBounds.width + rightChildIndent;
1354: int y = pathBounds.y + pathBounds.height / 2;
1355: if (isExpanded) {
1356: drawCentered(tree, g, getExpandedIcon(), x, y);
1357: } else {
1358: drawCentered(tree, g, getCollapsedIcon(), x, y);
1359: }
1360: }
1361:
1362: protected void drawCentered(final Component c, final Graphics g,
1363: final Icon icon, final int x, final int y) {
1364: if (icon == null) {
1365: return;
1366: }
1367:
1368: icon.paintIcon(c, g, x - icon.getIconWidth() / 2, y
1369: - icon.getIconHeight() / 2);
1370: }
1371:
1372: protected void drawDashedHorizontalLine(final Graphics g,
1373: final int y, final int x1, final int x2) {
1374: if (!(g instanceof Graphics2D)) {
1375: paintHorizontalLine(g, tree, y, x1, x2);
1376: return;
1377: }
1378:
1379: drawDashedLine((Graphics2D) g, x1, x2, y, y);
1380: }
1381:
1382: protected void drawDashedVerticalLine(final Graphics g,
1383: final int x, final int y1, final int y2) {
1384: if (!(g instanceof Graphics2D)) {
1385: paintVerticalLine(g, tree, x, y1, y2);
1386: return;
1387: }
1388:
1389: drawDashedLine((Graphics2D) g, x, x, y1, y2);
1390: }
1391:
1392: protected void paintVerticalLine(final Graphics g,
1393: final JComponent c, final int x, final int top,
1394: final int bottom) {
1395: g.drawLine(x, top, x, bottom);
1396: }
1397:
1398: protected void paintHorizontalLine(final Graphics g,
1399: final JComponent c, final int y, final int left,
1400: final int right) {
1401: g.drawLine(left, y, right, y);
1402: }
1403:
1404: protected void paintVerticalPartOfLeg(final Graphics g,
1405: final Rectangle clipBounds, final Insets insets,
1406: final TreePath path) {
1407:
1408: if (!tree.isRootVisible() && !tree.getShowsRootHandles()
1409: && isRootPath(path)) {
1410: return;
1411: }
1412:
1413: g.setColor(getHashColor());
1414:
1415: int offset = getRowX(tree.getRowForPath(path), path
1416: .getPathCount() - 1)
1417: + leftChildIndent;
1418: int x = tree.getComponentOrientation().isLeftToRight() ? insets.left
1419: + offset
1420: : tree.getWidth() - insets.right - offset;
1421:
1422: TreePath pathToLastVisibleChild = getLastChildPath(path);
1423:
1424: Rectangle pathBounds;
1425: int top;
1426: if (tree.getShowsRootHandles() && !tree.isRootVisible()
1427: && isRootPath(path)) {
1428: pathBounds = tree.getPathBounds(getFirstChildPath(path));
1429: top = pathBounds.y + pathBounds.height / 2;
1430: } else {
1431: pathBounds = tree.getPathBounds(path);
1432: top = pathBounds.y + pathBounds.height
1433: + getVerticalLegBuffer();
1434: }
1435:
1436: Rectangle pathToLastVisibleChildBounds = tree
1437: .getPathBounds(pathToLastVisibleChild);
1438: if (pathToLastVisibleChildBounds != null) {
1439: int bottom = pathToLastVisibleChildBounds.y
1440: + pathToLastVisibleChildBounds.height / 2;
1441: paintVerticalLine(g, tree, x, top, bottom);
1442: }
1443: }
1444:
1445: protected void paintHorizontalPartOfLeg(final Graphics g,
1446: final Rectangle clipBounds, final Insets insets,
1447: final Rectangle bounds, final TreePath path, final int row,
1448: final boolean isExpanded, final boolean hasBeenExpanded,
1449: final boolean isLeaf) {
1450:
1451: if (!tree.isRootVisible() && !tree.getShowsRootHandles()
1452: && path.getPathCount() == 2 || isRootPath(path)
1453: && !tree.getShowsRootHandles()) {
1454:
1455: return;
1456: }
1457:
1458: g.setColor(getHashColor());
1459:
1460: int left;
1461: int right;
1462: int offset = getRowX(row, path.getPathCount() - 1)
1463: - rightChildIndent;
1464: if (tree.getComponentOrientation().isLeftToRight()) {
1465: left = insets.left + offset;
1466: right = bounds.x - getHorizontalLegBuffer();
1467: } else {
1468: left = tree.getWidth() - offset - insets.right;
1469: right = bounds.x + bounds.width + getHorizontalLegBuffer();
1470: }
1471: int y = bounds.y + bounds.height / 2;
1472:
1473: paintHorizontalLine(g, tree, y, left, right);
1474: }
1475:
1476: protected boolean shouldPaintExpandControl(final TreePath path,
1477: final int row, final boolean isExpanded,
1478: final boolean hasBeenExpanded, final boolean isLeaf) {
1479:
1480: return !isLeaf
1481: && path.getPathCount() > (tree.getShowsRootHandles() ? 0
1482: : (isRootVisible() ? 1 : 2));
1483: }
1484:
1485: protected int getVerticalLegBuffer() {
1486: return 0;
1487: }
1488:
1489: protected int getHorizontalLegBuffer() {
1490: return 0;
1491: }
1492:
1493: protected int getRowX(final int row, final int depth) {
1494: return totalChildIndent * depthOffset + totalChildIndent
1495: * depth;
1496: }
1497:
1498: protected void updateLayoutCacheExpandedNodes() {
1499: if (treeModel != null && treeModel.getRoot() != null) {
1500: updateExpandedDescendants(new TreePath(treeModel.getRoot()));
1501: }
1502: }
1503:
1504: protected void updateExpandedDescendants(final TreePath path) {
1505: Enumeration expandedPaths = tree.getExpandedDescendants(path);
1506: if (expandedPaths == null) {
1507: return;
1508: }
1509: while (expandedPaths.hasMoreElements()) {
1510: TreePath expandedPath = (TreePath) expandedPaths
1511: .nextElement();
1512: treeState.setExpandedState(expandedPath, true);
1513: }
1514: }
1515:
1516: protected TreePath getLastChildPath(final TreePath parent) {
1517: if (treeModel.isLeaf(parent.getLastPathComponent())) {
1518: return null;
1519: }
1520: int childCount = treeModel.getChildCount(parent
1521: .getLastPathComponent());
1522: if (childCount == 0) {
1523: return null;
1524: }
1525:
1526: return parent.pathByAddingChild(treeModel.getChild(parent
1527: .getLastPathComponent(), childCount - 1));
1528: }
1529:
1530: protected void updateDepthOffset() {
1531: depthOffset = 0;
1532: if (!isRootVisible()) {
1533: depthOffset--;
1534: }
1535: if (getShowsRootHandles()) {
1536: depthOffset++;
1537: }
1538: }
1539:
1540: protected void updateCellEditor() {
1541: if (!isEditable()) {
1542: return;
1543: }
1544: completeEditing();
1545: if (cellEditor != null) {
1546: cellEditor.removeCellEditorListener(cellEditorHandler);
1547: }
1548: cellEditor = tree.getCellEditor();
1549: if (cellEditor == null) {
1550: tree.setCellEditor(createDefaultCellEditor());
1551: createdCellEditor = true;
1552: return;
1553: }
1554: createdCellEditor = false;
1555: if (cellEditor != null) {
1556: cellEditor.addCellEditorListener(cellEditorHandler);
1557: }
1558: }
1559:
1560: protected void updateRenderer() {
1561: currentCellRenderer = tree.getCellRenderer();
1562: if (currentCellRenderer == null) {
1563: tree.setCellRenderer(createDefaultCellRenderer());
1564: createdRenderer = true;
1565: } else {
1566: createdRenderer = false;
1567: }
1568: }
1569:
1570: protected void configureLayoutCache() {
1571: treeState.setModel(treeModel);
1572: treeState.setSelectionModel(treeSelectionModel);
1573: treeState.setNodeDimensions(nodeDimensions);
1574: treeState.setRootVisible(isRootVisible);
1575: treeState.setRowHeight(rowHeight);
1576: treeState.invalidateSizes();
1577: updateLayoutCacheExpandedNodes();
1578: }
1579:
1580: protected void updateSize() {
1581: validCachedPreferredSize = false;
1582: tree.treeDidChange();
1583: }
1584:
1585: protected void updateCachedPreferredSize() {
1586: Insets insets = tree.getInsets();
1587: int preferredWidth;
1588: if (tree.getComponentOrientation().isLeftToRight()) {
1589: preferredWidth = treeState.getPreferredWidth(null);
1590: } else {
1591: preferredWidth = calculatePreferredWidthForRTL();
1592: }
1593: preferredSize = new Dimension(preferredWidth + insets.left
1594: + insets.right, treeState.getPreferredHeight()
1595: + insets.top + insets.bottom);
1596: validCachedPreferredSize = true;
1597: }
1598:
1599: protected void pathWasExpanded(final TreePath path) {
1600: treeState.setExpandedState(path, true);
1601: updateExpandedDescendants(path);
1602: updateSize();
1603:
1604: if (tree.getScrollsOnExpand()
1605: && treeModel.getChildCount(path.getLastPathComponent()) > 0) {
1606: Object firstChild = treeModel.getChild(path
1607: .getLastPathComponent(), 0);
1608: Object lastChild = treeModel.getChild(path
1609: .getLastPathComponent(), treeModel
1610: .getChildCount(path.getLastPathComponent()) - 1);
1611:
1612: int firstRow = tree.getRowForPath(path
1613: .pathByAddingChild(firstChild));
1614: int lastRow = tree.getRowForPath(path
1615: .pathByAddingChild(lastChild));
1616: ensureRowsAreVisible(firstRow, lastRow);
1617: }
1618: }
1619:
1620: protected void pathWasCollapsed(final TreePath path) {
1621: treeState.setExpandedState(path, false);
1622: updateSize();
1623: }
1624:
1625: protected void ensureRowsAreVisible(final int beginRow,
1626: final int endRow) {
1627: Rectangle beginRowBounds = tree.getRowBounds(beginRow);
1628: Rectangle endRowBounds = tree.getRowBounds(endRow);
1629: if (beginRowBounds == null && endRowBounds == null) {
1630: return;
1631: }
1632:
1633: if (beginRowBounds == null) {
1634: tree.scrollRectToVisible(endRowBounds);
1635: } else if (endRowBounds == null) {
1636: tree.scrollRectToVisible(beginRowBounds);
1637: } else {
1638: tree
1639: .scrollRectToVisible(beginRowBounds
1640: .union(endRowBounds));
1641: }
1642: }
1643:
1644: protected void completeEditing() {
1645: completeEditing(tree.getInvokesStopCellEditing(), !tree
1646: .getInvokesStopCellEditing(), false);
1647: }
1648:
1649: protected void completeEditing(final boolean messageStop,
1650: final boolean messageCancel, final boolean messageTree) {
1651: if (!isEditing(tree)) {
1652: return;
1653: }
1654:
1655: if (messageStop) {
1656: getCellEditor().stopCellEditing();
1657: }
1658: if (messageCancel) {
1659: getCellEditor().cancelCellEditing();
1660: }
1661: if (messageTree) {
1662: treeModel.valueForPathChanged(editingPath, getCellEditor()
1663: .getCellEditorValue());
1664: }
1665:
1666: if (!isEditing(tree)) {
1667: return;
1668: }
1669:
1670: tree.remove(editingComponent);
1671: editingComponent = null;
1672: editingPath = null;
1673:
1674: tree.repaint();
1675: tree.requestFocus();
1676: }
1677:
1678: protected boolean startEditing(final TreePath path,
1679: final MouseEvent event) {
1680: if (!isEditable()) {
1681: return false;
1682: }
1683: completeEditing();
1684:
1685: if (path == null) {
1686: return false;
1687: }
1688:
1689: if (!getCellEditor().isCellEditable(event)) {
1690: return false;
1691: }
1692:
1693: if (getCellEditor().shouldSelectCell(event)) {
1694: tree.setSelectionPath(path);
1695: }
1696:
1697: Rectangle bounds = tree.getPathBounds(path);
1698: if (bounds == null) {
1699: return false;
1700: }
1701: int width = bounds.width;
1702:
1703: // TODO: should be another behavior. Should take this information from some cache.
1704: editorHasDifferentSize = true;
1705: if (editorHasDifferentSize) {
1706: bounds.width += 10;
1707: if (bounds.width < 100) {
1708: bounds.width = 100;
1709: }
1710: }
1711:
1712: if (!tree.getComponentOrientation().isLeftToRight()) {
1713: bounds.x = bounds.x - (bounds.width - width);
1714: }
1715:
1716: editingPath = path;
1717: editingRow = tree.getRowForPath(path);
1718: editingComponent = cellEditor.getTreeCellEditorComponent(tree,
1719: editingPath.getLastPathComponent(), tree
1720: .isPathSelected(editingPath), tree
1721: .isExpanded(editingPath), treeModel
1722: .isLeaf(editingPath.getLastPathComponent()),
1723: editingRow);
1724: tree.add(editingComponent);
1725: editingComponent.setBounds(bounds);
1726:
1727: editingComponent.validate();
1728: editingComponent.repaint();
1729: editingComponent.requestFocus();
1730:
1731: return true;
1732: }
1733:
1734: protected void checkForClickInExpandControl(final TreePath path,
1735: final int mouseX, final int mouseY) {
1736: if (isLocationInExpandControl(path, mouseX, mouseY)) {
1737: handleExpandControlClick(path, mouseX, mouseY);
1738: }
1739: }
1740:
1741: protected boolean isLocationInExpandControl(final TreePath path,
1742: final int mouseX, final int mouseY) {
1743: if (treeModel == null || path == null
1744: || treeModel.isLeaf(path.getLastPathComponent())) {
1745: return false;
1746: }
1747: if (!getShowsRootHandles()) {
1748: if (isRootVisible() && path.getPathCount() == 1
1749: || !isRootVisible() && path.getPathCount() <= 2) {
1750:
1751: return false;
1752: }
1753: }
1754: Rectangle pathBounds = tree.getPathBounds(path);
1755: if (mouseY < pathBounds.y
1756: || mouseY > pathBounds.y + pathBounds.height) {
1757: return false;
1758: }
1759:
1760: int expandIconWidth = expandedIcon != null ? expandedIcon
1761: .getIconWidth() : 8;
1762: int startExpandZone;
1763: int endExpandZone;
1764: if (tree.getComponentOrientation().isLeftToRight()) {
1765: startExpandZone = pathBounds.x - rightChildIndent
1766: - expandIconWidth / 2;
1767: endExpandZone = startExpandZone + expandIconWidth;
1768: } else {
1769: startExpandZone = pathBounds.x + pathBounds.width
1770: + rightChildIndent - expandIconWidth / 2;
1771: endExpandZone = startExpandZone + expandIconWidth;
1772: }
1773:
1774: return mouseX >= startExpandZone && mouseX <= endExpandZone;
1775: }
1776:
1777: protected void handleExpandControlClick(final TreePath path,
1778: final int mouseX, final int mouseY) {
1779: toggleExpandState(path);
1780: }
1781:
1782: protected void toggleExpandState(final TreePath path) {
1783: if (treeModel.isLeaf(path.getLastPathComponent())) {
1784: return;
1785: }
1786:
1787: if (tree.isExpanded(path)) {
1788: tree.collapsePath(path);
1789: } else {
1790: tree.expandPath(path);
1791: }
1792: }
1793:
1794: protected boolean isToggleSelectionEvent(final MouseEvent event) {
1795: return SwingUtilities.isLeftMouseButton(event)
1796: && ((event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0);
1797: }
1798:
1799: protected boolean isMultiSelectEvent(final MouseEvent event) {
1800: return SwingUtilities.isLeftMouseButton(event)
1801: && ((event.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0);
1802: }
1803:
1804: protected boolean isToggleEvent(final MouseEvent event) {
1805: return SwingUtilities.isLeftMouseButton(event)
1806: && event.getClickCount() == tree.getToggleClickCount();
1807: }
1808:
1809: protected void selectPathForEvent(final TreePath path,
1810: final MouseEvent event) {
1811: if (isToggleSelectionEvent(event)) {
1812: TreePath anchorPath = tree.getAnchorSelectionPath();
1813: if (tree.isPathSelected(path)) {
1814: tree.removeSelectionPath(path);
1815: } else {
1816: tree.addSelectionPath(path);
1817: }
1818: tree.setLeadSelectionPath(path);
1819: tree.setAnchorSelectionPath(anchorPath);
1820: } else if (isMultiSelectEvent(event)) {
1821: TreePath anchorPath = tree.getAnchorSelectionPath();
1822: if (anchorPath == null) {
1823: tree.setSelectionPath(path);
1824: } else {
1825: int anchorRow = tree.getRowForPath(anchorPath);
1826: int leadRow = tree.getRowForPath(path);
1827: tree.setSelectionInterval(anchorRow, leadRow);
1828: tree.setAnchorSelectionPath(anchorPath);
1829: tree.setLeadSelectionPath(path);
1830: }
1831: } else if (SwingUtilities.isLeftMouseButton(event)) {
1832: tree.setSelectionPath(path);
1833: if (isToggleEvent(event)) {
1834: toggleExpandState(path);
1835: }
1836: }
1837: }
1838:
1839: protected boolean isLeaf(final int row) {
1840: TreePath path = tree.getPathForRow(row);
1841: if (path == null) {
1842: return true;
1843: }
1844: return treeModel.isLeaf(path.getLastPathComponent());
1845: }
1846:
1847: private boolean isRootPath(final TreePath path) {
1848: return path.getPathCount() == 1
1849: && path.getLastPathComponent().equals(
1850: treeModel.getRoot());
1851: }
1852:
1853: private TreePath getFirstChildPath(final TreePath parent) {
1854: if (treeModel.isLeaf(parent.getLastPathComponent())) {
1855: return null;
1856: }
1857:
1858: return parent.pathByAddingChild(treeModel.getChild(parent
1859: .getLastPathComponent(), 0));
1860: }
1861:
1862: private boolean useFixedHeightLayoutCache() {
1863: return rowHeight > 0 && largeModel;
1864: }
1865:
1866: private void updateComponentHandler() {
1867: if (useFixedHeightLayoutCache()) {
1868: if (componentHandler == null) {
1869: componentHandler = createComponentListener();
1870: }
1871: tree.addComponentListener(componentHandler);
1872: } else {
1873: tree.removeComponentListener(componentHandler);
1874: }
1875: }
1876:
1877: private void drawDashedLine(final Graphics2D g, final int x1,
1878: final int x2, final int y1, final int y2) {
1879: Stroke oldStroke = g.getStroke();
1880:
1881: g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
1882: BasicStroke.JOIN_BEVEL, 0, new float[] { 1 }, 0));
1883: g.drawLine(x1, y1, x2, y2);
1884:
1885: g.setStroke(oldStroke);
1886: }
1887:
1888: private void resetDrawingCache() {
1889: drawingCache.clear();
1890: }
1891:
1892: private int calculatePreferredWidthForRTL() {
1893: int result = 0;
1894: TreePath startPath = new TreePath(treeModel.getRoot());
1895: TreePath endPath = null;
1896:
1897: Enumeration paths = treeState.getVisiblePathsFrom(startPath);
1898: if (paths == null) {
1899: return result;
1900: } else {
1901: int currentWidth = tree.getWidth();
1902: while (paths.hasMoreElements()) {
1903: TreePath path = (TreePath) paths.nextElement();
1904:
1905: if (isRootPath(path) && !isRootVisible()) {
1906: continue;
1907: }
1908:
1909: Rectangle pathBounds = tree.getPathBounds(path);
1910: if (pathBounds == null) {
1911: continue;
1912: }
1913:
1914: int width = currentWidth - pathBounds.x;
1915: if (result < width) {
1916: result = width;
1917: }
1918:
1919: if (path.equals(endPath)) {
1920: break;
1921: }
1922: }
1923: }
1924:
1925: return result;
1926: }
1927: }
|