@Override
 public void calcData(final DataKey key, final DataSink sink) {
   if (key.equals(LangDataKeys.PSI_ELEMENT)) {
     if (mySelectedElements != null && !mySelectedElements.isEmpty()) {
       T selectedElement = mySelectedElements.iterator().next();
       if (selectedElement instanceof ClassMemberWithElement) {
         sink.put(
             LangDataKeys.PSI_ELEMENT, ((ClassMemberWithElement) selectedElement).getElement());
       }
     }
   }
 }
  @Override
  public Object getData(String dataId) {
    if (PlatformDataKeys.HELP_ID.is(dataId)) return HELP_ID;
    if (DATA_KEY.is(dataId)) return this;
    if (myTree == null) return null;
    TreePath[] paths = myTree.getSelectionPaths();

    if (paths == null || paths.length == 0) return null;

    if (paths.length > 1) {
      if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
        return collectPsiElements();
      }
      return null;
    }

    TreePath path = paths[0];

    InspectionTreeNode selectedNode = (InspectionTreeNode) path.getLastPathComponent();

    if (selectedNode instanceof RefElementNode) {
      final RefElementNode refElementNode = (RefElementNode) selectedNode;
      RefEntity refElement = refElementNode.getElement();
      if (refElement == null) return null;
      final RefEntity item = refElement.getRefManager().getRefinedElement(refElement);

      if (!item.isValid()) return null;

      PsiElement psiElement = item instanceof RefElement ? ((RefElement) item).getElement() : null;
      if (psiElement == null) return null;

      final CommonProblemDescriptor problem = refElementNode.getProblem();
      if (problem != null) {
        if (problem instanceof ProblemDescriptor) {
          psiElement = ((ProblemDescriptor) problem).getPsiElement();
          if (psiElement == null) return null;
        } else {
          return null;
        }
      }

      if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
        return getSelectedNavigatable(problem, psiElement);
      } else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
        return psiElement.isValid() ? psiElement : null;
      }
    } else if (selectedNode instanceof ProblemDescriptionNode
        && CommonDataKeys.NAVIGATABLE.is(dataId)) {
      return getSelectedNavigatable(((ProblemDescriptionNode) selectedNode).getDescriptor());
    }

    return null;
  }
    @Override
    public void calcData(final DataKey key, final DataSink sink) {
      Node node = getSelectedNode();

      if (key == PlatformDataKeys.PROJECT) {
        sink.put(PlatformDataKeys.PROJECT, myProject);
      } else if (key == USAGE_VIEW_KEY) {
        sink.put(USAGE_VIEW_KEY, UsageViewImpl.this);
      } else if (key == PlatformDataKeys.NAVIGATABLE_ARRAY) {
        sink.put(PlatformDataKeys.NAVIGATABLE_ARRAY, getNavigatablesForNodes(getSelectedNodes()));
      } else if (key == PlatformDataKeys.EXPORTER_TO_TEXT_FILE) {
        sink.put(PlatformDataKeys.EXPORTER_TO_TEXT_FILE, myTextFileExporter);
      } else if (key == USAGES_KEY) {
        final Set<Usage> selectedUsages = getSelectedUsages();
        sink.put(
            USAGES_KEY,
            selectedUsages != null
                ? selectedUsages.toArray(new Usage[selectedUsages.size()])
                : null);
      } else if (key == USAGE_TARGETS_KEY) {
        sink.put(USAGE_TARGETS_KEY, getSelectedUsageTargets());
      } else if (key == PlatformDataKeys.VIRTUAL_FILE_ARRAY) {
        final Set<Usage> usages = getSelectedUsages();
        Usage[] ua = usages != null ? usages.toArray(new Usage[usages.size()]) : null;
        VirtualFile[] data = UsageDataUtil.provideVirtualFileArray(ua, getSelectedUsageTargets());
        sink.put(PlatformDataKeys.VIRTUAL_FILE_ARRAY, data);
      } else if (key == PlatformDataKeys.HELP_ID) {
        sink.put(PlatformDataKeys.HELP_ID, HELP_ID);
      } else if (key == PlatformDataKeys.COPY_PROVIDER) {
        sink.put(PlatformDataKeys.COPY_PROVIDER, this);
      } else if (node != null) {
        Object userObject = node.getUserObject();
        if (userObject instanceof TypeSafeDataProvider) {
          ((TypeSafeDataProvider) userObject).calcData(key, sink);
        } else if (userObject instanceof DataProvider) {
          DataProvider dataProvider = (DataProvider) userObject;
          Object data = dataProvider.getData(key.getName());
          if (data != null) {
            sink.put(key, data);
          }
        }
      }
    }
