9b7bfa5b85010e47d327c465bf034b5d5ab239b6
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / annotator / intentions / dynamic / DynamicToolWindowWrapper.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.groovy.annotator.intentions.dynamic;
17
18 import com.intellij.ide.DeleteProvider;
19 import com.intellij.openapi.actionSystem.*;
20 import com.intellij.openapi.components.ServiceManager;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.editor.Document;
23 import com.intellij.openapi.editor.markup.TextAttributes;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.ui.DialogWrapper;
26 import com.intellij.openapi.ui.Messages;
27 import com.intellij.openapi.wm.IdeFocusManager;
28 import com.intellij.openapi.wm.ToolWindow;
29 import com.intellij.openapi.wm.ToolWindowAnchor;
30 import com.intellij.openapi.wm.ToolWindowManager;
31 import com.intellij.psi.*;
32 import com.intellij.psi.search.GlobalSearchScope;
33 import com.intellij.refactoring.listeners.RefactoringElementListener;
34 import com.intellij.refactoring.listeners.RefactoringElementListenerProvider;
35 import com.intellij.refactoring.listeners.RefactoringListenerManager;
36 import com.intellij.ui.*;
37 import com.intellij.ui.content.Content;
38 import com.intellij.ui.content.ContentManager;
39 import com.intellij.ui.treeStructure.treetable.ListTreeTableModelOnColumns;
40 import com.intellij.ui.treeStructure.treetable.TreeTable;
41 import com.intellij.ui.treeStructure.treetable.TreeTableModel;
42 import com.intellij.ui.treeStructure.treetable.TreeTableTree;
43 import com.intellij.util.IncorrectOperationException;
44 import com.intellij.util.NullableFunction;
45 import com.intellij.util.containers.ContainerUtil;
46 import com.intellij.util.ui.AbstractTableCellEditor;
47 import com.intellij.util.ui.ColumnInfo;
48 import com.intellij.util.ui.UIUtil;
49 import com.intellij.util.ui.tree.TreeUtil;
50 import org.jetbrains.annotations.NonNls;
51 import org.jetbrains.annotations.NotNull;
52 import org.jetbrains.annotations.Nullable;
53 import org.jetbrains.plugins.groovy.GroovyBundle;
54 import org.jetbrains.plugins.groovy.GroovyFileType;
55 import org.jetbrains.plugins.groovy.GroovyIcons;
56 import org.jetbrains.plugins.groovy.annotator.intentions.QuickfixUtil;
57 import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.elements.*;
58 import org.jetbrains.plugins.groovy.debugger.fragments.GroovyCodeFragment;
59 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
60 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
61
62 import javax.swing.*;
63 import javax.swing.event.CellEditorListener;
64 import javax.swing.event.ChangeEvent;
65 import javax.swing.table.TableCellRenderer;
66 import javax.swing.tree.*;
67 import java.awt.*;
68 import java.awt.event.ActionEvent;
69 import java.awt.event.ActionListener;
70 import java.awt.event.InputEvent;
71 import java.awt.event.KeyEvent;
72 import java.util.ArrayList;
73 import java.util.Collection;
74 import java.util.List;
75 import java.util.Set;
76
77 /**
78  * User: Dmitry.Krasilschikov
79  * Date: 09.01.2008
80  */
81 public class DynamicToolWindowWrapper {
82   private final Project myProject;
83   private ToolWindow myToolWindow = null;
84
85   public DynamicToolWindowWrapper(Project project) {
86     myProject = project;
87   }
88
89   public static DynamicToolWindowWrapper getInstance(Project project) {
90     return ServiceManager.getService(project, DynamicToolWindowWrapper.class);
91   }
92
93   public static final String DYNAMIC_TOOLWINDOW_ID = GroovyBundle.message("dynamic.tool.window.id");
94   private JPanel myTreeTablePanel;
95   private JPanel myBigPanel;
96   private ListTreeTableModelOnColumns myTreeTableModel;
97
98   private static final int CLASS_OR_ELEMENT_NAME_COLUMN = 0;
99   private static final int TYPE_COLUMN = 1;
100
101   private static final String[] myColumnNames = {"Dynamic Element", "Type"};
102
103   private MyTreeTable myTreeTable;
104
105   private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicToolWindowWrapper");
106
107   public TreeTable getTreeTable() {
108     getToolWindow();
109
110     return myTreeTable;
111   }
112
113   public ToolWindow getToolWindow() {
114     if (myToolWindow == null) {
115       myToolWindow = ToolWindowManager.getInstance(myProject).registerToolWindow(DYNAMIC_TOOLWINDOW_ID, true, ToolWindowAnchor.RIGHT);
116       myToolWindow.setIcon(GroovyIcons.DYNAMIC_PROPERTY_TOOL_WINDOW_ICON);
117       myToolWindow.setTitle(GroovyBundle.message("dynamic.window"));
118       myToolWindow.setToHideOnEmptyContent(true);
119
120       final JPanel panel = buildBigPanel();
121       final ContentManager contentManager = myToolWindow.getContentManager();
122       final Content content = contentManager.getFactory().createContent(panel, "", false);
123       content.setPreferredFocusableComponent(myTreeTable);
124       contentManager.addContent(content);
125     }
126
127     return myToolWindow;
128   }
129
130   private JPanel buildBigPanel() {
131     myBigPanel = new JPanel(new BorderLayout());
132     myBigPanel.setBackground(UIUtil.getFieldForegroundColor());
133
134     final DynamicFilterComponent filter = new DynamicFilterComponent(GroovyBundle.message("dynamic.toolwindow.property.filter"), 10);
135     filter.setBackground(UIUtil.getLabelBackground());
136
137     myBigPanel.add(new JLabel(GroovyBundle.message("dynamic.toolwindow.search.elements")), BorderLayout.NORTH);
138     myBigPanel.add(filter, BorderLayout.NORTH);
139
140     myTreeTablePanel = new JPanel(new BorderLayout());
141     rebuildTreePanel();
142
143     myBigPanel.add(myTreeTablePanel);
144     myBigPanel.setPreferredSize(new Dimension(200, myBigPanel.getHeight()));
145
146     myBigPanel.revalidate();
147     return myBigPanel;
148   }
149
150   public void rebuildTreePanel() {
151     DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
152     buildTree(rootNode);
153
154     rebuildTreeView(rootNode, false);
155   }
156
157   private void rebuildTreeView(DefaultMutableTreeNode root, boolean expandAll) {
158     PsiDocumentManager.getInstance(myProject).commitAllDocuments();
159
160     myTreeTablePanel.removeAll();
161
162     final JScrollPane treeTable = createTable(root);
163
164     if (expandAll) {
165       TreeUtil.expandAll(myTreeTable.getTree());
166     }
167
168     myTreeTablePanel.add(treeTable);
169     myTreeTablePanel.revalidate();
170   }
171
172   private DefaultMutableTreeNode buildTree(DefaultMutableTreeNode rootNode) {
173     final Collection<DClassElement> containingClasses = DynamicManager.getInstance(myProject).getAllContainingClasses();
174
175     DefaultMutableTreeNode containingClassNode;
176     for (DClassElement containingClassElement : containingClasses) {
177       containingClassNode = new DefaultMutableTreeNode(containingClassElement);
178
179       final Collection<DPropertyElement> properties =
180         DynamicManager.getInstance(myProject).findDynamicPropertiesOfClass(containingClassElement.getName());
181
182 //      if (properties.length == 0) continue;
183
184       DefaultMutableTreeNode propertyTreeNode;
185       for (DPropertyElement property : properties) {
186
187         propertyTreeNode = new DefaultMutableTreeNode(property);
188         containingClassNode.add(propertyTreeNode);
189       }
190
191       DefaultMutableTreeNode methodTreeNode;
192       final Set<DMethodElement> methods = containingClassElement.getMethods();
193
194       for (DMethodElement methodElement : methods) {
195         final String[] psiTypes = QuickfixUtil.getArgumentsTypes(methodElement.getPairs());
196
197         final DMethodElement method = DynamicManager.getInstance(myProject)
198           .findConcreteDynamicMethod(containingClassElement.getName(), methodElement.getName(), psiTypes);
199
200         methodTreeNode = new DefaultMutableTreeNode(method);
201         containingClassNode.add(methodTreeNode);
202       }
203
204       rootNode.add(containingClassNode);
205     }
206     return rootNode;
207   }
208
209   private JScrollPane createTable(final MutableTreeNode myTreeRoot) {
210     ColumnInfo[] columnInfos =
211       {new ClassColumnInfo(myColumnNames[CLASS_OR_ELEMENT_NAME_COLUMN]), new PropertyTypeColumnInfo(myColumnNames[TYPE_COLUMN])};
212
213     myTreeTableModel = new ListTreeTableModelOnColumns(myTreeRoot, columnInfos);
214
215     myTreeTable = new MyTreeTable(myTreeTableModel);
216
217     DefaultActionGroup group = new DefaultActionGroup();
218     group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_DELETE));
219     PopupHandler.installUnknownPopupHandler(myTreeTable, group, ActionManager.getInstance());
220
221     final MyColoredTreeCellRenderer treeCellRenderer = new MyColoredTreeCellRenderer();
222
223     myTreeTable.setDefaultRenderer(String.class, new TableCellRenderer() {
224       public Component getTableCellRendererComponent(JTable table,
225                                                      Object value,
226                                                      boolean isSelected,
227                                                      boolean hasFocus,
228                                                      int row,
229                                                      int column) {
230         if (value instanceof String) {
231           final GrTypeElement typeElement;
232           try {
233             typeElement = GroovyPsiElementFactory.getInstance(myProject).createTypeElement(((String)value));
234             if (typeElement != null){
235               String shortName = typeElement.getType().getPresentableText();
236               return new JLabel(shortName);
237             }
238           }
239           catch (IncorrectOperationException e) {
240             LOG.debug("Type cannot be created", e);
241           }
242           return new JLabel(QuickfixUtil.shortenType((String)value));
243         }
244
245         return new JLabel();
246       }
247     });
248
249     myTreeTable.setTreeCellRenderer(treeCellRenderer);
250
251     myTreeTable.setRootVisible(false);
252     myTreeTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
253
254     final MyPropertyTypeCellEditor typeCellEditor = new MyPropertyTypeCellEditor();
255
256     typeCellEditor.addCellEditorListener(new CellEditorListener() {
257       public void editingStopped(ChangeEvent e) {
258         final TreeTableTree tree = getTree();
259
260         String newTypeValue = ((MyPropertyTypeCellEditor)e.getSource()).getCellEditorValue();
261
262         if (newTypeValue == null || tree == null) {
263           myTreeTable.editingStopped(e);
264           return;
265         }
266
267         try {
268           GrTypeElement typeElement = GroovyPsiElementFactory.getInstance(myProject).createTypeElement(newTypeValue);
269           if (typeElement != null) {
270             String canonical = typeElement.getType().getCanonicalText();
271             if (canonical != null) newTypeValue = canonical;
272           }
273         }
274         catch (IncorrectOperationException ex) {
275           //do nothing in case bad string is entered
276         }
277
278         final TreePath editingTypePath = tree.getSelectionPath();
279         if (editingTypePath == null) return;
280
281         final TreePath editingClassPath = editingTypePath.getParentPath();
282
283         Object oldTypeValue = myTreeTable.getValueAt(tree.getRowForPath(editingTypePath), TYPE_COLUMN);
284
285         if (!(oldTypeValue instanceof String)) {
286           myTreeTable.editingStopped(e);
287           return;
288         }
289
290         final Object editingPropertyObject = myTreeTable.getValueAt(tree.getRowForPath(editingTypePath), CLASS_OR_ELEMENT_NAME_COLUMN);
291         final Object editingClassObject = myTreeTable.getValueAt(tree.getRowForPath(editingClassPath), CLASS_OR_ELEMENT_NAME_COLUMN);
292
293         if (!(editingPropertyObject instanceof DItemElement) || !(editingClassObject instanceof DClassElement)) {
294           myTreeTable.editingStopped(e);
295           return;
296         }
297
298         final DItemElement dynamicElement = (DItemElement)editingPropertyObject;
299         final String name = dynamicElement.getName();
300         final String className = ((DClassElement)editingClassObject).getName();
301
302         if (dynamicElement instanceof DPropertyElement) {
303           DynamicManager.getInstance(myProject).replaceDynamicPropertyType(className, name, (String)oldTypeValue, newTypeValue);
304
305         } else if (dynamicElement instanceof DMethodElement) {
306           final List<MyPair> myPairList = ((DMethodElement)dynamicElement).getPairs();
307           DynamicManager.getInstance(myProject).replaceDynamicMethodType(className, name, myPairList, (String)oldTypeValue, newTypeValue);
308         }
309       }
310
311       public void editingCanceled(ChangeEvent e) {
312         myTreeTable.editingCanceled(e);
313       }
314     });
315
316     RefactoringListenerManager.getInstance(myProject).addListenerProvider(new RefactoringElementListenerProvider() {
317       @Nullable
318       public RefactoringElementListener getListener(final PsiElement element) {
319         if (element instanceof PsiClass) {
320           final String qualifiedName = ((PsiClass)element).getQualifiedName();
321
322           return new RefactoringElementListener() {
323             public void elementMoved(@NotNull PsiElement newElement) {
324               renameElement(qualifiedName, newElement);
325             }
326
327             public void elementRenamed(@NotNull PsiElement newElement) {
328               renameElement(qualifiedName, newElement);
329             }
330
331             private void renameElement(String oldClassName, PsiElement newElement) {
332               if (newElement instanceof PsiClass) {
333                 final String newClassName = ((PsiClass)newElement).getQualifiedName();
334
335                 final DRootElement rootElement = DynamicManager.getInstance(myProject).getRootElement();
336                 final DClassElement oldClassElement = rootElement.getClassElement(oldClassName);
337                 final TreeNode oldClassNode = TreeUtil.findNodeWithObject((DefaultMutableTreeNode)myTreeRoot, oldClassElement);
338
339                 DynamicManager.getInstance(myProject).replaceClassName(oldClassElement, newClassName);
340                 myTreeTableModel.nodeChanged(oldClassNode);
341               }
342             }
343           };
344         }
345         return null;
346       }
347     });
348
349     myTreeTable.setDefaultEditor(String.class, typeCellEditor);
350
351     myTreeTable.registerKeyboardAction(new ActionListener() {
352       public void actionPerformed(ActionEvent event) {
353         final int selectionRow = myTreeTable.getTree().getLeadSelectionRow();
354         myTreeTable.editCellAt(selectionRow, CLASS_OR_ELEMENT_NAME_COLUMN, event);
355       }
356     }, KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), JComponent.WHEN_FOCUSED);
357
358     myTreeTable.registerKeyboardAction(new ActionListener() {
359       public void actionPerformed(ActionEvent event) {
360         final int selectionRow = myTreeTable.getTree().getLeadSelectionRow();
361         myTreeTable.editCellAt(selectionRow, TYPE_COLUMN, event);
362       }
363     }, KeyStroke.getKeyStroke(KeyEvent.VK_F2, InputEvent.CTRL_MASK), JComponent.WHEN_FOCUSED);
364
365     // todo use "myTreeTable.setAutoCreateRowSorter(true);" since 1.6
366
367     myTreeTable.getTree().setShowsRootHandles(true);
368     myTreeTable.getTableHeader().setReorderingAllowed(false);
369
370     myTreeTable.setPreferredScrollableViewportSize(new Dimension(300, myTreeTable.getRowHeight() * 10));
371     myTreeTable.getColumn(myColumnNames[CLASS_OR_ELEMENT_NAME_COLUMN]).setPreferredWidth(200);
372     myTreeTable.getColumn(myColumnNames[TYPE_COLUMN]).setPreferredWidth(160);
373
374     JScrollPane scrollpane = ScrollPaneFactory.createScrollPane(myTreeTable);
375
376     scrollpane.setPreferredSize(new Dimension(600, 400));
377     return scrollpane;
378   }
379
380   private void deleteRow() {
381     final int[] rows = myTreeTable.getSelectedRows();
382
383     boolean isShowDialog = true;
384     final int rowsCount = rows.length;
385     int i = 0;
386
387     final TreeTableTree tree = myTreeTable.getTree();
388
389     for (TreePath selectionPath : tree.getSelectionPaths()) {
390       if (rowsCount > 1) isShowDialog = false;
391       if (i++ == 0) isShowDialog = true;
392
393       //class
394       final TreePath parent = selectionPath.getParentPath();
395
396       if (parent.getParentPath() == null) {
397         //selectionPath is class
398
399         final Object classRow = selectionPath.getLastPathComponent();
400         if (!(classRow instanceof DefaultMutableTreeNode)) return;
401
402         if (!removeClass(((DefaultMutableTreeNode)classRow), isShowDialog, rowsCount)) return;
403
404       } else {
405         //selectionPath is dynamic item
406         final Object classRow = parent.getLastPathComponent();
407         final Object dynamicRow = selectionPath.getLastPathComponent();
408
409         if (!(classRow instanceof DefaultMutableTreeNode)) return;
410         if (!(dynamicRow instanceof DefaultMutableTreeNode)) return;
411
412         final DefaultMutableTreeNode dynamicItemNode = (DefaultMutableTreeNode)dynamicRow;
413         final DefaultMutableTreeNode classNode = (DefaultMutableTreeNode)classRow;
414
415         final boolean removeClass = classNode.getChildCount() == 1;
416         if (!removeDynamicElement(dynamicItemNode, isShowDialog, rowsCount)) return;
417         if (removeClass) {
418           removeNamedElement((DNamedElement)classNode.getUserObject());
419         }
420       }
421     }
422     DynamicManager.getInstance(myProject).fireChange();
423   }
424
425   private boolean removeClass(DefaultMutableTreeNode classNode, boolean isShowDialog, int rowsCount) {
426     final TreeNode rootObject = classNode.getParent();
427     return rootObject instanceof DefaultMutableTreeNode && removeDynamicElement(classNode, isShowDialog, rowsCount);
428
429   }
430
431   private boolean removeDynamicElement(DefaultMutableTreeNode child, boolean isShowDialog, int rowsCount) {
432     Object namedElement = child.getUserObject();
433
434     if (!(namedElement instanceof DNamedElement)) return false;
435
436     if (isShowDialog) {
437       int result;
438       if (rowsCount > 1) {
439         result = Messages.showOkCancelDialog(myBigPanel, GroovyBundle.message("are.you.sure.to.delete.elements", String.valueOf(rowsCount)),
440                                              GroovyBundle.message("dynamic.element.deletion"), Messages.getQuestionIcon());
441
442       } else {
443         result = Messages.showOkCancelDialog(myBigPanel, GroovyBundle.message("are.you.sure.to.delete.dynamic.property",
444                                                                               ((DNamedElement)namedElement).getName()),
445                                              GroovyBundle.message("dynamic.property.deletion"), Messages.getQuestionIcon());
446       }
447
448       if (result != DialogWrapper.OK_EXIT_CODE) return false;
449     }
450
451     removeNamedElement(((DNamedElement)namedElement));
452
453     /*final Object selectionNode = selectionPath.getLastPathComponent();
454     if (!(selectionNode instanceof DefaultMutableTreeNode)) return false;
455
456     DefaultMutableTreeNode toSelect = (parent.getChildAfter(child) != null || parent.getChildCount() == 1 ?
457         ((DefaultMutableTreeNode) selectionNode).getNextNode() :
458         ((DefaultMutableTreeNode) selectionNode).getPreviousNode());
459
460 //    DefaultMutableTreeNode toSelect = toSelect != null ? (DefaultMutableTreeNode) toSelect.getLastPathComponent() : null;
461
462     removeFromParent(parent, child);
463     if (toSelect != null) {
464       setSelectedNode(toSelect, myProject);
465     }*/
466
467     return true;
468   }
469
470   private void removeNamedElement(DNamedElement namedElement) {
471     if (namedElement instanceof DClassElement) {
472       DynamicManager.getInstance(myProject).removeClassElement((DClassElement)namedElement);
473     } else if (namedElement instanceof DItemElement) {
474       DynamicManager.getInstance(myProject).removeItemElement((DItemElement)namedElement);
475     }
476   }
477
478   public void setSelectedNode(DefaultMutableTreeNode node) {
479     JTree tree = myTreeTable.getTree();
480     TreePath path = new TreePath(node.getPath());
481     tree.expandPath(path.getParentPath());
482     int row = tree.getRowForPath(path);
483     myTreeTable.getSelectionModel().setSelectionInterval(row, row);
484     myTreeTable.scrollRectToVisible(myTreeTable.getCellRect(row, 0, true));
485     IdeFocusManager.getInstance(myProject).requestFocus(myTreeTable, true);
486   }
487
488
489   public void removeFromParent(DefaultMutableTreeNode parent, DefaultMutableTreeNode child) {
490     int idx = myTreeTableModel.getIndexOfChild(parent, child);
491     child.removeFromParent();
492     myTreeTableModel.nodesWereRemoved(parent, new int[]{idx}, new TreeNode[]{child});
493   }
494
495   static class PropertyTypeColumnInfo extends ColumnInfo<DefaultMutableTreeNode, String> {
496     public PropertyTypeColumnInfo(String name) {
497       super(name);
498     }
499
500     public boolean isCellEditable(DefaultMutableTreeNode node) {
501       final Object value = node.getUserObject();
502
503       return !(value instanceof DClassElement);
504     }
505
506     public String valueOf(DefaultMutableTreeNode treeNode) {
507       Object userObject = treeNode.getUserObject();
508
509       if (userObject instanceof DItemElement) return ((DItemElement)userObject).getType();
510
511       return null;
512     }
513   }
514
515   static class ClassColumnInfo extends ColumnInfo<DefaultMutableTreeNode, DNamedElement> {
516     public ClassColumnInfo(String name) {
517       super(name);
518     }
519
520     public boolean isCellEditable(DefaultMutableTreeNode treeNode) {
521       final Object userObject = treeNode.getUserObject();
522
523       return userObject instanceof DPropertyElement;
524     }
525
526     public Class getColumnClass() {
527       return TreeTableModel.class;
528     }
529
530     public DNamedElement valueOf(DefaultMutableTreeNode treeNode) {
531       Object userObject = treeNode.getUserObject();
532       if (userObject instanceof DClassElement) return ((DClassElement)userObject);
533       if (userObject instanceof DPropertyElement) return ((DPropertyElement)userObject);
534       if (userObject instanceof DMethodElement) return ((DMethodElement)userObject);
535
536       return null;
537     }
538   }
539
540   class DynamicFilterComponent extends FilterComponent {
541
542     public DynamicFilterComponent(@NonNls String propertyName, int historySize) {
543       super(propertyName, historySize);
544     }
545
546     public void filter() {
547       DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
548       buildTree(rootNode);
549
550       String filterText;
551       List<DefaultMutableTreeNode> classes = new ArrayList<DefaultMutableTreeNode>();
552       List<DefaultMutableTreeNode> dynamicNodes = new ArrayList<DefaultMutableTreeNode>();
553
554       if (rootNode.isLeaf()) return;
555       DefaultMutableTreeNode classNode = (DefaultMutableTreeNode)rootNode.getFirstChild();
556       while (classNode != null) {
557
558         if (classNode.isLeaf()) {
559           classNode = (DefaultMutableTreeNode)rootNode.getChildAfter(classNode);
560           continue;
561         }
562
563         DefaultMutableTreeNode dynamicNode = (DefaultMutableTreeNode)classNode.getFirstChild();
564         while (dynamicNode != null) {
565
566           final Object childObject = dynamicNode.getUserObject();
567           if (!(childObject instanceof DItemElement)) break;
568
569           filterText = getFilter();
570           if (filterText == null || filterText.isEmpty()) {
571             ((DItemElement)childObject).setHightlightedText("");
572
573             dynamicNodes.add(dynamicNode);
574             dynamicNode = (DefaultMutableTreeNode)classNode.getChildAfter(dynamicNode);
575             continue;
576           }
577
578           final String name = (((DItemElement)childObject)).getName();
579
580           if (name.contains(filterText)) {
581             ((DItemElement)childObject).setHightlightedText(filterText);
582             dynamicNodes.add(dynamicNode);
583           }
584
585           dynamicNode = (DefaultMutableTreeNode)classNode.getChildAfter(dynamicNode);
586         }
587
588         if (!dynamicNodes.isEmpty()) {
589           classes.add(classNode);
590         }
591
592         classNode.removeAllChildren();
593
594         for (DefaultMutableTreeNode node : dynamicNodes) {
595           classNode.add(node);
596         }
597
598         dynamicNodes.clear();
599
600         classNode = (DefaultMutableTreeNode)rootNode.getChildAfter(classNode);
601       }
602       rootNode.removeAllChildren();
603
604       for (DefaultMutableTreeNode aClass : classes) {
605         rootNode.add(aClass);
606       }
607
608       classes.clear();
609
610       rebuildTreeView(rootNode, true);
611       myBigPanel.invalidate();
612     }
613   }
614
615   public ListTreeTableModelOnColumns getTreeTableModel() {
616     getToolWindow();
617
618     return myTreeTableModel;
619   }
620
621   private static class MyColoredTreeCellRenderer extends ColoredTreeCellRenderer {
622     public void customizeCellRenderer(JTree tree,
623                                       Object value,
624                                       boolean selected,
625                                       boolean expanded,
626                                       boolean leaf,
627                                       int row,
628                                       boolean hasFocus) {
629       value = ((DefaultMutableTreeNode)value).getUserObject();
630
631       setPaintFocusBorder(false);
632
633       if (!(value instanceof DNamedElement)) return;
634
635       if (value instanceof DClassElement) {
636         final String containingClassName = ((DClassElement)value).getName();
637         //        append(className, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
638         final String classShortName = QuickfixUtil.shortenType(containingClassName);
639         append(classShortName, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
640       }
641
642       if (value instanceof DItemElement) {
643         final DItemElement itemElement = ((DItemElement)value);
644         final String substringToHighlight = itemElement.getHightlightedText();
645         final String name = itemElement.getName();
646
647         if (substringToHighlight != null) {
648           appendHighlightName(substringToHighlight, name);
649         } else {
650           appendName(name);
651         }
652
653         if (value instanceof DMethodElement) {
654           appendMethodParameters(name, (DMethodElement)value);
655         } else if (value instanceof DPropertyElement) {
656           setToolTipText(name);
657         }
658       }
659     }
660
661     private void appendHighlightName(String substringToHighlight, String name) {
662       final int begin = name.indexOf(substringToHighlight);
663 //          if (name.length() <= begin) return;
664       final String first = name.substring(0, begin);
665       append(first, SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES);
666       final TextAttributes textAttributes = new TextAttributes();
667       textAttributes.setBackgroundColor(UIUtil.getListSelectionBackground());
668       append(substringToHighlight, SimpleTextAttributes.fromTextAttributes(textAttributes));
669       append(name.substring(first.length() + substringToHighlight.length()), SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES);
670     }
671
672     private void appendName(String name) {
673       append(name, SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES);
674     }
675
676     private void appendMethodParameters(final String name, DMethodElement value) {
677       StringBuilder buffer = new StringBuilder();
678       buffer.append("(");
679
680       final String[] types = mapToUnqualified(QuickfixUtil.getArgumentsNames(value.getPairs()));
681       for (int i = 0; i < types.length; i++) {
682         if (i != 0) buffer.append(", ");
683
684         String type = types[i];
685         buffer.append(type);
686       }
687       buffer.append(")");
688
689       append(buffer.toString(), SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES);
690       setToolTipText(name + buffer.toString());
691     }
692
693     private static String[] mapToUnqualified(final String[] argumentsNames) {
694       return ContainerUtil.map2Array(argumentsNames, String.class, new NullableFunction<String, String>() {
695         @Nullable
696         public String fun(final String s) {
697           if (s == null) return null;
698           int index = s.lastIndexOf(".");
699           if (index > 0 && index < s.length() - 1) return s.substring(index + 1);
700           return s;
701         }
702       });
703     }
704   }
705
706   private class MyPropertyTypeCellEditor extends AbstractTableCellEditor {
707     final EditorTextField field;
708
709     public MyPropertyTypeCellEditor() {
710       final Document document = PsiDocumentManager.getInstance(myProject).getDocument(new GroovyCodeFragment(myProject, ""));
711       field = new EditorTextField(document, myProject, GroovyFileType.GROOVY_FILE_TYPE);
712     }
713
714     public String getCellEditorValue() {
715       return field.getText();
716     }
717
718     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
719       if (value instanceof String) {
720         field.setText(((String)value));
721       }
722
723       return field;
724     }
725   }
726
727   @Nullable
728   private TreeTableTree getTree() {
729     return myTreeTable != null ? myTreeTable.getTree() : null;
730   }
731
732   public class MyTreeTable extends TreeTable implements DataProvider {
733     public MyTreeTable(TreeTableModel treeTableModel) {
734       super(treeTableModel);
735     }
736
737     @Nullable
738     public Object getData(@NonNls String dataId) {
739       if (LangDataKeys.PSI_ELEMENT.is(dataId)) {
740         return getSelectedElement();
741
742       } else if (LangDataKeys.PSI_FILE.is(dataId)) {
743         final PsiElement element = getSelectedElement();
744
745         if (element == null) return null;
746         return element.getContainingFile();
747       } else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) {
748         return new DeleteProvider() {
749           @Override
750           public void deleteElement(DataContext dataContext) {
751             deleteRow();
752           }
753
754           @Override
755           public boolean canDeleteElement(DataContext dataContext) {
756             return myTreeTable.getTree().getSelectionPaths() != null;
757           }
758         };
759       }
760
761       return null;
762     }
763
764     private PsiElement getSelectedElement() {
765       final TreePath path = getTree().getSelectionPath();
766
767       if (path == null) return null;
768       final Object selectedObject = path.getLastPathComponent();
769       if (!(selectedObject instanceof DefaultMutableTreeNode)) return null;
770
771       final DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)selectedObject;
772       final Object userObject = selectedNode.getUserObject();
773       if (!(userObject instanceof DNamedElement)) return null;
774
775       if (userObject instanceof DClassElement) {
776         final DClassElement classElement = (DClassElement)userObject;
777
778         try {
779           final GrTypeElement typeElement = GroovyPsiElementFactory.getInstance(myProject).createTypeElement(classElement.getName());
780           PsiType type = typeElement.getType();
781
782           if (type instanceof PsiPrimitiveType) {
783             type = ((PsiPrimitiveType)type).getBoxedType(PsiManager.getInstance(myProject), GlobalSearchScope.allScope(myProject));
784           }
785
786           if (!(type instanceof PsiClassType)) return null;
787           return ((PsiClassType)type).resolve();
788
789         }
790         catch (IncorrectOperationException e) {
791           return null;
792         }
793       } else if (userObject instanceof DItemElement) {
794         final DItemElement itemElement = (DItemElement)userObject;
795
796         final TreeNode parentNode = selectedNode.getParent();
797         if (!(parentNode instanceof DefaultMutableTreeNode)) return null;
798
799         final Object classObject = ((DefaultMutableTreeNode)parentNode).getUserObject();
800         if (!(classObject instanceof DClassElement)) return null;
801
802         final String className = ((DClassElement)classObject).getName();
803
804         return itemElement.getPsi(PsiManager.getInstance(myProject), className);
805       }
806       return null;
807     }
808   }
809 }