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); } } } }