void (Graphics g, JComponent c) { AbstractLayoutCache cache = treeState; // TODO: treeState ??? if (cache == null) return; JTree tree = (JTree)c; if (!isValid(tree)) return; g = g.create(); try { painting.set(true); Rectangle paintBounds = g.getClipBounds(); Insets insets = tree.getInsets(); TreePath path = cache.getPathClosestTo(0, paintBounds.y - insets.top); int row = cache.getRowForPath(path); if (row >= 0) { boolean dark = isDark(JBUI.CurrentTheme.Tree.BACKGROUND); Control.Painter painter = getPainter(tree); Rectangle buffer = new Rectangle(); RenderingHelper helper = new RenderingHelper(tree); int maxPaintY = paintBounds.y + paintBounds.height; while (path != null) { Rectangle bounds = cache.getBounds(path, buffer); if (bounds == null) break; // something goes wrong bounds.y += insets.top; int depth = TreeUtil.getNodeDepth(tree, path); Object value = path.getLastPathComponent(); boolean leaf = isLeaf(value); boolean expanded = !leaf && cache.getExpandedState(path); boolean selected = tree.isRowSelected(row); boolean focused = RenderingUtil.isFocused(tree); boolean lead = focused && row == getLeadSelectionRow(); boolean selectedControl = selected && focused; Color background = isSeparator(value) ? null : getBackground(tree, path, row, selected); if (background != null) { g.setColor(background); if (g instanceof Graphics2D && ExperimentalUI.isNewUI() && is("ide.experimental.ui.tree.selection") && !(tree instanceof PlainSelectionTree) && (selected || row == TreeHoverListener.getHoveredRow(tree))) { int borderOffset = JBUI.scale(12); Control control = getControl(c, path); int rendererOffset = painter.getRendererOffset(control, depth, leaf); int controlOffset = painter.getControlOffset(control, depth, leaf); int left = Math.min(helper.getX() + borderOffset, insets.left + (controlOffset < 0 ? rendererOffset : controlOffset)); int treeRight = helper.getX() + helper.getWidth() - borderOffset; int right = treeRight; if (helper.isShrinkingSelectionDisabled(row)) { int rendererRight = insets.left + rendererOffset + bounds.width + JBUI.scale(4); right = Math.max(treeRight, rendererRight); } int[] rows = tree.getSelectionRows(); boolean shouldPaintTop = false; boolean shouldPaintBottom = false; if (rows != null && rows.length > 1) { for (int selectedRow : rows) { int delta = selectedRow - row; if (delta == 1) shouldPaintBottom = true; if (delta == -1) shouldPaintTop = true; if (shouldPaintTop && shouldPaintBottom) break; } } if (shouldPaintTop && shouldPaintBottom) { g.fillRect(left, bounds.y, right - left, bounds.height); } else { int arc = JBUI.CurrentTheme.Tree.ARC.get(); FILL.paint((Graphics2D)g, left, bounds.y, right - left, bounds.height, arc); if (shouldPaintTop) { g.fillRect(left, bounds.y, right - left, arc); } if (shouldPaintBottom) { g.fillRect(left, bounds.y + bounds.height - arc, right - left, arc); } } } else { g.fillRect(helper.getX(), bounds.y, helper.getWidth(), bounds.height); } if (selectedControl && !dark && !isDark(background)) { selectedControl = false; } } Control control = getControl(c, path); int offset = painter.getRendererOffset(control, depth, leaf); painter.paint(tree, g, insets.left, bounds.y, offset, bounds.height, control, depth, leaf, expanded, selectedControl); // TODO: editingComponent, editingRow ??? if (editingComponent == null || editingRow != row) { int width = helper.getX() + helper.getWidth() - insets.left - offset; if (width > 0) { Component component = getRenderer(tree, value, selected, expanded, leaf, row, lead); if (isSeparator(component)) { int x; if (ExperimentalUI.isNewUI()) { Insets separatorInsets = JBUI.CurrentTheme.Popup.separatorInsets(); x = paintBounds.x + separatorInsets.left; width = paintBounds.width - separatorInsets.left - separatorInsets.right; } else { int separatorWidthGap = JBUI.scale(SeparatorWithText.DEFAULT_H_GAP); x = paintBounds.x + separatorWidthGap; width = paintBounds.width - 2 * separatorWidthGap; } if (width > 0) { rendererPane.paintComponent(g, component, tree, x, bounds.y, width, bounds.height, true); } } else if (component != null) { width -= helper.getRightMargin(); // shrink a long node according to the right margin if (width < bounds.width && helper.isRendererShrinkingDisabled(row)) { width = bounds.width; // disable shrinking a long nodes } if (width > 0) { setBackground(tree, component, background, false); rendererPane.paintComponent(g, component, tree, insets.left + offset, bounds.y, width, bounds.height, true); } } } if (!isMac && lead && g instanceof Graphics2D) { if (!selected) { g.setColor(getBackground(tree, path, row, true)); DRAW.paint((Graphics2D)g, helper.getX(), bounds.y, helper.getWidth(), bounds.height, 0); } else if (isLeadSelectionNeeded(tree, row)) { g.setColor(RenderingUtil.getBackground(tree)); DRAW.paint((Graphics2D)g, helper.getX() + 1, bounds.y + 1, helper.getWidth() - 2, bounds.height - 2, 0); } } JTree.DropLocation dropLocation = tree.getDropLocation(); if (dropLocation != null && g instanceof Graphics2D && path.equals(dropLocation.getPath())) { // paint a dragged tree path in accordance to Highlighters.RectangleHighlighter g.setColor(JBUI.CurrentTheme.DragAndDrop.ROW_BACKGROUND); FILL.paint((Graphics2D)g, helper.getX(), bounds.y, helper.getWidth(), bounds.height, 0); g.setColor(JBUI.CurrentTheme.DragAndDrop.BORDER_COLOR); DRAW.paint((Graphics2D)g, helper.getX(), bounds.y, helper.getWidth(), bounds.height, 0); } } if ((bounds.y + bounds.height) >= maxPaintY) break; path = cache.getPathForRow(++row); } } } finally { g.dispose(); painting.set(false); removeCachedRenderers(); } }