public class FileChooserDialogImpl extends DialogWrapper
    implements FileChooserDialog, PathChooserDialog, FileLookup {
  @NonNls public static final String FILE_CHOOSER_SHOW_PATH_PROPERTY = "FileChooser.ShowPath";
  public static final String RECENT_FILES_KEY = "file.chooser.recent.files";
  private final FileChooserDescriptor myChooserDescriptor;
  protected FileSystemTreeImpl myFileSystemTree;
  private Project myProject;
  private VirtualFile[] myChosenFiles = VirtualFile.EMPTY_ARRAY;

  private JPanel myNorthPanel;
  private TextFieldAction myTextFieldAction;
  protected FileTextFieldImpl myPathTextField;
  private JComponent myPathTextFieldWrapper;

  private MergingUpdateQueue myUiUpdater;
  private boolean myTreeIsUpdating;

  public static DataKey<PathField> PATH_FIELD = DataKey.create("PathField");

  public FileChooserDialogImpl(
      @NotNull final FileChooserDescriptor descriptor, @Nullable Project project) {
    super(project, true);
    myChooserDescriptor = descriptor;
    myProject = project;
    setTitle(getChooserTitle(descriptor));
  }

  public FileChooserDialogImpl(
      @NotNull final FileChooserDescriptor descriptor, @NotNull Component parent) {
    this(descriptor, parent, null);
  }

  public FileChooserDialogImpl(
      @NotNull final FileChooserDescriptor descriptor,
      @NotNull Component parent,
      @Nullable Project project) {
    super(parent, true);
    myChooserDescriptor = descriptor;
    myProject = project;
    setTitle(getChooserTitle(descriptor));
  }

  private static String getChooserTitle(final FileChooserDescriptor descriptor) {
    final String title = descriptor.getTitle();
    return title != null ? title : UIBundle.message("file.chooser.default.title");
  }

  @Override
  @NotNull
  public VirtualFile[] choose(
      @Nullable final Project project, @NotNull final VirtualFile... toSelect) {
    init();
    if ((myProject == null) && (project != null)) {
      myProject = project;
    }
    if (toSelect.length == 1) {
      restoreSelection(toSelect[0]);
    } else if (toSelect.length == 0) {
      restoreSelection(null); // select last opened file
    } else {
      selectInTree(toSelect, true);
    }

    Runnable showRunnable =
        new Runnable() {
          @Override
          public void run() {
            show();
          }
        };
    // file chooser calls VFS refresh which might lead to rootsChanged in any open project and dumb
    // mode that the clients don't expect.
    // so if reindexing has to happen, let it happen under a modal progress and be finished before
    // the file chooser returns.
    // this hack should be gone if file chooser doesn't use VFS
    // (https://youtrack.jetbrains.com/issue/IDEA-101218)
    for (final Project eachProject : ProjectManager.getInstance().getOpenProjects()) {
      showRunnable = allowModalDumbModeInside(showRunnable, eachProject);
    }
    showRunnable.run();

    return myChosenFiles;
  }

  @NotNull
  private static Runnable allowModalDumbModeInside(
      final @NotNull Runnable runnable, @NotNull final Project eachProject) {
    return new Runnable() {
      @Override
      public void run() {
        DumbService.getInstance(eachProject)
            .allowStartingDumbModeInside(DumbModePermission.MAY_START_MODAL, runnable);
      }
    };
  }

  @NotNull
  @Override
  public VirtualFile[] choose(
      @Nullable final VirtualFile toSelect, @Nullable final Project project) {
    if (toSelect == null) {
      return choose(project);
    }
    return choose(project, toSelect);
  }

  @Override
  public void choose(
      @Nullable VirtualFile toSelect, @NotNull Consumer<List<VirtualFile>> callback) {
    init();
    restoreSelection(toSelect);
    show();
    if (myChosenFiles.length > 0) {
      callback.consume(Arrays.asList(myChosenFiles));
    } else if (callback instanceof FileChooser.FileChooserConsumer) {
      ((FileChooser.FileChooserConsumer) callback).cancelled();
    }
  }

  protected void restoreSelection(@Nullable VirtualFile toSelect) {
    final VirtualFile lastOpenedFile = FileChooserUtil.getLastOpenedFile(myProject);
    final VirtualFile file =
        FileChooserUtil.getFileToSelect(myChooserDescriptor, myProject, toSelect, lastOpenedFile);

    if (file != null && file.isValid()) {
      myFileSystemTree.select(
          file,
          new Runnable() {
            public void run() {
              if (!file.equals(myFileSystemTree.getSelectedFile())) {
                VirtualFile parent = file.getParent();
                if (parent != null) {
                  myFileSystemTree.select(parent, null);
                }
              } else if (file.isDirectory()) {
                myFileSystemTree.expand(file, null);
              }
            }
          });
    }
  }

  protected void storeSelection(@Nullable VirtualFile file) {
    FileChooserUtil.setLastOpenedFile(myProject, file);
    if (file != null && file.getFileSystem() instanceof LocalFileSystem) {
      saveRecent(file.getPath());
    }
  }

  protected void saveRecent(String path) {
    final List<String> files = new ArrayList<String>(Arrays.asList(getRecentFiles()));
    files.remove(path);
    files.add(0, path);
    while (files.size() > 30) {
      files.remove(files.size() - 1);
    }
    PropertiesComponent.getInstance().setValues(RECENT_FILES_KEY, ArrayUtil.toStringArray(files));
  }

  @NotNull
  private String[] getRecentFiles() {
    final String[] recent = PropertiesComponent.getInstance().getValues(RECENT_FILES_KEY);
    if (recent != null) {
      if (recent.length > 0
          && myPathTextField.getField().getText().replace('\\', '/').equals(recent[0])) {
        final String[] pathes = new String[recent.length - 1];
        System.arraycopy(recent, 1, pathes, 0, recent.length - 1);
        return pathes;
      }
      return recent;
    }
    return ArrayUtil.EMPTY_STRING_ARRAY;
  }

  protected JComponent createHistoryButton() {
    JLabel label = new JLabel(AllIcons.Actions.Get);
    label.setToolTipText("Recent files");
    new ClickListener() {
      @Override
      public boolean onClick(@NotNull MouseEvent event, int clickCount) {
        showRecentFilesPopup();
        return true;
      }
    }.installOn(label);

    new AnAction() {
      @Override
      public void actionPerformed(AnActionEvent e) {
        showRecentFilesPopup();
      }

      @Override
      public void update(AnActionEvent e) {
        e.getPresentation().setEnabled(!IdeEventQueue.getInstance().isPopupActive());
      }
    }.registerCustomShortcutSet(KeyEvent.VK_DOWN, 0, myPathTextField.getField());
    return label;
  }

  private void showRecentFilesPopup() {
    final JBList files =
        new JBList(getRecentFiles()) {
          @Override
          public Dimension getPreferredSize() {
            return new Dimension(
                myPathTextField.getField().getWidth(), super.getPreferredSize().height);
          }
        };
    files.setCellRenderer(
        new ColoredListCellRenderer() {
          @Override
          protected void customizeCellRenderer(
              JList list, Object value, int index, boolean selected, boolean hasFocus) {
            final String path = value.toString();
            append(path);
            final VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(new File(path));
            if (file != null) {
              setIcon(IconUtil.getIcon(file, Iconable.ICON_FLAG_READ_STATUS, null));
            }
          }
        });
    JBPopupFactory.getInstance()
        .createListPopupBuilder(files)
        .setItemChoosenCallback(
            new Runnable() {
              @Override
              public void run() {
                myPathTextField.getField().setText(files.getSelectedValue().toString());
              }
            })
        .createPopup()
        .showUnderneathOf(myPathTextField.getField());
  }

  protected DefaultActionGroup createActionGroup() {
    registerFileChooserShortcut(IdeActions.ACTION_DELETE, "FileChooser.Delete");
    registerFileChooserShortcut(IdeActions.ACTION_SYNCHRONIZE, "FileChooser.Refresh");

    return (DefaultActionGroup) ActionManager.getInstance().getAction("FileChooserToolbar");
  }

  private void registerFileChooserShortcut(
      @NonNls final String baseActionId, @NonNls final String fileChooserActionId) {
    final JTree tree = myFileSystemTree.getTree();
    final AnAction syncAction = ActionManager.getInstance().getAction(fileChooserActionId);

    AnAction original = ActionManager.getInstance().getAction(baseActionId);
    syncAction.registerCustomShortcutSet(original.getShortcutSet(), tree, myDisposable);
  }

  @Nullable
  protected final JComponent createTitlePane() {
    final String description = myChooserDescriptor.getDescription();
    if (StringUtil.isEmptyOrSpaces(description)) return null;

    final JLabel label = new JLabel(description);
    label.setBorder(
        BorderFactory.createCompoundBorder(
            new SideBorder(UIUtil.getPanelBackground().darker(), SideBorder.BOTTOM),
            JBUI.Borders.empty(0, 5, 10, 5)));
    return label;
  }

  protected JComponent createCenterPanel() {
    JPanel panel = new MyPanel();

    myUiUpdater = new MergingUpdateQueue("FileChooserUpdater", 200, false, panel);
    Disposer.register(myDisposable, myUiUpdater);
    new UiNotifyConnector(panel, myUiUpdater);

    panel.setBorder(JBUI.Borders.empty());

    createTree();

    final DefaultActionGroup group = createActionGroup();
    ActionToolbar toolBar =
        ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true);
    toolBar.setTargetComponent(panel);

    final JPanel toolbarPanel = new JPanel(new BorderLayout());
    toolbarPanel.add(toolBar.getComponent(), BorderLayout.CENTER);

    myTextFieldAction =
        new TextFieldAction() {
          public void linkSelected(final LinkLabel aSource, final Object aLinkData) {
            toggleShowTextField();
          }
        };
    toolbarPanel.add(myTextFieldAction, BorderLayout.EAST);

    myPathTextFieldWrapper = new JPanel(new BorderLayout());
    myPathTextFieldWrapper.setBorder(JBUI.Borders.emptyBottom(2));
    myPathTextField =
        new FileTextFieldImpl.Vfs(
            FileChooserFactoryImpl.getMacroMap(),
            getDisposable(),
            new LocalFsFinder.FileChooserFilter(myChooserDescriptor, myFileSystemTree)) {
          protected void onTextChanged(final String newValue) {
            myUiUpdater.cancelAllUpdates();
            updateTreeFromPath(newValue);
          }
        };
    Disposer.register(myDisposable, myPathTextField);
    myPathTextFieldWrapper.add(myPathTextField.getField(), BorderLayout.CENTER);
    if (getRecentFiles().length > 0) {
      myPathTextFieldWrapper.add(createHistoryButton(), BorderLayout.EAST);
    }

    myNorthPanel = new JPanel(new BorderLayout());
    myNorthPanel.add(toolbarPanel, BorderLayout.NORTH);

    updateTextFieldShowing();

    panel.add(myNorthPanel, BorderLayout.NORTH);

    registerMouseListener(group);

    JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myFileSystemTree.getTree());
    // scrollPane.setBorder(BorderFactory.createLineBorder(new Color(148, 154, 156)));
    panel.add(scrollPane, BorderLayout.CENTER);
    panel.setPreferredSize(JBUI.size(400));

    panel.add(
        new JLabel(
            "<html><center><small><font color=gray>Drag and drop a file into the space above to quickly locate it in the tree.</font></small></center></html>",
            SwingConstants.CENTER),
        BorderLayout.SOUTH);

    ApplicationManager.getApplication()
        .getMessageBus()
        .connect(getDisposable())
        .subscribe(
            ApplicationActivationListener.TOPIC,
            new ApplicationActivationListener.Adapter() {
              @Override
              public void applicationActivated(IdeFrame ideFrame) {
                ((SaveAndSyncHandlerImpl) SaveAndSyncHandler.getInstance())
                    .maybeRefresh(ModalityState.current());
              }
            });

    return panel;
  }

  public JComponent getPreferredFocusedComponent() {
    if (isToShowTextField()) {
      return myPathTextField != null ? myPathTextField.getField() : null;
    } else {
      return myFileSystemTree != null ? myFileSystemTree.getTree() : null;
    }
  }

  public final void dispose() {
    LocalFileSystem.getInstance().removeWatchedRoots(myRequests.values());
    super.dispose();
  }

  private boolean isTextFieldActive() {
    return myPathTextField.getField().getRootPane() != null;
  }

  protected void doOKAction() {
    if (!isOKActionEnabled()) {
      return;
    }

    if (isTextFieldActive()) {
      final String text = myPathTextField.getTextFieldText();
      final LookupFile file = myPathTextField.getFile();
      if (text == null || file == null || !file.exists()) {
        setErrorText("Specified path cannot be found");
        return;
      }
    }

    final List<VirtualFile> selectedFiles = Arrays.asList(getSelectedFilesInt());
    final VirtualFile[] files =
        VfsUtilCore.toVirtualFileArray(
            FileChooserUtil.getChosenFiles(myChooserDescriptor, selectedFiles));
    if (files.length == 0) {
      myChosenFiles = VirtualFile.EMPTY_ARRAY;
      close(CANCEL_EXIT_CODE);
      return;
    }

    try {
      myChooserDescriptor.validateSelectedFiles(files);
    } catch (Exception e) {
      Messages.showErrorDialog(getContentPane(), e.getMessage(), getTitle());
      return;
    }

    myChosenFiles = files;
    storeSelection(files[files.length - 1]);

    super.doOKAction();
  }

  public final void doCancelAction() {
    myChosenFiles = VirtualFile.EMPTY_ARRAY;
    super.doCancelAction();
  }

  protected JTree createTree() {
    myFileSystemTree = new FileSystemTreeImpl(myProject, myChooserDescriptor);
    Disposer.register(myDisposable, myFileSystemTree);

    myFileSystemTree.addOkAction(
        new Runnable() {
          public void run() {
            doOKAction();
          }
        });
    JTree tree = myFileSystemTree.getTree();
    tree.setCellRenderer(new NodeRenderer());
    tree.getSelectionModel().addTreeSelectionListener(new FileTreeSelectionListener());
    tree.addTreeExpansionListener(new FileTreeExpansionListener());
    setOKActionEnabled(false);

    myFileSystemTree.addListener(
        new FileSystemTree.Listener() {
          public void selectionChanged(final List<VirtualFile> selection) {
            updatePathFromTree(selection, false);
          }
        },
        myDisposable);

    new FileDrop(
        tree,
        new FileDrop.Target() {
          public FileChooserDescriptor getDescriptor() {
            return myChooserDescriptor;
          }

          public boolean isHiddenShown() {
            return myFileSystemTree.areHiddensShown();
          }

          public void dropFiles(final List<VirtualFile> files) {
            if (!myChooserDescriptor.isChooseMultiple() && files.size() > 0) {
              selectInTree(new VirtualFile[] {files.get(0)}, true);
            } else {
              selectInTree(VfsUtilCore.toVirtualFileArray(files), true);
            }
          }
        });

    return tree;
  }

  protected final void registerMouseListener(final ActionGroup group) {
    myFileSystemTree.registerMouseListener(group);
  }

  private VirtualFile[] getSelectedFilesInt() {
    if (myTreeIsUpdating || !myUiUpdater.isEmpty()) {
      if (isTextFieldActive() && !StringUtil.isEmpty(myPathTextField.getTextFieldText())) {
        LookupFile toFind = myPathTextField.getFile();
        if (toFind instanceof LocalFsFinder.VfsFile && toFind.exists()) {
          VirtualFile file = ((LocalFsFinder.VfsFile) toFind).getFile();
          if (file != null) {
            return new VirtualFile[] {file};
          }
        }
      }
      return VirtualFile.EMPTY_ARRAY;
    }

    return myFileSystemTree.getSelectedFiles();
  }

  private final Map<String, LocalFileSystem.WatchRequest> myRequests =
      new HashMap<String, LocalFileSystem.WatchRequest>();

  private static boolean isToShowTextField() {
    return PropertiesComponent.getInstance().getBoolean(FILE_CHOOSER_SHOW_PATH_PROPERTY, true);
  }

  private static void setToShowTextField(boolean toShowTextField) {
    PropertiesComponent.getInstance()
        .setValue(FILE_CHOOSER_SHOW_PATH_PROPERTY, Boolean.toString(toShowTextField));
  }

  private final class FileTreeExpansionListener implements TreeExpansionListener {
    public void treeExpanded(TreeExpansionEvent event) {
      final Object[] path = event.getPath().getPath();
      if (path.length == 2) {
        // top node has been expanded => watch disk recursively
        final DefaultMutableTreeNode node = (DefaultMutableTreeNode) path[1];
        Object userObject = node.getUserObject();
        if (userObject instanceof FileNodeDescriptor) {
          final VirtualFile file = ((FileNodeDescriptor) userObject).getElement().getFile();
          if (file != null && file.isDirectory()) {
            final String rootPath = file.getPath();
            if (myRequests.get(rootPath) == null) {
              final LocalFileSystem.WatchRequest watchRequest =
                  LocalFileSystem.getInstance().addRootToWatch(rootPath, true);
              myRequests.put(rootPath, watchRequest);
            }
          }
        }
      }
    }

    public void treeCollapsed(TreeExpansionEvent event) {}
  }

  private final class FileTreeSelectionListener implements TreeSelectionListener {
    public void valueChanged(TreeSelectionEvent e) {
      TreePath[] paths = e.getPaths();

      boolean enabled = true;
      for (TreePath treePath : paths) {
        if (!e.isAddedPath(treePath)) {
          continue;
        }
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) treePath.getLastPathComponent();
        Object userObject = node.getUserObject();
        if (!(userObject instanceof FileNodeDescriptor)) {
          enabled = false;
          break;
        }
        FileElement descriptor = ((FileNodeDescriptor) userObject).getElement();
        VirtualFile file = descriptor.getFile();
        enabled = file != null && myChooserDescriptor.isFileSelectable(file);
      }
      setOKActionEnabled(enabled);
    }
  }

  protected final class MyPanel extends JPanel implements DataProvider {
    public MyPanel() {
      super(new BorderLayout(0, 0));
    }

    public Object getData(String dataId) {
      if (CommonDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) {
        return myFileSystemTree.getSelectedFiles();
      } else if (PATH_FIELD.is(dataId)) {
        return new PathField() {
          public void toggleVisible() {
            toggleShowTextField();
          }
        };
      } else if (FileSystemTree.DATA_KEY.is(dataId)) {
        return myFileSystemTree;
      }
      return myChooserDescriptor.getUserData(dataId);
    }
  }

  public void toggleShowTextField() {
    setToShowTextField(!isToShowTextField());
    updateTextFieldShowing();
  }

  private void updateTextFieldShowing() {
    myTextFieldAction.update();
    myNorthPanel.remove(myPathTextFieldWrapper);
    if (isToShowTextField()) {
      final ArrayList<VirtualFile> selection = new ArrayList<VirtualFile>();
      if (myFileSystemTree.getSelectedFile() != null) {
        selection.add(myFileSystemTree.getSelectedFile());
      }
      updatePathFromTree(selection, true);
      myNorthPanel.add(myPathTextFieldWrapper, BorderLayout.CENTER);
    } else {
      setErrorText(null);
    }
    myPathTextField.getField().requestFocus();

    myNorthPanel.revalidate();
    myNorthPanel.repaint();
  }

  private void updatePathFromTree(final List<VirtualFile> selection, boolean now) {
    if (!isToShowTextField() || myTreeIsUpdating) return;

    String text = "";
    if (selection.size() > 0) {
      text = VfsUtil.getReadableUrl(selection.get(0));
    } else {
      final List<VirtualFile> roots = myChooserDescriptor.getRoots();
      if (!myFileSystemTree.getTree().isRootVisible() && roots.size() == 1) {
        text = VfsUtil.getReadableUrl(roots.get(0));
      }
    }

    myPathTextField.setText(
        text,
        now,
        new Runnable() {
          public void run() {
            myPathTextField.getField().selectAll();
            setErrorText(null);
          }
        });
  }

  private void updateTreeFromPath(final String text) {
    if (!isToShowTextField()) return;
    if (myPathTextField.isPathUpdating()) return;
    if (text == null) return;

    myUiUpdater.queue(
        new Update("treeFromPath.1") {
          public void run() {
            ApplicationManager.getApplication()
                .executeOnPooledThread(
                    new Runnable() {
                      public void run() {
                        final LocalFsFinder.VfsFile toFind =
                            (LocalFsFinder.VfsFile) myPathTextField.getFile();
                        if (toFind == null || !toFind.exists()) return;

                        myUiUpdater.queue(
                            new Update("treeFromPath.2") {
                              public void run() {
                                selectInTree(toFind.getFile(), text);
                              }
                            });
                      }
                    });
          }
        });
  }

  private void selectInTree(final VirtualFile vFile, String fromText) {
    if (vFile != null && vFile.isValid()) {
      if (fromText == null || fromText.equalsIgnoreCase(myPathTextField.getTextFieldText())) {
        selectInTree(new VirtualFile[] {vFile}, false);
      }
    } else {
      reportFileNotFound();
    }
  }

  private void selectInTree(final VirtualFile[] array, final boolean requestFocus) {
    myTreeIsUpdating = true;
    final List<VirtualFile> fileList = Arrays.asList(array);
    if (!Arrays.asList(myFileSystemTree.getSelectedFiles()).containsAll(fileList)) {
      myFileSystemTree.select(
          array,
          new Runnable() {
            public void run() {
              if (!myFileSystemTree.areHiddensShown()
                  && !Arrays.asList(myFileSystemTree.getSelectedFiles()).containsAll(fileList)) {
                myFileSystemTree.showHiddens(true);
                selectInTree(array, requestFocus);
                return;
              }

              myTreeIsUpdating = false;
              setErrorText(null);
              if (requestFocus) {
                //noinspection SSBasedInspection
                SwingUtilities.invokeLater(
                    new Runnable() {
                      public void run() {
                        myFileSystemTree.getTree().requestFocus();
                      }
                    });
              }
            }
          });
    } else {
      myTreeIsUpdating = false;
      setErrorText(null);
    }
  }

  private void reportFileNotFound() {
    myTreeIsUpdating = false;
    setErrorText(null);
  }

  @Override
  protected String getDimensionServiceKey() {
    return "FileChooserDialogImpl";
  }

  @Override
  protected String getHelpId() {
    return "select.path.dialog";
  }
}
/** @author max */
public class InspectionResultsView extends JPanel
    implements Disposable, OccurenceNavigator, DataProvider {
  public static final DataKey<InspectionResultsView> DATA_KEY = DataKey.create("inspectionView");

  private final Project myProject;
  private final InspectionTree myTree;
  private final Browser myBrowser;
  private final ConcurrentMap<HighlightDisplayLevel, ConcurrentMap<String, InspectionGroupNode>>
      myGroups = ContainerUtil.newConcurrentMap();
  private final OccurenceNavigator myOccurenceNavigator;
  private volatile InspectionProfile myInspectionProfile;
  private final AnalysisScope myScope;
  @NonNls private static final String HELP_ID = "reference.toolWindows.inspections";
  private final ConcurrentMap<HighlightDisplayLevel, InspectionSeverityGroupNode>
      mySeverityGroupNodes = ContainerUtil.newConcurrentMap();

  private final Splitter mySplitter;
  @NotNull private final GlobalInspectionContextImpl myGlobalInspectionContext;
  private boolean myRerun;
  private volatile boolean myDisposed;

  @NotNull private final InspectionRVContentProvider myProvider;
  private AnAction myIncludeAction;
  private AnAction myExcludeAction;

  public InspectionResultsView(
      @NotNull final Project project,
      final InspectionProfile inspectionProfile,
      @NotNull AnalysisScope scope,
      @NotNull GlobalInspectionContextImpl globalInspectionContext,
      @NotNull InspectionRVContentProvider provider) {
    setLayout(new BorderLayout());

    myProject = project;
    myInspectionProfile = inspectionProfile;
    myScope = scope;
    myGlobalInspectionContext = globalInspectionContext;
    myProvider = provider;

    myTree = new InspectionTree(project, globalInspectionContext);
    initTreeListeners();

    myOccurenceNavigator = initOccurenceNavigator();

    myBrowser = new Browser(this);

    mySplitter =
        new OnePixelSplitter(false, AnalysisUIOptions.getInstance(myProject).SPLITTER_PROPORTION);

    mySplitter.setFirstComponent(
        ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT | SideBorder.RIGHT));
    mySplitter.setSecondComponent(myBrowser);

    mySplitter.addPropertyChangeListener(
        new PropertyChangeListener() {
          @Override
          public void propertyChange(PropertyChangeEvent evt) {
            if (Splitter.PROP_PROPORTION.equals(evt.getPropertyName())) {
              myGlobalInspectionContext.setSplitterProportion(
                  ((Float) evt.getNewValue()).floatValue());
            }
          }
        });
    add(mySplitter, BorderLayout.CENTER);

    myBrowser.addClickListener(
        new Browser.ClickListener() {
          @Override
          public void referenceClicked(final Browser.ClickEvent e) {
            if (e.getEventType() == Browser.ClickEvent.REF_ELEMENT) {
              final RefElement refElement = e.getClickedElement();
              final OpenFileDescriptor descriptor = getOpenFileDescriptor(refElement);
              if (descriptor != null) {
                FileEditorManager.getInstance(project).openTextEditor(descriptor, false);
              }
            } else if (e.getEventType() == Browser.ClickEvent.FILE_OFFSET) {
              final VirtualFile file = e.getFile();
              final OpenFileDescriptor descriptor =
                  new OpenFileDescriptor(project, file, e.getStartOffset());
              final Editor editor =
                  FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
              if (editor != null) {
                final TextAttributes selectionAttributes =
                    EditorColorsManager.getInstance()
                        .getGlobalScheme()
                        .getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
                HighlightManager.getInstance(project)
                    .addRangeHighlight(
                        editor,
                        e.getStartOffset(),
                        e.getEndOffset(),
                        selectionAttributes,
                        true,
                        null);
              }
            }
          }
        });

    createActionsToolbar();
    TreeUtil.selectFirstNode(myTree);
  }

  private void initTreeListeners() {
    myTree
        .getSelectionModel()
        .addTreeSelectionListener(
            new TreeSelectionListener() {
              @Override
              public void valueChanged(TreeSelectionEvent e) {
                syncBrowser();
                if (isAutoScrollMode()) {
                  OpenSourceUtil.openSourcesFrom(
                      DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
                }
              }
            });

    EditSourceOnDoubleClickHandler.install(myTree);

    myTree.addKeyListener(
        new KeyAdapter() {
          @Override
          public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_ENTER) {
              OpenSourceUtil.openSourcesFrom(
                  DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
            }
          }
        });

    myTree.addMouseListener(
        new PopupHandler() {
          @Override
          public void invokePopup(Component comp, int x, int y) {
            popupInvoked(comp, x, y);
          }
        });

    SmartExpander.installOn(myTree);
  }

  private OccurenceNavigatorSupport initOccurenceNavigator() {
    return new OccurenceNavigatorSupport(myTree) {
      @Override
      @Nullable
      protected Navigatable createDescriptorForNode(DefaultMutableTreeNode node) {
        if (node instanceof InspectionTreeNode && ((InspectionTreeNode) node).isResolved()) {
          return null;
        }
        if (node instanceof RefElementNode) {
          final RefElementNode refNode = (RefElementNode) node;
          if (refNode.hasDescriptorsUnder()) return null;
          final RefEntity element = refNode.getElement();
          if (element == null || !element.isValid()) return null;
          final CommonProblemDescriptor problem = refNode.getProblem();
          if (problem != null) {
            return navigate(problem);
          }
          if (element instanceof RefElement) {
            return getOpenFileDescriptor((RefElement) element);
          }
        } else if (node instanceof ProblemDescriptionNode) {
          if (!((ProblemDescriptionNode) node).isValid()) return null;
          return navigate(((ProblemDescriptionNode) node).getDescriptor());
        }
        return null;
      }

      @Nullable
      private Navigatable navigate(final CommonProblemDescriptor descriptor) {
        return getSelectedNavigatable(descriptor);
      }

      @Override
      public String getNextOccurenceActionName() {
        return InspectionsBundle.message("inspection.action.go.next");
      }

      @Override
      public String getPreviousOccurenceActionName() {
        return InspectionsBundle.message("inspection.actiongo.prev");
      }
    };
  }

  private void createActionsToolbar() {
    final JComponent leftActionsToolbar = createLeftActionsToolbar();
    final JComponent rightActionsToolbar = createRightActionsToolbar();

    JPanel westPanel = new JPanel(new BorderLayout());
    westPanel.add(leftActionsToolbar, BorderLayout.WEST);
    westPanel.add(rightActionsToolbar, BorderLayout.EAST);
    add(westPanel, BorderLayout.WEST);
  }

  @SuppressWarnings({"NonStaticInitializer"})
  private JComponent createRightActionsToolbar() {
    myIncludeAction =
        new AnAction(InspectionsBundle.message("inspections.result.view.include.action.text")) {
          {
            registerCustomShortcutSet(CommonShortcuts.INSERT, myTree);
          }

          @Override
          public void actionPerformed(AnActionEvent e) {
            final TreePath[] paths = myTree.getSelectionPaths();
            if (paths != null) {
              for (TreePath path : paths) {
                ((InspectionTreeNode) path.getLastPathComponent()).amnesty();
              }
            }
            updateView(false);
          }

          @Override
          public void update(final AnActionEvent e) {
            final TreePath[] paths = myTree.getSelectionPaths();
            e.getPresentation()
                .setEnabled(
                    paths != null
                        && paths.length > 0
                        && !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS);
          }
        };

    myExcludeAction =
        new AnAction(InspectionsBundle.message("inspections.result.view.exclude.action.text")) {
          {
            registerCustomShortcutSet(CommonShortcuts.getDelete(), myTree);
          }

          @Override
          public void actionPerformed(final AnActionEvent e) {
            final TreePath[] paths = myTree.getSelectionPaths();
            if (paths != null) {
              for (TreePath path : paths) {
                ((InspectionTreeNode) path.getLastPathComponent()).ignoreElement();
              }
            }
            updateView(false);
          }

          @Override
          public void update(final AnActionEvent e) {
            final TreePath[] path = myTree.getSelectionPaths();
            e.getPresentation().setEnabled(path != null && path.length > 0);
          }
        };

    DefaultActionGroup specialGroup = new DefaultActionGroup();
    specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupBySeverityAction(this));
    specialGroup.add(myGlobalInspectionContext.getUIOptions().createGroupByDirectoryAction(this));
    specialGroup.add(
        myGlobalInspectionContext.getUIOptions().createFilterResolvedItemsAction(this));
    specialGroup.add(
        myGlobalInspectionContext.getUIOptions().createShowOutdatedProblemsAction(this));
    specialGroup.add(myGlobalInspectionContext.getUIOptions().createShowDiffOnlyAction(this));
    specialGroup.add(new EditSettingsAction());
    specialGroup.add(new InvokeQuickFixAction(this));
    specialGroup.add(new InspectionsOptionsToolbarAction(this));
    return createToolbar(specialGroup);
  }

  private JComponent createLeftActionsToolbar() {
    final CommonActionsManager actionsManager = CommonActionsManager.getInstance();
    DefaultActionGroup group = new DefaultActionGroup();
    group.add(new RerunAction(this));
    group.add(new CloseAction());
    final TreeExpander treeExpander =
        new TreeExpander() {
          @Override
          public void expandAll() {
            TreeUtil.expandAll(myTree);
          }

          @Override
          public boolean canExpand() {
            return true;
          }

          @Override
          public void collapseAll() {
            TreeUtil.collapseAll(myTree, 0);
          }

          @Override
          public boolean canCollapse() {
            return true;
          }
        };
    group.add(actionsManager.createExpandAllAction(treeExpander, myTree));
    group.add(actionsManager.createCollapseAllAction(treeExpander, myTree));
    group.add(actionsManager.createPrevOccurenceAction(getOccurenceNavigator()));
    group.add(actionsManager.createNextOccurenceAction(getOccurenceNavigator()));
    group.add(myGlobalInspectionContext.createToggleAutoscrollAction());
    group.add(new ExportHTMLAction(this));
    group.add(new ContextHelpAction(HELP_ID));

    return createToolbar(group);
  }

  private static JComponent createToolbar(final DefaultActionGroup specialGroup) {
    return ActionManager.getInstance()
        .createActionToolbar(ActionPlaces.CODE_INSPECTION, specialGroup, false)
        .getComponent();
  }

  @Override
  public void dispose() {
    mySplitter.dispose();
    myBrowser.dispose();
    myInspectionProfile = null;
    myDisposed = true;
  }

  private boolean isAutoScrollMode() {
    String activeToolWindowId = ToolWindowManager.getInstance(myProject).getActiveToolWindowId();
    return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE
        && (activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION));
  }

  @Nullable
  private static OpenFileDescriptor getOpenFileDescriptor(final RefElement refElement) {
    final VirtualFile[] file = new VirtualFile[1];
    final int[] offset = new int[1];

    ApplicationManager.getApplication()
        .runReadAction(
            new Runnable() {
              @Override
              public void run() {
                PsiElement psiElement = refElement.getElement();
                if (psiElement != null) {
                  final PsiFile containingFile = psiElement.getContainingFile();
                  if (containingFile != null) {
                    file[0] = containingFile.getVirtualFile();
                    offset[0] = psiElement.getTextOffset();
                  }
                } else {
                  file[0] = null;
                }
              }
            });

    if (file[0] != null && file[0].isValid()) {
      return new OpenFileDescriptor(refElement.getRefManager().getProject(), file[0], offset[0]);
    }
    return null;
  }

  private void syncBrowser() {
    if (myTree.getSelectionModel().getSelectionCount() != 1) {
      myBrowser.showEmpty();
    } else {
      TreePath pathSelected = myTree.getSelectionModel().getLeadSelectionPath();
      if (pathSelected != null) {
        final InspectionTreeNode node = (InspectionTreeNode) pathSelected.getLastPathComponent();
        if (node instanceof RefElementNode) {
          final RefElementNode refElementNode = (RefElementNode) node;
          final CommonProblemDescriptor problem = refElementNode.getProblem();
          final RefEntity refSelected = refElementNode.getElement();
          if (problem != null) {
            showInBrowser(refSelected, problem);
          } else {
            showInBrowser(refSelected);
          }
        } else if (node instanceof ProblemDescriptionNode) {
          final ProblemDescriptionNode problemNode = (ProblemDescriptionNode) node;
          showInBrowser(problemNode.getElement(), problemNode.getDescriptor());
        } else if (node instanceof InspectionNode) {
          showInBrowser(((InspectionNode) node).getToolWrapper());
        } else {
          myBrowser.showEmpty();
        }
      }
    }
  }

  private void showInBrowser(final RefEntity refEntity) {
    Cursor currentCursor = getCursor();
    setCursor(new Cursor(Cursor.WAIT_CURSOR));
    myBrowser.showPageFor(refEntity);
    setCursor(currentCursor);
  }

  private void showInBrowser(@NotNull InspectionToolWrapper toolWrapper) {
    Cursor currentCursor = getCursor();
    setCursor(new Cursor(Cursor.WAIT_CURSOR));
    myBrowser.showDescription(toolWrapper);
    setCursor(currentCursor);
  }

  private void showInBrowser(final RefEntity refEntity, CommonProblemDescriptor descriptor) {
    Cursor currentCursor = getCursor();
    setCursor(new Cursor(Cursor.WAIT_CURSOR));
    myBrowser.showPageFor(refEntity, descriptor);
    setCursor(currentCursor);
  }

  @NotNull
  public InspectionNode addTool(
      @NotNull final InspectionToolWrapper toolWrapper,
      HighlightDisplayLevel errorLevel,
      boolean groupedBySeverity) {
    String groupName =
        toolWrapper.getGroupDisplayName().isEmpty()
            ? InspectionProfileEntry.GENERAL_GROUP_NAME
            : toolWrapper.getGroupDisplayName();
    InspectionTreeNode parentNode = getToolParentNode(groupName, errorLevel, groupedBySeverity);
    InspectionNode toolNode = new InspectionNode(toolWrapper);
    boolean showStructure = myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE;
    myProvider.appendToolNodeContent(
        myGlobalInspectionContext, toolNode, parentNode, showStructure);
    InspectionToolPresentation presentation =
        myGlobalInspectionContext.getPresentation(toolWrapper);
    toolNode =
        presentation.createToolNode(
            myGlobalInspectionContext, toolNode, myProvider, parentNode, showStructure);
    ((DefaultInspectionToolPresentation) presentation).setToolNode(toolNode);

    registerActionShortcuts(presentation);
    return toolNode;
  }

  private void registerActionShortcuts(@NotNull InspectionToolPresentation presentation) {
    final QuickFixAction[] fixes = presentation.getQuickFixes(RefEntity.EMPTY_ELEMENTS_ARRAY);
    if (fixes != null) {
      for (QuickFixAction fix : fixes) {
        fix.registerCustomShortcutSet(fix.getShortcutSet(), this);
      }
    }
  }

  private void clearTree() {
    myTree.removeAllNodes();
    mySeverityGroupNodes.clear();
  }

  @Nullable
  public String getCurrentProfileName() {
    return myInspectionProfile == null ? null : myInspectionProfile.getDisplayName();
  }

  public InspectionProfile getCurrentProfile() {
    return myInspectionProfile;
  }

  public boolean update() {
    return updateView(true);
  }

  public boolean updateView(boolean strict) {
    if (!strict && !myGlobalInspectionContext.getUIOptions().FILTER_RESOLVED_ITEMS) {
      myTree.repaint();
      return false;
    }
    clearTree();
    boolean resultsFound = buildTree();
    myTree.restoreExpansionAndSelection();
    return resultsFound;
  }

  private boolean buildTree() {
    InspectionProfile profile = myInspectionProfile;
    boolean isGroupedBySeverity = myGlobalInspectionContext.getUIOptions().GROUP_BY_SEVERITY;
    myGroups.clear();
    final Map<String, Tools> tools = myGlobalInspectionContext.getTools();
    boolean resultsFound = false;
    for (Tools currentTools : tools.values()) {
      InspectionToolWrapper defaultToolWrapper = currentTools.getDefaultState().getTool();
      final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
      for (ScopeToolState state : myProvider.getTools(currentTools)) {
        InspectionToolWrapper toolWrapper = state.getTool();
        if (myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper)) {
          addTool(
              toolWrapper,
              ((InspectionProfileImpl) profile)
                  .getErrorLevel(key, state.getScope(myProject), myProject),
              isGroupedBySeverity);
          resultsFound = true;
        }
      }
    }
    return resultsFound;
  }

  @NotNull
  private InspectionTreeNode getToolParentNode(
      @NotNull String groupName, HighlightDisplayLevel errorLevel, boolean groupedBySeverity) {
    if (groupName.isEmpty()) {
      return getRelativeRootNode(groupedBySeverity, errorLevel);
    }
    ConcurrentMap<String, InspectionGroupNode> map = myGroups.get(errorLevel);
    if (map == null) {
      map =
          ConcurrencyUtil.cacheOrGet(
              myGroups, errorLevel, ContainerUtil.<String, InspectionGroupNode>newConcurrentMap());
    }
    InspectionGroupNode group;
    if (groupedBySeverity) {
      group = map.get(groupName);
    } else {
      group = null;
      for (Map<String, InspectionGroupNode> groupMap : myGroups.values()) {
        if ((group = groupMap.get(groupName)) != null) break;
      }
    }
    if (group == null) {
      group = ConcurrencyUtil.cacheOrGet(map, groupName, new InspectionGroupNode(groupName));
      addChildNodeInEDT(getRelativeRootNode(groupedBySeverity, errorLevel), group);
    }
    return group;
  }

  @NotNull
  private InspectionTreeNode getRelativeRootNode(
      boolean isGroupedBySeverity, HighlightDisplayLevel level) {
    if (isGroupedBySeverity) {
      InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level);
      if (severityGroupNode == null) {
        InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myProject, level);
        severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode);
        if (severityGroupNode == newNode) {
          InspectionTreeNode root = myTree.getRoot();
          addChildNodeInEDT(root, severityGroupNode);
        }
      }
      return severityGroupNode;
    }
    return myTree.getRoot();
  }

  private void addChildNodeInEDT(
      @NotNull final DefaultMutableTreeNode root,
      @NotNull final MutableTreeNode severityGroupNode) {
    UIUtil.invokeLaterIfNeeded(
        new Runnable() {
          @Override
          public void run() {
            if (!myDisposed) {
              root.add(severityGroupNode);
            }
          }
        });
  }

  private OccurenceNavigator getOccurenceNavigator() {
    return myOccurenceNavigator;
  }

  @Override
  public boolean hasNextOccurence() {
    return myOccurenceNavigator != null && myOccurenceNavigator.hasNextOccurence();
  }

  @Override
  public boolean hasPreviousOccurence() {
    return myOccurenceNavigator != null && myOccurenceNavigator.hasPreviousOccurence();
  }

  @Override
  public OccurenceInfo goNextOccurence() {
    return myOccurenceNavigator != null ? myOccurenceNavigator.goNextOccurence() : null;
  }

  @Override
  public OccurenceInfo goPreviousOccurence() {
    return myOccurenceNavigator != null ? myOccurenceNavigator.goPreviousOccurence() : null;
  }

  @Override
  public String getNextOccurenceActionName() {
    return myOccurenceNavigator != null ? myOccurenceNavigator.getNextOccurenceActionName() : "";
  }

  @Override
  public String getPreviousOccurenceActionName() {
    return myOccurenceNavigator != null
        ? myOccurenceNavigator.getPreviousOccurenceActionName()
        : "";
  }

  @NotNull
  public Project getProject() {
    return myProject;
  }

  @Override
  public Object getData(String dataId) {
    if (PlatformDataKeys.HELP_ID.is(dataId)) return HELP_ID;
    if (DATA_KEY.is(dataId)) return this;
    if (myTree == null) return null;
    TreePath[] paths = myTree.getSelectionPaths();

    if (paths == null || paths.length == 0) return null;

    if (paths.length > 1) {
      if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
        return collectPsiElements();
      }
      return null;
    }

    TreePath path = paths[0];

    InspectionTreeNode selectedNode = (InspectionTreeNode) path.getLastPathComponent();

    if (selectedNode instanceof RefElementNode) {
      final RefElementNode refElementNode = (RefElementNode) selectedNode;
      RefEntity refElement = refElementNode.getElement();
      if (refElement == null) return null;
      final RefEntity item = refElement.getRefManager().getRefinedElement(refElement);

      if (!item.isValid()) return null;

      PsiElement psiElement = item instanceof RefElement ? ((RefElement) item).getElement() : null;
      if (psiElement == null) return null;

      final CommonProblemDescriptor problem = refElementNode.getProblem();
      if (problem != null) {
        if (problem instanceof ProblemDescriptor) {
          psiElement = ((ProblemDescriptor) problem).getPsiElement();
          if (psiElement == null) return null;
        } else {
          return null;
        }
      }

      if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
        return getSelectedNavigatable(problem, psiElement);
      } else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
        return psiElement.isValid() ? psiElement : null;
      }
    } else if (selectedNode instanceof ProblemDescriptionNode
        && CommonDataKeys.NAVIGATABLE.is(dataId)) {
      return getSelectedNavigatable(((ProblemDescriptionNode) selectedNode).getDescriptor());
    }

    return null;
  }

  @Nullable
  private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor) {
    return getSelectedNavigatable(
        descriptor,
        descriptor instanceof ProblemDescriptor
            ? ((ProblemDescriptor) descriptor).getPsiElement()
            : null);
  }

  @Nullable
  private Navigatable getSelectedNavigatable(
      final CommonProblemDescriptor descriptor, final PsiElement psiElement) {
    if (descriptor instanceof ProblemDescriptorBase) {
      Navigatable navigatable = ((ProblemDescriptorBase) descriptor).getNavigatable();
      if (navigatable != null) {
        return navigatable;
      }
    }
    if (psiElement == null || !psiElement.isValid()) return null;
    PsiFile containingFile = psiElement.getContainingFile();
    VirtualFile virtualFile = containingFile == null ? null : containingFile.getVirtualFile();

    if (virtualFile != null) {
      int startOffset = psiElement.getTextOffset();
      if (descriptor instanceof ProblemDescriptorBase) {
        final TextRange textRange =
            ((ProblemDescriptorBase) descriptor).getTextRangeForNavigation();
        if (textRange != null) {
          if (virtualFile instanceof VirtualFileWindow) {
            virtualFile = ((VirtualFileWindow) virtualFile).getDelegate();
          }
          startOffset = textRange.getStartOffset();
        }
      }
      return new OpenFileDescriptor(myProject, virtualFile, startOffset);
    }
    return null;
  }

  private PsiElement[] collectPsiElements() {
    RefEntity[] refElements = myTree.getSelectedElements();
    List<PsiElement> psiElements = new ArrayList<PsiElement>();
    for (RefEntity refElement : refElements) {
      PsiElement psiElement =
          refElement instanceof RefElement ? ((RefElement) refElement).getElement() : null;
      if (psiElement != null && psiElement.isValid()) {
        psiElements.add(psiElement);
      }
    }

    return PsiUtilCore.toPsiElementArray(psiElements);
  }

  private void popupInvoked(Component component, int x, int y) {
    final TreePath path = myTree.getLeadSelectionPath();

    if (path == null) return;

    final DefaultActionGroup actions = new DefaultActionGroup();
    final ActionManager actionManager = ActionManager.getInstance();
    actions.add(actionManager.getAction(IdeActions.ACTION_EDIT_SOURCE));
    actions.add(actionManager.getAction(IdeActions.ACTION_FIND_USAGES));

    actions.add(myIncludeAction);
    actions.add(myExcludeAction);

    actions.addSeparator();

    final InspectionToolWrapper toolWrapper = myTree.getSelectedToolWrapper();
    if (toolWrapper != null) {
      final QuickFixAction[] quickFixes = myProvider.getQuickFixes(toolWrapper, myTree);
      if (quickFixes != null) {
        for (QuickFixAction quickFixe : quickFixes) {
          actions.add(quickFixe);
        }
      }
      final HighlightDisplayKey key = HighlightDisplayKey.find(toolWrapper.getShortName());
      if (key == null) return; // e.g. DummyEntryPointsTool

      // options
      actions.addSeparator();
      actions.add(new EditSettingsAction());
      final List<AnAction> options = new InspectionsOptionsToolbarAction(this).createActions();
      for (AnAction action : options) {
        actions.add(action);
      }
    }

    actions.addSeparator();
    actions.add(actionManager.getAction(IdeActions.GROUP_VERSION_CONTROLS));

    final ActionPopupMenu menu =
        actionManager.createActionPopupMenu(ActionPlaces.CODE_INSPECTION, actions);
    menu.getComponent().show(component, x, y);
  }

  @NotNull
  public InspectionTree getTree() {
    return myTree;
  }

  @NotNull
  public GlobalInspectionContextImpl getGlobalInspectionContext() {
    return myGlobalInspectionContext;
  }

  @NotNull
  public InspectionRVContentProvider getProvider() {
    return myProvider;
  }

  public boolean isSingleToolInSelection() {
    return myTree != null && myTree.getSelectedToolWrapper() != null;
  }

  public boolean isRerun() {
    boolean rerun = myRerun;
    myRerun = false;
    return rerun;
  }

  private InspectionProfile guessProfileToSelect(
      final InspectionProjectProfileManager profileManager) {
    final Set<InspectionProfile> profiles = new HashSet<InspectionProfile>();
    final RefEntity[] selectedElements = myTree.getSelectedElements();
    for (RefEntity selectedElement : selectedElements) {
      if (selectedElement instanceof RefElement) {
        final RefElement refElement = (RefElement) selectedElement;
        final PsiElement element = refElement.getElement();
        if (element != null) {
          profiles.add(profileManager.getInspectionProfile());
        }
      }
    }
    if (profiles.isEmpty()) {
      return (InspectionProfile) profileManager.getProjectProfileImpl();
    }
    return profiles.iterator().next();
  }

  public boolean isProfileDefined() {
    return myInspectionProfile != null && myInspectionProfile.isEditable();
  }

  public static void showPopup(AnActionEvent e, JBPopup popup) {
    final InputEvent event = e.getInputEvent();
    if (event instanceof MouseEvent) {
      popup.showUnderneathOf(event.getComponent());
    } else {
      popup.showInBestPositionFor(e.getDataContext());
    }
  }

  public AnalysisScope getScope() {
    return myScope;
  }

  private class CloseAction extends AnAction implements DumbAware {
    private CloseAction() {
      super(CommonBundle.message("action.close"), null, AllIcons.Actions.Cancel);
    }

    @Override
    public void actionPerformed(AnActionEvent e) {
      myGlobalInspectionContext.close(true);
    }
  }

  private class EditSettingsAction extends AnAction {
    private EditSettingsAction() {
      super(
          InspectionsBundle.message("inspection.action.edit.settings"),
          InspectionsBundle.message("inspection.action.edit.settings"),
          AllIcons.General.Settings);
    }

    @Override
    public void actionPerformed(AnActionEvent e) {
      final InspectionProjectProfileManager profileManager =
          InspectionProjectProfileManager.getInstance(myProject);
      final InspectionToolWrapper toolWrapper = myTree.getSelectedToolWrapper();
      InspectionProfile inspectionProfile = myInspectionProfile;
      final boolean profileIsDefined = isProfileDefined();
      if (!profileIsDefined) {
        inspectionProfile = guessProfileToSelect(profileManager);
      }

      if (toolWrapper != null) {
        final HighlightDisplayKey key =
            HighlightDisplayKey.find(
                toolWrapper.getShortName()); // do not search for dead code entry point tool
        if (key != null) {
          if (new EditInspectionToolsSettingsAction(key)
                  .editToolSettings(
                      myProject, (InspectionProfileImpl) inspectionProfile, profileIsDefined)
              && profileIsDefined) {
            updateCurrentProfile();
          }
          return;
        }
      }
      if (EditInspectionToolsSettingsAction.editToolSettings(
              myProject, inspectionProfile, profileIsDefined, null)
          && profileIsDefined) {
        updateCurrentProfile();
      }
    }
  }

  public void updateCurrentProfile() {
    final String name = myInspectionProfile.getName();
    myInspectionProfile =
        (InspectionProfile) myInspectionProfile.getProfileManager().getProfile(name);
  }

  private class RerunAction extends AnAction {
    public RerunAction(JComponent comp) {
      super(
          InspectionsBundle.message("inspection.action.rerun"),
          InspectionsBundle.message("inspection.action.rerun"),
          AllIcons.Actions.Rerun);
      registerCustomShortcutSet(CommonShortcuts.getRerun(), comp);
    }

    @Override
    public void update(AnActionEvent e) {
      e.getPresentation().setEnabled(myScope.isValid());
    }

    @Override
    public void actionPerformed(AnActionEvent e) {
      rerun();
    }

    private void rerun() {
      myRerun = true;
      if (myScope.isValid()) {
        AnalysisUIOptions.getInstance(myProject).save(myGlobalInspectionContext.getUIOptions());
        myGlobalInspectionContext.doInspections(myScope);
      }
    }
  }
}