JComponent () { myTree = new Tree(); myTree.setBorder(BORDER); myTree.setShowsRootHandles(true); myTree.setRootVisible(false); myTree.setExpandableItemsEnabled(false); FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, true, true, false, true) { @Override public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { if (!super.isFileVisible(file, showHiddenFiles)) return false; if (myRoots.contains(file)) return false; ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); return !changeListManager.isIgnoredFile(file) && !changeListManager.isUnversioned(file); } }; descriptor.withRoots(new ArrayList<>(myRoots)).withShowHiddenFiles(true).withHideIgnored(true); final MyCheckboxTreeCellRenderer cellRenderer = new MyCheckboxTreeCellRenderer(mySelectionManager, myModulesSet, myProject, myTree, myRoots); FileSystemTreeImpl fileSystemTree = new FileSystemTreeImpl(myProject, descriptor, myTree, cellRenderer, null, o -> { DefaultMutableTreeNode lastPathComponent = ((DefaultMutableTreeNode)o.getLastPathComponent()); Object uo = lastPathComponent.getUserObject(); if (uo instanceof FileNodeDescriptor) { VirtualFile file = ((FileNodeDescriptor)uo).getElement().getFile(); String module = myModulesSet.get(file); if (module != null) return module; return file == null ? "" : file.getName(); } return o.toString(); }) { @Override protected Comparator<? super NodeDescriptor<?>> getFileComparator() { return (o1, o2) -> { if (o1 instanceof FileNodeDescriptor && o2 instanceof FileNodeDescriptor) { VirtualFile f1 = ((FileNodeDescriptor)o1).getElement().getFile(); VirtualFile f2 = ((FileNodeDescriptor)o2).getElement().getFile(); boolean isDir1 = f1.isDirectory(); boolean isDir2 = f2.isDirectory(); if (isDir1 != isDir2) return isDir1 ? -1 : 1; return f1.getPath().compareToIgnoreCase(f2.getPath()); } return o1.getIndex() - o2.getIndex(); }; } }; new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent e, int clickCount) { int row = myTree.getRowForLocation(e.getX(), e.getY()); if (row < 0) return false; Object o = myTree.getPathForRow(row).getLastPathComponent(); if (getTreeRoot() == o || getFile(o) == null) return false; Rectangle rowBounds = myTree.getRowBounds(row); cellRenderer.setBounds(rowBounds); Rectangle checkBounds = cellRenderer.myCheckbox.getBounds(); checkBounds.setLocation(rowBounds.getLocation()); if (checkBounds.height == 0) checkBounds.height = rowBounds.height; if (checkBounds.contains(e.getPoint())) { mySelectionManager.toggleSelection((DefaultMutableTreeNode)o); myTree.revalidate(); myTree.repaint(); } return true; } }.installOn(myTree); myTree.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_SPACE) { TreePath[] paths = myTree.getSelectionPaths(); if (paths == null) return; for (TreePath path : paths) { if (path == null) continue; Object o = path.getLastPathComponent(); if (getTreeRoot() == o || getFile(o) == null) return; mySelectionManager.toggleSelection((DefaultMutableTreeNode)o); } myTree.revalidate(); myTree.repaint(); e.consume(); } } }); JBPanel panel = new JBPanel(new BorderLayout()); panel.add(new JBScrollPane(fileSystemTree.getTree()), BorderLayout.CENTER); final JLabel selectedLabel = new JLabel(""); selectedLabel.setBorder(JBUI.Borders.empty(2, 0)); panel.add(selectedLabel, BorderLayout.SOUTH); mySelectionManager.setSelectionChangeListener(new PlusMinus<>() { @Override public void plus(VirtualFile virtualFile) { mySelectedFiles.add(virtualFile); recalculateErrorText(); } private void recalculateErrorText() { checkEmpty(); if (mySelectionManager.canAddSelection()) { selectedLabel.setText(""); } else { HtmlChunk.Element errorText = HtmlChunk.text("(" + VcsLogBundle.message("vcs.log.filters.structure.max.selected.error.message", MAX_FOLDERS) + ")") .wrapWith(HtmlChunk.tag("font").attr("color", "red")); selectedLabel.setText(new HtmlBuilder() .appendRaw((VcsLogBundle.message("vcs.log.filters.structure.label", errorText))) .wrapWith(HtmlChunk.html()) .toString()); } selectedLabel.revalidate(); } @Override public void minus(VirtualFile virtualFile) { mySelectedFiles.remove(virtualFile); recalculateErrorText(); } }); panel.setPreferredSize(JBUI.size(400, 300)); return panel; }