JScrollPane (final MutableTreeNode myTreeRoot) { ColumnInfo[] columnInfos = {new ClassColumnInfo(), new PropertyTypeColumnInfo()}; myTreeTableModel = new ListTreeTableModelOnColumns(myTreeRoot, columnInfos); myTreeTable = new MyTreeTable(myTreeTableModel); TreeTableSpeedSearch.installOn(myTreeTable, o -> { final Object node = o.getLastPathComponent(); if (node instanceof DefaultMutableTreeNode) { final Object object = ((DefaultMutableTreeNode)node).getUserObject(); if (object instanceof DNamedElement) { return ((DNamedElement)object).getName(); } } return ""; }); PopupHandler.installPopupMenu(myTreeTable, new DefaultActionGroup(new RemoveDynamicAction()), "DynamicToolWindowWrapperPopup"); final MyColoredTreeCellRenderer treeCellRenderer = new MyColoredTreeCellRenderer(); myTreeTable.setDefaultRenderer(String.class, new TableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value instanceof String) { try { final PsiType type = JavaPsiFacade.getElementFactory(myProject).createTypeFromText((String)value, null); String shortName = type.getPresentableText(); return new JLabel(shortName); } catch (IncorrectOperationException e) { LOG.debug("Type cannot be created", e); } return new JLabel(QuickfixUtil.shortenType((String)value)); } return new JLabel(); } }); myTreeTable.setTreeCellRenderer(treeCellRenderer); myTreeTable.setRootVisible(false); myTreeTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); final MyPropertyTypeCellEditor typeCellEditor = new MyPropertyTypeCellEditor(); typeCellEditor.addCellEditorListener(new CellEditorListener() { @Override public void editingStopped(ChangeEvent e) { final TreeTableTree tree = getTree(); String newTypeValue = ((MyPropertyTypeCellEditor)e.getSource()).getCellEditorValue(); if (tree == null) { myTreeTable.editingStopped(e); return; } try { final PsiType type = JavaPsiFacade.getElementFactory(myProject).createTypeFromText(newTypeValue, null); newTypeValue = type.getCanonicalText(); } catch (IncorrectOperationException ex) { //do nothing in case bad string is entered } final TreePath editingTypePath = tree.getSelectionPath(); if (editingTypePath == null) return; final TreePath editingClassPath = editingTypePath.getParentPath(); Object oldTypeValue = myTreeTable.getValueAt(tree.getRowForPath(editingTypePath), TYPE_COLUMN); if (!(oldTypeValue instanceof String)) { myTreeTable.editingStopped(e); return; } final Object editingPropertyObject = myTreeTable.getValueAt(tree.getRowForPath(editingTypePath), CLASS_OR_ELEMENT_NAME_COLUMN); final Object editingClassObject = myTreeTable.getValueAt(tree.getRowForPath(editingClassPath), CLASS_OR_ELEMENT_NAME_COLUMN); if (!(editingPropertyObject instanceof DItemElement dynamicElement) || !(editingClassObject instanceof DClassElement)) { myTreeTable.editingStopped(e); return; } final String name = dynamicElement.getName(); final String className = ((DClassElement)editingClassObject).getName(); if (dynamicElement instanceof DPropertyElement) { DynamicManager.getInstance(myProject).replaceDynamicPropertyType(className, name, (String)oldTypeValue, newTypeValue); } else if (dynamicElement instanceof DMethodElement) { final List<ParamInfo> myPairList = ((DMethodElement)dynamicElement).getPairs(); DynamicManager.getInstance(myProject).replaceDynamicMethodType(className, name, myPairList, (String)oldTypeValue, newTypeValue); } } @Override public void editingCanceled(ChangeEvent e) { myTreeTable.editingCanceled(e); } }); RefactoringListenerManager.getInstance(myProject).addListenerProvider(new RefactoringElementListenerProvider() { @Override @Nullable public RefactoringElementListener getListener(final PsiElement element) { if (element instanceof PsiClass) { final String qualifiedName = ((PsiClass)element).getQualifiedName(); return new RefactoringElementListener() { @Override public void elementMoved(@NotNull PsiElement newElement) { renameElement(qualifiedName, newElement); } @Override public void elementRenamed(@NotNull PsiElement newElement) { renameElement(qualifiedName, newElement); } private void renameElement(String oldClassName, PsiElement newElement) { if (newElement instanceof PsiClass) { final String newClassName = ((PsiClass)newElement).getQualifiedName(); final DRootElement rootElement = DynamicManager.getInstance(myProject).getRootElement(); final DClassElement oldClassElement = rootElement.getClassElement(oldClassName); final TreeNode oldClassNode = TreeUtil.findNodeWithObject((DefaultMutableTreeNode)myTreeRoot, oldClassElement); DynamicManager.getInstance(myProject).replaceClassName(oldClassElement, newClassName); myTreeTableModel.nodeChanged(oldClassNode); } } }; } return null; } }); myTreeTable.setDefaultEditor(String.class, typeCellEditor); myTreeTable.registerKeyboardAction(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { final int selectionRow = myTreeTable.getTree().getLeadSelectionRow(); myTreeTable.editCellAt(selectionRow, CLASS_OR_ELEMENT_NAME_COLUMN, event); } }, KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), JComponent.WHEN_FOCUSED); myTreeTable.registerKeyboardAction(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { final int selectionRow = myTreeTable.getTree().getLeadSelectionRow(); myTreeTable.editCellAt(selectionRow, TYPE_COLUMN, event); } }, KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.CTRL_MASK), JComponent.WHEN_FOCUSED); myTreeTable.getTree().setShowsRootHandles(true); myTreeTable.getTableHeader().setReorderingAllowed(false); myTreeTable.setVisibleRowCount(10); myTreeTable.getColumn(myColumnNames[CLASS_OR_ELEMENT_NAME_COLUMN]).setPreferredWidth(200); myTreeTable.getColumn(myColumnNames[TYPE_COLUMN]).setPreferredWidth(160); JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTreeTable); scrollPane.setPreferredSize(JBUI.size(600, 400)); return scrollPane; }