221 |
protected boolean editorHasDifferentSize; |
protected boolean editorHasDifferentSize; |
222 |
|
|
223 |
/** The action listener for the editor's Timer. */ |
/** The action listener for the editor's Timer. */ |
224 |
Timer editorTimer = new EditorUpdateTimer(); |
private Timer editorTimer = new EditorUpdateTimer(); |
225 |
|
|
226 |
|
/** The new value of the node after editing. */ |
227 |
|
private Object newVal; |
228 |
|
|
229 |
/** Listeners */ |
/** Listeners */ |
230 |
private PropertyChangeListener propertyChangeListener; |
private PropertyChangeListener propertyChangeListener; |
231 |
|
|
601 |
if (path != null) |
if (path != null) |
602 |
{ |
{ |
603 |
Object cell = path.getLastPathComponent(); |
Object cell = path.getLastPathComponent(); |
604 |
|
|
605 |
TreeModel mod = tree.getModel(); |
TreeModel mod = tree.getModel(); |
606 |
if (mod != null) |
if (mod != null) |
607 |
{ |
{ |
736 |
*/ |
*/ |
737 |
public boolean isEditing(JTree tree) |
public boolean isEditing(JTree tree) |
738 |
{ |
{ |
739 |
return createdCellEditor; |
return getCellEditor() != null; |
740 |
} |
} |
741 |
|
|
742 |
/** |
/** |
750 |
public boolean stopEditing(JTree tree) |
public boolean stopEditing(JTree tree) |
751 |
{ |
{ |
752 |
if (isEditing(tree)) |
if (isEditing(tree)) |
753 |
return getCellEditor().stopCellEditing(); |
completeEditing(true, false, false); |
754 |
return true; |
return !isEditing(tree); |
755 |
} |
} |
756 |
|
|
757 |
/** |
/** |
1221 |
createdRenderer = true; |
createdRenderer = true; |
1222 |
|
|
1223 |
cellEditor = createDefaultCellEditor(); |
cellEditor = createDefaultCellEditor(); |
1224 |
createdCellEditor = false; |
createdCellEditor = true; |
1225 |
|
|
1226 |
TreeModel mod = tree.getModel(); |
TreeModel mod = tree.getModel(); |
1227 |
setModel(mod); |
setModel(mod); |
1276 |
JTree tree = (JTree) c; |
JTree tree = (JTree) c; |
1277 |
|
|
1278 |
TreeModel mod = tree.getModel(); |
TreeModel mod = tree.getModel(); |
1279 |
|
|
1280 |
if (mod != null) |
if (mod != null) |
1281 |
{ |
{ |
1282 |
Object root = mod.getRoot(); |
Object root = mod.getRoot(); |
1283 |
|
|
1284 |
if (!tree.isRootVisible()) |
if (!tree.isRootVisible()) |
1285 |
tree.expandPath(new TreePath(root)); |
tree.expandPath(new TreePath(root)); |
1286 |
|
|
1287 |
paintRecursive(g, 0, 0, 0, 0, tree, mod, root); |
paintRecursive(g, 0, 0, 0, 0, tree, mod, root); |
1288 |
|
|
1289 |
if (hasControlIcons()) |
if (hasControlIcons()) |
1431 |
getCellEditor().stopCellEditing(); |
getCellEditor().stopCellEditing(); |
1432 |
stopEditingInCompleteEditing = true; |
stopEditingInCompleteEditing = true; |
1433 |
} |
} |
1434 |
else if (messageCancel) |
|
1435 |
|
if (messageCancel) |
1436 |
{ |
{ |
|
stopEditingInCompleteEditing = true; |
|
1437 |
getCellEditor().cancelCellEditing(); |
getCellEditor().cancelCellEditing(); |
1438 |
|
stopEditingInCompleteEditing = true; |
1439 |
} |
} |
1440 |
else if (messageTree) |
|
1441 |
tree.getModel().valueForPathChanged(tree.getLeadSelectionPath(), |
if (messageTree) |
1442 |
editingPath); |
tree.getModel().valueForPathChanged(tree.getLeadSelectionPath(), |
1443 |
|
newVal); |
1444 |
} |
} |
1445 |
|
|
1446 |
/** |
/** |
1473 |
{ |
{ |
1474 |
editingPath = path; |
editingPath = path; |
1475 |
editingRow = tree.getRowForPath(editingPath); |
editingRow = tree.getRowForPath(editingPath); |
1476 |
|
Object val = editingPath.getLastPathComponent(); |
1477 |
cellEditor.addCellEditorListener(cellEditorListener); |
cellEditor.addCellEditorListener(cellEditorListener); |
1478 |
stopEditingInCompleteEditing = false; |
stopEditingInCompleteEditing = false; |
|
Object val = editingPath.getLastPathComponent(); |
|
1479 |
boolean expanded = tree.isExpanded(editingPath); |
boolean expanded = tree.isExpanded(editingPath); |
1480 |
|
|
1481 |
editingComponent = ed.getTreeCellEditorComponent(tree, val, true, |
editingComponent = ed.getTreeCellEditorComponent(tree, val, true, |
1482 |
expanded, |
expanded, |
1483 |
isLeaf(editingRow), |
isLeaf(editingRow), |
1484 |
editingRow); |
editingRow); |
1485 |
|
|
1486 |
editingComponent.getParent().setVisible(true); |
editingComponent.getParent().setVisible(true); |
1487 |
editingComponent.getParent().validate(); |
editingComponent.getParent().validate(); |
1488 |
tree.add(editingComponent.getParent()); |
tree.add(editingComponent.getParent()); |
1639 |
implements ActionListener |
implements ActionListener |
1640 |
{ |
{ |
1641 |
/** |
/** |
1642 |
* Creates a new EditorUpdateTimer object with a default delay of 0.5 |
* Creates a new EditorUpdateTimer object with a default delay of 0.3 |
1643 |
* seconds. |
* seconds. |
1644 |
*/ |
*/ |
1645 |
public EditorUpdateTimer() |
public EditorUpdateTimer() |
1646 |
{ |
{ |
1647 |
super(500, null); |
super(300, null); |
1648 |
addActionListener(this); |
addActionListener(this); |
1649 |
} |
} |
1650 |
|
|
1653 |
*/ |
*/ |
1654 |
public void actionPerformed(ActionEvent ev) |
public void actionPerformed(ActionEvent ev) |
1655 |
{ |
{ |
1656 |
Caret c = ((JTextField) BasicTreeUI.this.editingComponent).getCaret(); |
Caret c = ((JTextField) editingComponent).getCaret(); |
1657 |
if (c != null) |
if (c != null) |
1658 |
c.setVisible(!c.isVisible()); |
c.setVisible(!c.isVisible()); |
1659 |
BasicTreeUI.this.tree.repaint(); |
tree.repaint(); |
1660 |
} |
} |
1661 |
|
|
1662 |
/** |
/** |
1665 |
public void update() |
public void update() |
1666 |
{ |
{ |
1667 |
stop(); |
stop(); |
1668 |
Caret c = ((JTextField) BasicTreeUI.this.editingComponent).getCaret(); |
Caret c = ((JTextField) editingComponent).getCaret(); |
1669 |
if (c != null) |
if (c != null) |
1670 |
{ |
{ |
1671 |
setDelay(c.getBlinkRate()); |
setDelay(c.getBlinkRate()); |
1672 |
if (((JTextField) BasicTreeUI.this.editingComponent).isEditable()) |
if (((JTextField) editingComponent).isEditable()) |
1673 |
start(); |
start(); |
1674 |
else |
else |
1675 |
c.setVisible(false); |
c.setVisible(false); |
1759 |
*/ |
*/ |
1760 |
public void editingStopped(ChangeEvent e) |
public void editingStopped(ChangeEvent e) |
1761 |
{ |
{ |
1762 |
BasicTreeUI.this.editingPath = null; |
newVal = ((JTextField) editingComponent).getText(); |
1763 |
BasicTreeUI.this.editingRow = -1; |
completeEditing(false, false, true); |
1764 |
|
editingPath = null; |
1765 |
|
editingRow = -1; |
1766 |
|
stopEditingInCompleteEditing = false; |
1767 |
if (editingComponent != null) |
if (editingComponent != null) |
1768 |
BasicTreeUI.this.tree.remove(editingComponent.getParent()); |
tree.remove(editingComponent.getParent()); |
1769 |
BasicTreeUI.this.editingComponent = null; |
editingComponent = null; |
1770 |
if (cellEditor != null) |
if (cellEditor != null) |
1771 |
{ |
{ |
1772 |
BasicTreeUI.this.cellEditor.removeCellEditorListener(cellEditorListener); |
cellEditor.removeCellEditorListener(cellEditorListener); |
1773 |
BasicTreeUI.this.cellEditor = null; |
cellEditor = null; |
1774 |
BasicTreeUI.this.createdCellEditor = false; |
createdCellEditor = false; |
1775 |
} |
} |
1776 |
editorTimer.stop(); |
editorTimer.stop(); |
|
tree.repaint(); |
|
1777 |
} |
} |
1778 |
|
|
1779 |
/** |
/** |
1783 |
* @param e is the notification event |
* @param e is the notification event |
1784 |
*/ |
*/ |
1785 |
public void editingCanceled(ChangeEvent e) |
public void editingCanceled(ChangeEvent e) |
1786 |
{ |
{ |
1787 |
BasicTreeUI.this.editingPath = null; |
editingPath = null; |
1788 |
BasicTreeUI.this.editingRow = -1; |
editingRow = -1; |
1789 |
|
stopEditingInCompleteEditing = false; |
1790 |
if (editingComponent != null) |
if (editingComponent != null) |
1791 |
BasicTreeUI.this.tree.remove(editingComponent.getParent()); |
tree.remove(editingComponent.getParent()); |
1792 |
BasicTreeUI.this.editingComponent = null; |
editingComponent = null; |
1793 |
if (cellEditor != null) |
if (cellEditor != null) |
1794 |
{ |
{ |
1795 |
BasicTreeUI.this.cellEditor.removeCellEditorListener(cellEditorListener); |
cellEditor.removeCellEditorListener(cellEditorListener); |
1796 |
BasicTreeUI.this.cellEditor = null; |
cellEditor = null; |
1797 |
BasicTreeUI.this.createdCellEditor = false; |
createdCellEditor = false; |
1798 |
} |
} |
1799 |
editorTimer.stop(); |
editorTimer.stop(); |
1800 |
tree.repaint(); |
tree.repaint(); |
1874 |
*/ |
*/ |
1875 |
public void keyPressed(KeyEvent e) |
public void keyPressed(KeyEvent e) |
1876 |
{ |
{ |
1877 |
TreeModel mod = BasicTreeUI.this.tree.getModel(); |
TreeModel mod = tree.getModel(); |
1878 |
TreePath start = BasicTreeUI.this.tree.getLeadSelectionPath(); |
TreePath start = tree.getLeadSelectionPath(); |
1879 |
Object last = null; |
Object last = null; |
1880 |
if (start != null) |
if (start != null) |
1881 |
last = start.getLastPathComponent(); |
last = start.getLastPathComponent(); |
1886 |
if (e.getKeyCode() == KeyEvent.VK_DOWN |
if (e.getKeyCode() == KeyEvent.VK_DOWN |
1887 |
|| e.getKeyCode() == KeyEvent.VK_KP_DOWN) |
|| e.getKeyCode() == KeyEvent.VK_KP_DOWN) |
1888 |
{ |
{ |
1889 |
Object next = BasicTreeUI.this.getNextVisibleNode(last); |
Object next = getNextVisibleNode(last); |
1890 |
if (next != null) |
if (next != null) |
1891 |
{ |
{ |
1892 |
TreePath newPath = new TreePath(getPathToRoot(next, 0)); |
TreePath newPath = new TreePath(getPathToRoot(next, 0)); |
1893 |
BasicTreeUI.this.selectPath(BasicTreeUI.this.tree, newPath); |
selectPath(tree, newPath); |
1894 |
if (e.isControlDown()) |
if (e.isControlDown()) |
1895 |
tree.setLeadSelectionPath(newPath); |
tree.setLeadSelectionPath(newPath); |
1896 |
else if (!mod.isLeaf(next) && e.isShiftDown()) |
else if (!mod.isLeaf(next) && e.isShiftDown()) |
1897 |
BasicTreeUI.this.tree.expandPath(newPath); |
tree.expandPath(newPath); |
1898 |
} |
} |
1899 |
} |
} |
1900 |
// UP, KP_UP |
// UP, KP_UP |
1901 |
else if (e.getKeyCode() == KeyEvent.VK_UP |
else if (e.getKeyCode() == KeyEvent.VK_UP |
1902 |
|| e.getKeyCode() == KeyEvent.VK_KP_UP) |
|| e.getKeyCode() == KeyEvent.VK_KP_UP) |
1903 |
{ |
{ |
1904 |
Object prev = BasicTreeUI.this.getPreviousVisibleNode(last); |
Object prev = getPreviousVisibleNode(last); |
1905 |
|
|
1906 |
if (prev != null) |
if (prev != null) |
1907 |
{ |
{ |
1908 |
TreePath newPath = new TreePath(getPathToRoot(prev, 0)); |
TreePath newPath = new TreePath(getPathToRoot(prev, 0)); |
1909 |
BasicTreeUI.this.selectPath(BasicTreeUI.this.tree, |
selectPath(tree, new TreePath(getPathToRoot(prev, 0))); |
|
new TreePath(getPathToRoot(prev, |
|
|
0))); |
|
1910 |
if (e.isControlDown()) |
if (e.isControlDown()) |
1911 |
tree.setLeadSelectionPath(newPath); |
tree.setLeadSelectionPath(newPath); |
1912 |
else if (!mod.isLeaf(prev) && e.isShiftDown()) |
else if (!mod.isLeaf(prev) && e.isShiftDown()) |
1913 |
BasicTreeUI.this.tree.expandPath(newPath); |
tree.expandPath(newPath); |
1914 |
} |
} |
1915 |
} |
} |
1916 |
// LEFT, KP_LEFT |
// LEFT, KP_LEFT |
1920 |
TreePath path = new TreePath(getPathToRoot(last, 0)); |
TreePath path = new TreePath(getPathToRoot(last, 0)); |
1921 |
Object p = getParent(mod.getRoot(), last); |
Object p = getParent(mod.getRoot(), last); |
1922 |
|
|
1923 |
if (!mod.isLeaf(last) && BasicTreeUI.this.tree.isExpanded(path)) |
if (!mod.isLeaf(last) && tree.isExpanded(path)) |
1924 |
BasicTreeUI.this.tree.collapsePath(path); |
tree.collapsePath(path); |
1925 |
else if (p != null) |
else if (p != null) |
1926 |
BasicTreeUI.this.selectPath(BasicTreeUI.this.tree, |
selectPath(tree, new TreePath(getPathToRoot(p, 0))); |
|
new TreePath(getPathToRoot(p, 0))); |
|
1927 |
} |
} |
1928 |
// RIGHT, KP_RIGHT |
// RIGHT, KP_RIGHT |
1929 |
else if (e.getKeyCode() == KeyEvent.VK_RIGHT |
else if (e.getKeyCode() == KeyEvent.VK_RIGHT |
1931 |
{ |
{ |
1932 |
TreePath path = new TreePath(getPathToRoot(last, 0)); |
TreePath path = new TreePath(getPathToRoot(last, 0)); |
1933 |
|
|
1934 |
if (!mod.isLeaf(last) && BasicTreeUI.this.tree.isCollapsed(path)) |
if (!mod.isLeaf(last) && tree.isCollapsed(path)) |
1935 |
BasicTreeUI.this.tree.expandPath(path); |
tree.expandPath(path); |
1936 |
else |
else |
1937 |
{ |
{ |
1938 |
Object next = BasicTreeUI.this.getNextVisibleNode(last); |
Object next = getNextVisibleNode(last); |
1939 |
|
|
1940 |
if (next != null) |
if (next != null) |
1941 |
BasicTreeUI.this.selectPath( |
selectPath(tree, new TreePath(getPathToRoot(next, 0))); |
|
BasicTreeUI.this.tree, |
|
|
new TreePath( |
|
|
getPathToRoot( |
|
|
next, |
|
|
0))); |
|
1942 |
} |
} |
1943 |
} |
} |
1944 |
// Enter |
// Enter |
1945 |
else if (e.getKeyCode() == KeyEvent.VK_ENTER) |
else if (e.getKeyCode() == KeyEvent.VK_ENTER) |
1946 |
{ |
{ |
1947 |
TreePath path = new TreePath(getPathToRoot(last, 0)); |
TreePath path = new TreePath(getPathToRoot(last, 0)); |
1948 |
if (!tree.isEditing()) |
if (!mod.isLeaf(last)) |
1949 |
{ |
{ |
1950 |
if (!mod.isLeaf(last)) |
if (tree.isExpanded(path)) |
1951 |
{ |
tree.collapsePath(path); |
1952 |
if (BasicTreeUI.this.tree.isExpanded(path)) |
else |
1953 |
BasicTreeUI.this.tree.collapsePath(path); |
tree.expandPath(path); |
|
else |
|
|
BasicTreeUI.this.tree.expandPath(path); |
|
|
} |
|
1954 |
} |
} |
1955 |
} |
} |
1956 |
} |
} |
1967 |
}// KeyHandler |
}// KeyHandler |
1968 |
|
|
1969 |
/** |
/** |
1970 |
* MouseListener is responsible for updating the selevtion based on mouse |
* MouseListener is responsible for updating the selection based on mouse |
1971 |
* events. |
* events. |
1972 |
*/ |
*/ |
1973 |
public class MouseHandler |
public class MouseHandler |
2067 |
public void mousePressed(MouseEvent e) |
public void mousePressed(MouseEvent e) |
2068 |
{ |
{ |
2069 |
Point click = e.getPoint(); |
Point click = e.getPoint(); |
2070 |
int row = Math.round(click.y / BasicTreeUI.this.getRowHeight()); |
int row = Math.round(click.y / getRowHeight()); |
2071 |
TreePath path = BasicTreeUI.this.getClosestPathForLocation(tree, click.x, |
TreePath path = getClosestPathForLocation(tree, click.x, click.y); |
|
click.y); |
|
2072 |
|
|
2073 |
if (path != null) |
if (path != null) |
2074 |
{ |
{ |
2075 |
boolean inBounds = false; |
boolean inBounds = false; |
2076 |
boolean cntlClick = false; |
boolean cntlClick = false; |
2077 |
Rectangle bounds = BasicTreeUI.this.getPathBounds(BasicTreeUI.this. |
Rectangle bounds = getPathBounds(tree, path); |
2078 |
tree, path); |
|
|
|
|
2079 |
bounds.x -= rightChildIndent - 4; |
bounds.x -= rightChildIndent - 4; |
2080 |
bounds.width += rightChildIndent + 4; |
bounds.width += rightChildIndent + 4; |
2081 |
|
|
2082 |
if (bounds.contains(click.x, click.y)) |
if (bounds.contains(click.x, click.y)) |
2083 |
inBounds = true; |
inBounds = true; |
2084 |
else if (BasicTreeUI.this.hasControlIcons() |
else if (hasControlIcons() |
2085 |
&& (click.x < (bounds.x - rightChildIndent + 5) && |
&& (click.x < (bounds.x - rightChildIndent + 5) && |
2086 |
click.x > (bounds.x - rightChildIndent - 5))) |
click.x > (bounds.x - rightChildIndent - 5))) |
2087 |
cntlClick = true; |
cntlClick = true; |
2088 |
|
|
2089 |
if ((inBounds || cntlClick) && BasicTreeUI.this.tree.isVisible(path)) |
if ((inBounds || cntlClick) && tree.isVisible(path)) |
2090 |
{ |
{ |
2091 |
BasicTreeUI.this.selectPath(BasicTreeUI.this.tree, path); |
selectPath(tree, path); |
2092 |
|
|
2093 |
if ((e.getClickCount() == 2 || cntlClick) |
if ((e.getClickCount() == 2 || cntlClick) |
2094 |
&& !BasicTreeUI.this.isLeaf(row)) |
&& !isLeaf(row)) |
2095 |
{ |
{ |
2096 |
if (BasicTreeUI.this.tree.isExpanded(path)) |
if (tree.isExpanded(path)) |
2097 |
BasicTreeUI.this.tree.collapsePath(path); |
tree.collapsePath(path); |
2098 |
else |
else |
2099 |
BasicTreeUI.this.tree.expandPath(path); |
tree.expandPath(path); |
2100 |
} |
} |
2101 |
|
|
2102 |
if (tree.isEditable()) |
if (tree.isEditable()) |
2103 |
startEditing(path, e); |
startEditing(path, e); |
2104 |
} |
} |
2105 |
} |
} |
2106 |
} |
} |
2314 |
*/ |
*/ |
2315 |
public void treeExpanded(TreeExpansionEvent event) |
public void treeExpanded(TreeExpansionEvent event) |
2316 |
{ |
{ |
2317 |
BasicTreeUI.this.tree.repaint(); |
tree.repaint(); |
2318 |
} |
} |
2319 |
|
|
2320 |
/** |
/** |
2324 |
*/ |
*/ |
2325 |
public void treeCollapsed(TreeExpansionEvent event) |
public void treeCollapsed(TreeExpansionEvent event) |
2326 |
{ |
{ |
2327 |
BasicTreeUI.this.tree.repaint(); |
tree.repaint(); |
2328 |
} |
} |
2329 |
}// TreeExpansionHandler |
}// TreeExpansionHandler |
2330 |
|
|
2437 |
*/ |
*/ |
2438 |
public void treeNodesChanged(TreeModelEvent e) |
public void treeNodesChanged(TreeModelEvent e) |
2439 |
{ |
{ |
2440 |
BasicTreeUI.this.tree.repaint(); |
tree.repaint(); |
2441 |
} |
} |
2442 |
|
|
2443 |
/** |
/** |
2449 |
*/ |
*/ |
2450 |
public void treeNodesInserted(TreeModelEvent e) |
public void treeNodesInserted(TreeModelEvent e) |
2451 |
{ |
{ |
2452 |
BasicTreeUI.this.tree.repaint(); |
tree.repaint(); |
2453 |
} |
} |
2454 |
|
|
2455 |
/** |
/** |
2464 |
*/ |
*/ |
2465 |
public void treeNodesRemoved(TreeModelEvent e) |
public void treeNodesRemoved(TreeModelEvent e) |
2466 |
{ |
{ |
2467 |
BasicTreeUI.this.tree.repaint(); |
tree.repaint(); |
2468 |
} |
} |
2469 |
|
|
2470 |
/** |
/** |
2478 |
*/ |
*/ |
2479 |
public void treeStructureChanged(TreeModelEvent e) |
public void treeStructureChanged(TreeModelEvent e) |
2480 |
{ |
{ |
2481 |
BasicTreeUI.this.tree.repaint(); |
tree.repaint(); |
2482 |
} |
} |
2483 |
}// TreeModelHandler |
}// TreeModelHandler |
2484 |
|
|
2543 |
*/ |
*/ |
2544 |
public void valueChanged(TreeSelectionEvent event) |
public void valueChanged(TreeSelectionEvent event) |
2545 |
{ |
{ |
2546 |
if (BasicTreeUI.this.tree.isEditing() && |
if (tree.isEditing()) |
2547 |
!(event.getNewLeadSelectionPath()).equals |
tree.cancelEditing(); |
|
(BasicTreeUI.this.tree.getLeadSelectionPath())) |
|
|
BasicTreeUI.this.tree.stopEditing(); |
|
2548 |
} |
} |
2549 |
}// TreeSelectionHandler |
}// TreeSelectionHandler |
2550 |
|
|
2639 |
Rectangle getCellBounds(int x, int y, Object cell) |
Rectangle getCellBounds(int x, int y, Object cell) |
2640 |
{ |
{ |
2641 |
if (cell != null) |
if (cell != null) |
2642 |
{ |
{ |
2643 |
String s = cell.toString(); |
String s = cell.toString(); |
2644 |
Font f = tree.getFont(); |
Font f = tree.getFont(); |
2645 |
FontMetrics fm = tree.getToolkit().getFontMetrics(f); |
FontMetrics fm = tree.getToolkit().getFontMetrics(f); |
2728 |
if (dtcr == null) |
if (dtcr == null) |
2729 |
dtcr = createDefaultCellRenderer(); |
dtcr = createDefaultCellRenderer(); |
2730 |
|
|
2731 |
|
int row = getRowForPath(tree, curr); |
2732 |
|
|
2733 |
Component c = dtcr.getTreeCellRendererComponent(tree, node, |
Component c = dtcr.getTreeCellRendererComponent(tree, node, |
2734 |
selected, expanded, |
selected, expanded, |
2735 |
isLeaf, 0, false); |
isLeaf, row, false); |
2736 |
|
|
2737 |
rendererPane.paintComponent(g, c, c.getParent(), |
rendererPane.paintComponent(g, c, c.getParent(), |
2738 |
getCellBounds(x, y, node)); |
getCellBounds(x, y, node)); |