@Override
  public void paint(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(HORIZONTAL_SELECTION_OFFSET);
              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();
    }
  }
@Override
  public void paint(Graphics g, JComponent c)