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