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 nik */ public class XDebuggerTree extends DnDAwareTree implements DataProvider, Disposable { private final TransferToEDTQueue<Runnable> myLaterInvocator = TransferToEDTQueue.createRunnableMerger("XDebuggerTree later invocator", 100); private final ComponentListener myMoveListener = new ComponentAdapter() { @Override public void componentMoved(ComponentEvent e) { repaint(); // needed to repaint links in cell renderer on horizontal scrolling } }; private static final DataKey<XDebuggerTree> XDEBUGGER_TREE_KEY = DataKey.create("xdebugger.tree"); private final SingleAlarm myAlarm = new SingleAlarm( new Runnable() { @Override public void run() { final Editor editor = FileEditorManager.getInstance(myProject).getSelectedTextEditor(); if (editor != null) { editor.getContentComponent().revalidate(); editor.getContentComponent().repaint(); } } }, 100, this); private static final Convertor<TreePath, String> SPEED_SEARCH_CONVERTER = new Convertor<TreePath, String>() { @Override public String convert(TreePath o) { String text = null; if (o != null) { final Object node = o.getLastPathComponent(); if (node instanceof XDebuggerTreeNode) { text = ((XDebuggerTreeNode) node).getText().toString(); } } return StringUtil.notNullize(text); } }; private static final TransferHandler DEFAULT_TRANSFER_HANDLER = new TransferHandler() { @Override protected Transferable createTransferable(JComponent c) { if (!(c instanceof XDebuggerTree)) { return null; } XDebuggerTree tree = (XDebuggerTree) c; //noinspection deprecation TreePath[] selectedPaths = tree.getSelectionPaths(); if (selectedPaths == null || selectedPaths.length == 0) { return null; } StringBuilder plainBuf = new StringBuilder(); StringBuilder htmlBuf = new StringBuilder(); htmlBuf.append("<html>\n<body>\n<ul>\n"); TextTransferable.ColoredStringBuilder coloredTextContainer = new TextTransferable.ColoredStringBuilder(); for (TreePath path : selectedPaths) { htmlBuf.append(" <li>"); Object node = path.getLastPathComponent(); if (node != null) { if (node instanceof XDebuggerTreeNode) { ((XDebuggerTreeNode) node).appendToComponent(coloredTextContainer); coloredTextContainer.appendTo(plainBuf, htmlBuf); } else { String text = node.toString(); plainBuf.append(text); htmlBuf.append(text); } } plainBuf.append('\n'); htmlBuf.append("</li>\n"); } // remove the last newline plainBuf.setLength(plainBuf.length() - 1); htmlBuf.append("</ul>\n</body>\n</html>"); return new TextTransferable(htmlBuf.toString(), plainBuf.toString()); } @Override public int getSourceActions(JComponent c) { return COPY; } }; private final DefaultTreeModel myTreeModel; private final Project myProject; private final XDebuggerEditorsProvider myEditorsProvider; private XSourcePosition mySourcePosition; private final List<XDebuggerTreeListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private final XValueMarkers<?, ?> myValueMarkers; public XDebuggerTree( final @NotNull Project project, final @NotNull XDebuggerEditorsProvider editorsProvider, final @Nullable XSourcePosition sourcePosition, final @NotNull String popupActionGroupId, @Nullable XValueMarkers<?, ?> valueMarkers) { myValueMarkers = valueMarkers; myProject = project; myEditorsProvider = editorsProvider; mySourcePosition = sourcePosition; myTreeModel = new DefaultTreeModel(null); setModel(myTreeModel); setCellRenderer(new XDebuggerTreeRenderer()); new TreeLinkMouseListener(new XDebuggerTreeRenderer()) { @Override protected boolean doCacheLastNode() { return false; } @Override protected void handleTagClick(@Nullable Object tag, @NotNull MouseEvent event) { if (tag instanceof XDebuggerTreeNodeHyperlink) { ((XDebuggerTreeNodeHyperlink) tag).onClick(event); } } }.installOn(this); setRootVisible(false); setShowsRootHandles(true); new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent e) { return expandIfEllipsis(); } }.installOn(this); addKeyListener( new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE || key == KeyEvent.VK_RIGHT) { expandIfEllipsis(); } } }); if (Boolean.valueOf(System.getProperty("xdebugger.variablesView.rss"))) { new XDebuggerTreeSpeedSearch(this, SPEED_SEARCH_CONVERTER); } else { new TreeSpeedSearch(this, SPEED_SEARCH_CONVERTER); } final ActionManager actionManager = ActionManager.getInstance(); addMouseListener( new PopupHandler() { @Override public void invokePopup(final Component comp, final int x, final int y) { ActionGroup group = (ActionGroup) actionManager.getAction(popupActionGroupId); actionManager .createActionPopupMenu(ActionPlaces.UNKNOWN, group) .getComponent() .show(comp, x, y); } }); registerShortcuts(); setTransferHandler(DEFAULT_TRANSFER_HANDLER); addComponentListener(myMoveListener); } public void updateEditor() { myAlarm.cancelAndRequest(); } public boolean isUnderRemoteDebug() { // FIXME [VISTALL] we can access to RemoteRunProfile from platform - java specific // DataContext context = DataManager.getInstance().getDataContext(this); // ExecutionEnvironment env = LangDataKeys.EXECUTION_ENVIRONMENT.getData(context); // if (env != null && env.getRunProfile() instanceof RemoteRunProfile) { // return true; // } return false; } private boolean expandIfEllipsis() { MessageTreeNode[] treeNodes = getSelectedNodes(MessageTreeNode.class, null); if (treeNodes.length == 1) { MessageTreeNode node = treeNodes[0]; if (node.isEllipsis()) { TreeNode parent = node.getParent(); if (parent instanceof XValueContainerNode) { ((XValueContainerNode) parent).startComputingChildren(); return true; } } } return false; } public void addTreeListener(@NotNull XDebuggerTreeListener listener) { myListeners.add(listener); } public void removeTreeListener(@NotNull XDebuggerTreeListener listener) { myListeners.remove(listener); } public void setRoot(XDebuggerTreeNode root, final boolean rootVisible) { setRootVisible(rootVisible); myTreeModel.setRoot(root); } public XDebuggerTreeNode getRoot() { return (XDebuggerTreeNode) myTreeModel.getRoot(); } @Nullable public XSourcePosition getSourcePosition() { return mySourcePosition; } public void setSourcePosition(final @Nullable XSourcePosition sourcePosition) { mySourcePosition = sourcePosition; } @NotNull public XDebuggerEditorsProvider getEditorsProvider() { return myEditorsProvider; } @NotNull public Project getProject() { return myProject; } @Nullable public XValueMarkers<?, ?> getValueMarkers() { return myValueMarkers; } public DefaultTreeModel getTreeModel() { return myTreeModel; } @Override @Nullable public Object getData(@NonNls final String dataId) { if (XDEBUGGER_TREE_KEY.is(dataId)) { return this; } if (PlatformDataKeys.PREDEFINED_TEXT.is(dataId)) { XValueNodeImpl[] selectedNodes = getSelectedNodes(XValueNodeImpl.class, null); if (selectedNodes.length == 1 && selectedNodes[0].getFullValueEvaluator() == null) { return DebuggerUIUtil.getNodeRawValue(selectedNodes[0]); } } return null; } public void rebuildAndRestore(final XDebuggerTreeState treeState) { Object rootNode = myTreeModel.getRoot(); if (rootNode instanceof XDebuggerTreeNode) { ((XDebuggerTreeNode) rootNode).clearChildren(); treeState.restoreState(this); repaint(); } } public void childrenLoaded( final @NotNull XDebuggerTreeNode node, final @NotNull List<XValueContainerNode<?>> children, final boolean last) { for (XDebuggerTreeListener listener : myListeners) { listener.childrenLoaded(node, children, last); } } public void nodeLoaded(final @NotNull RestorableStateNode node, final @NotNull String name) { for (XDebuggerTreeListener listener : myListeners) { listener.nodeLoaded(node, name); } } public void markNodesObsolete() { Object root = myTreeModel.getRoot(); if (root instanceof XValueContainerNode<?>) { markNodesObsolete((XValueContainerNode<?>) root); } } @Override public void dispose() { ActionManager actionManager = ActionManager.getInstance(); actionManager.getAction(XDebuggerActions.SET_VALUE).unregisterCustomShortcutSet(this); actionManager.getAction(XDebuggerActions.COPY_VALUE).unregisterCustomShortcutSet(this); actionManager.getAction(XDebuggerActions.JUMP_TO_SOURCE).unregisterCustomShortcutSet(this); actionManager.getAction(XDebuggerActions.JUMP_TO_TYPE_SOURCE).unregisterCustomShortcutSet(this); actionManager.getAction(XDebuggerActions.MARK_OBJECT).unregisterCustomShortcutSet(this); // clear all possible inner fields that may still have links to debugger objects myTreeModel.setRoot(null); setCellRenderer(null); UIUtil.dispose(this); setLeadSelectionPath(null); setAnchorSelectionPath(null); removeComponentListener(myMoveListener); } private void registerShortcuts() { ActionManager actionManager = ActionManager.getInstance(); actionManager .getAction(XDebuggerActions.SET_VALUE) .registerCustomShortcutSet( new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)), this); actionManager .getAction(XDebuggerActions.COPY_VALUE) .registerCustomShortcutSet(CommonShortcuts.getCopy(), this); actionManager .getAction(XDebuggerActions.JUMP_TO_SOURCE) .registerCustomShortcutSet(CommonShortcuts.getEditSource(), this); Shortcut[] editTypeShortcuts = KeymapManager.getInstance() .getActiveKeymap() .getShortcuts(XDebuggerActions.EDIT_TYPE_SOURCE); actionManager .getAction(XDebuggerActions.JUMP_TO_TYPE_SOURCE) .registerCustomShortcutSet(new CustomShortcutSet(editTypeShortcuts), this); actionManager .getAction(XDebuggerActions.MARK_OBJECT) .registerCustomShortcutSet( new CustomShortcutSet( KeymapManager.getInstance().getActiveKeymap().getShortcuts("ToggleBookmark")), this); } private static void markNodesObsolete(final XValueContainerNode<?> node) { node.setObsolete(); List<? extends XValueContainerNode<?>> loadedChildren = node.getLoadedChildren(); if (loadedChildren != null) { for (XValueContainerNode<?> child : loadedChildren) { markNodesObsolete(child); } } } @Nullable public static XDebuggerTree getTree(final AnActionEvent e) { return e.getData(XDEBUGGER_TREE_KEY); } @Nullable public static XDebuggerTree getTree(DataContext context) { return XDEBUGGER_TREE_KEY.getData(context); } public TransferToEDTQueue<Runnable> getLaterInvocator() { return myLaterInvocator; } public void selectNodeOnLoad(final Condition<TreeNode> nodeFilter) { addTreeListener( new XDebuggerTreeListener() { @Override public void nodeLoaded(@NotNull RestorableStateNode node, String name) { if (nodeFilter.value(node)) { setSelectionPath(node.getPath()); } } @Override public void childrenLoaded( @NotNull XDebuggerTreeNode node, @NotNull List<XValueContainerNode<?>> children, boolean last) {} }); } public void expandNodesOnLoad(final Condition<TreeNode> nodeFilter) { addTreeListener( new XDebuggerTreeListener() { @Override public void nodeLoaded(@NotNull RestorableStateNode node, String name) { if (nodeFilter.value(node) && !node.isLeaf()) { // cause children computing node.getChildCount(); } } @Override public void childrenLoaded( @NotNull XDebuggerTreeNode node, @NotNull List<XValueContainerNode<?>> children, boolean last) { if (nodeFilter.value(node)) { expandPath(node.getPath()); } } }); } }
/** @author max */ public class ChangesListView extends Tree implements TypeSafeDataProvider, AdvancedDnDSource { private ChangesListView.DropTarget myDropTarget; private DnDManager myDndManager; private ChangeListOwner myDragOwner; private final Project myProject; private TreeState myTreeState; private boolean myShowFlatten = false; private final CopyProvider myCopyProvider; @NonNls public static final String HELP_ID_KEY = "helpId"; @NonNls public static final String ourHelpId = "ideaInterface.changes"; @NonNls public static final DataKey<List<VirtualFile>> UNVERSIONED_FILES_DATA_KEY = DataKey.create("ChangeListView.UnversionedFiles"); @NonNls public static final DataKey<List<FilePath>> MISSING_FILES_DATA_KEY = DataKey.create("ChangeListView.MissingFiles"); @NonNls public static final DataKey<List<LocallyDeletedChange>> LOCALLY_DELETED_CHANGES = DataKey.create("ChangeListView.LocallyDeletedChanges"); @NonNls public static final DataKey<String> HELP_ID_DATA_KEY = DataKey.create(HELP_ID_KEY); private ActionGroup myMenuGroup; public ChangesListView(final Project project) { myProject = project; getModel().setRoot(ChangesBrowserNode.create(myProject, TreeModelBuilder.ROOT_NODE_VALUE)); setShowsRootHandles(true); setRootVisible(false); new TreeSpeedSearch(this, new NodeToTextConvertor()); SmartExpander.installOn(this); myCopyProvider = new TreeCopyProvider(this); new TreeLinkMouseListener(new ChangesBrowserNodeRenderer(myProject, false, false)) .installOn(this); } @Override public DefaultTreeModel getModel() { return (DefaultTreeModel) super.getModel(); } public void installDndSupport(ChangeListOwner owner) { myDragOwner = owner; myDropTarget = new DropTarget(); myDndManager = DnDManager.getInstance(); myDndManager.registerSource(this); myDndManager.registerTarget(myDropTarget, this); } @Override public void dispose() { if (myDropTarget != null) { myDndManager.unregisterSource(this); myDndManager.unregisterTarget(myDropTarget, this); myDropTarget = null; myDndManager = null; myDragOwner = null; } } private void storeState() { myTreeState = TreeState.createOn(this, (ChangesBrowserNode) getModel().getRoot()); } private void restoreState() { myTreeState.applyTo(this, (ChangesBrowserNode) getModel().getRoot()); } public boolean isShowFlatten() { return myShowFlatten; } public void setShowFlatten(final boolean showFlatten) { myShowFlatten = showFlatten; } public void updateModel( List<? extends ChangeList> changeLists, Trinity<List<VirtualFile>, Integer, Integer> unversionedFiles, final List<LocallyDeletedChange> locallyDeletedFiles, List<VirtualFile> modifiedWithoutEditing, MultiMap<String, VirtualFile> switchedFiles, @Nullable Map<VirtualFile, String> switchedRoots, @Nullable List<VirtualFile> ignoredFiles, final List<VirtualFile> lockedFolders, @Nullable final Map<VirtualFile, LogicalLock> logicallyLockedFiles) { TreeModelBuilder builder = new TreeModelBuilder(myProject, isShowFlatten()); final DefaultTreeModel model = builder.buildModel( changeLists, unversionedFiles, locallyDeletedFiles, modifiedWithoutEditing, switchedFiles, switchedRoots, ignoredFiles, lockedFolders, logicallyLockedFiles); storeState(); DefaultTreeModel oldModel = getModel(); setModel(model); setCellRenderer(new ChangesBrowserNodeRenderer(myProject, isShowFlatten(), true)); ChangesBrowserNode root = (ChangesBrowserNode) model.getRoot(); expandPath(new TreePath(root.getPath())); restoreState(); expandDefaultChangeList(oldModel, root); } private void expandDefaultChangeList(DefaultTreeModel oldModel, ChangesBrowserNode root) { if (((ChangesBrowserNode) oldModel.getRoot()).getCount() == 0 && TreeUtil.collectExpandedPaths(this).size() == 1) { TreeNode toExpand = null; for (int i = 0; i < root.getChildCount(); i++) { TreeNode node = root.getChildAt(i); if (node instanceof ChangesBrowserChangeListNode && node.getChildCount() > 0) { ChangeList object = ((ChangesBrowserChangeListNode) node).getUserObject(); if (object instanceof LocalChangeList) { if (((LocalChangeList) object).isDefault()) { toExpand = node; break; } } } } if (toExpand != null) { expandPath(new TreePath(new Object[] {root, toExpand})); } } } @Override public void calcData(DataKey key, DataSink sink) { if (key == VcsDataKeys.CHANGES) { sink.put(VcsDataKeys.CHANGES, getSelectedChanges()); } else if (key == VcsDataKeys.CHANGE_LEAD_SELECTION) { sink.put(VcsDataKeys.CHANGE_LEAD_SELECTION, getLeadSelection()); } else if (key == VcsDataKeys.CHANGE_LISTS) { sink.put(VcsDataKeys.CHANGE_LISTS, getSelectedChangeLists()); } else if (key == PlatformDataKeys.VIRTUAL_FILE_ARRAY) { sink.put(PlatformDataKeys.VIRTUAL_FILE_ARRAY, getSelectedFiles()); } else if (key == PlatformDataKeys.NAVIGATABLE) { final VirtualFile[] files = getSelectedFiles(); if (files.length == 1 && !files[0].isDirectory()) { sink.put(PlatformDataKeys.NAVIGATABLE, new OpenFileDescriptor(myProject, files[0], 0)); } } else if (key == PlatformDataKeys.NAVIGATABLE_ARRAY) { sink.put( PlatformDataKeys.NAVIGATABLE_ARRAY, ChangesUtil.getNavigatableArray(myProject, getSelectedFiles())); } else if (key == PlatformDataKeys.DELETE_ELEMENT_PROVIDER) { final TreePath[] paths = getSelectionPaths(); if (paths != null) { for (TreePath path : paths) { ChangesBrowserNode node = (ChangesBrowserNode) path.getLastPathComponent(); if (!(node.getUserObject() instanceof ChangeList)) { sink.put(PlatformDataKeys.DELETE_ELEMENT_PROVIDER, new VirtualFileDeleteProvider()); break; } } } } else if (key == PlatformDataKeys.COPY_PROVIDER) { sink.put(PlatformDataKeys.COPY_PROVIDER, myCopyProvider); } else if (key == UNVERSIONED_FILES_DATA_KEY) { sink.put(UNVERSIONED_FILES_DATA_KEY, getSelectedUnversionedFiles()); } else if (key == VcsDataKeys.MODIFIED_WITHOUT_EDITING_DATA_KEY) { sink.put(VcsDataKeys.MODIFIED_WITHOUT_EDITING_DATA_KEY, getSelectedModifiedWithoutEditing()); } else if (key == LOCALLY_DELETED_CHANGES) { sink.put(LOCALLY_DELETED_CHANGES, getSelectedLocallyDeletedChanges()); } else if (key == MISSING_FILES_DATA_KEY) { sink.put(MISSING_FILES_DATA_KEY, getSelectedMissingFiles()); } else if (VcsDataKeys.HAVE_LOCALLY_DELETED == key) { sink.put(VcsDataKeys.HAVE_LOCALLY_DELETED, haveLocallyDeleted()); } else if (VcsDataKeys.HAVE_MODIFIED_WITHOUT_EDITING == key) { sink.put(VcsDataKeys.HAVE_MODIFIED_WITHOUT_EDITING, haveLocallyModified()); } else if (VcsDataKeys.HAVE_SELECTED_CHANGES == key) { sink.put(VcsDataKeys.HAVE_SELECTED_CHANGES, haveSelectedChanges()); } else if (key == HELP_ID_DATA_KEY) { sink.put(HELP_ID_DATA_KEY, ourHelpId); } else if (key == VcsDataKeys.CHANGES_IN_LIST_KEY) { final TreePath selectionPath = getSelectionPath(); if (selectionPath != null && selectionPath.getPathCount() > 1) { ChangesBrowserNode<?> firstNode = (ChangesBrowserNode) selectionPath.getPathComponent(1); if (firstNode instanceof ChangesBrowserChangeListNode) { final List<Change> list = firstNode.getAllChangesUnder(); sink.put(VcsDataKeys.CHANGES_IN_LIST_KEY, list); } } } } private List<VirtualFile> getSelectedUnversionedFiles() { return getSelectedVirtualFiles(ChangesBrowserNode.UNVERSIONED_FILES_TAG); } private List<VirtualFile> getSelectedModifiedWithoutEditing() { return getSelectedVirtualFiles(ChangesBrowserNode.MODIFIED_WITHOUT_EDITING_TAG); } private List<VirtualFile> getSelectedIgnoredFiles() { return getSelectedVirtualFiles(ChangesBrowserNode.IGNORED_FILES_TAG); } private List<VirtualFile> getSelectedVirtualFiles(final Object tag) { Set<VirtualFile> files = new HashSet<VirtualFile>(); final TreePath[] paths = getSelectionPaths(); if (paths != null) { for (TreePath path : paths) { if (path.getPathCount() > 1) { ChangesBrowserNode firstNode = (ChangesBrowserNode) path.getPathComponent(1); if (tag == null || firstNode.getUserObject() == tag) { ChangesBrowserNode<?> node = (ChangesBrowserNode) path.getLastPathComponent(); files.addAll(node.getAllFilesUnder()); } } } } return new ArrayList<VirtualFile>(files); } private List<FilePath> getSelectedFilePaths(final Object tag) { Set<FilePath> files = new HashSet<FilePath>(); final TreePath[] paths = getSelectionPaths(); if (paths != null) { for (TreePath path : paths) { if (path.getPathCount() > 1) { ChangesBrowserNode firstNode = (ChangesBrowserNode) path.getPathComponent(1); if (tag == null || firstNode.getUserObject() == tag) { ChangesBrowserNode<?> node = (ChangesBrowserNode) path.getLastPathComponent(); files.addAll(node.getAllFilePathsUnder()); } } } } return new ArrayList<FilePath>(files); } private List<LocallyDeletedChange> getSelectedLocallyDeletedChanges() { Set<LocallyDeletedChange> files = new HashSet<LocallyDeletedChange>(); final TreePath[] paths = getSelectionPaths(); if (paths != null) { for (TreePath path : paths) { if (path.getPathCount() > 1) { ChangesBrowserNode firstNode = (ChangesBrowserNode) path.getPathComponent(1); if (firstNode.getUserObject() == TreeModelBuilder.LOCALLY_DELETED_NODE) { ChangesBrowserNode<?> node = (ChangesBrowserNode) path.getLastPathComponent(); final List<LocallyDeletedChange> objectsUnder = node.getAllObjectsUnder(LocallyDeletedChange.class); files.addAll(objectsUnder); } } } } return new ArrayList<LocallyDeletedChange>(files); } private List<FilePath> getSelectedMissingFiles() { return getSelectedFilePaths(TreeModelBuilder.LOCALLY_DELETED_NODE); } protected VirtualFile[] getSelectedFiles() { final Change[] changes = getSelectedChanges(); Collection<VirtualFile> files = new HashSet<VirtualFile>(); for (Change change : changes) { final ContentRevision afterRevision = change.getAfterRevision(); if (afterRevision != null) { final VirtualFile file = afterRevision.getFile().getVirtualFile(); if (file != null && file.isValid()) { files.add(file); } } } files.addAll(getSelectedVirtualFiles(null)); return VfsUtilCore.toVirtualFileArray(files); } protected boolean haveSelectedFileType(final Object tag) { final TreePath[] paths = getSelectionPaths(); if (paths != null) { for (TreePath path : paths) { if (path.getPathCount() > 1) { ChangesBrowserNode firstNode = (ChangesBrowserNode) path.getPathComponent(1); if ((tag == null || firstNode.getUserObject() == tag) && path.getPathCount() > 2) { return true; } } } } return false; } public boolean haveLocallyDeleted() { return haveSelectedFileType(TreeModelBuilder.LOCALLY_DELETED_NODE); } public boolean haveLocallyModified() { return haveSelectedFileType(ChangesBrowserNode.MODIFIED_WITHOUT_EDITING_TAG); } private Boolean haveSelectedChanges() { final TreePath[] paths = getSelectionPaths(); if (paths == null) return false; for (TreePath path : paths) { ChangesBrowserNode node = (ChangesBrowserNode) path.getLastPathComponent(); if (node instanceof ChangesBrowserChangeNode) { return true; } else if (node instanceof ChangesBrowserChangeListNode) { final ChangesBrowserChangeListNode changeListNode = (ChangesBrowserChangeListNode) node; if (changeListNode.getChildCount() > 0) { return true; } } } return false; } private Change[] getLeadSelection() { final Set<Change> changes = new LinkedHashSet<Change>(); final TreePath[] paths = getSelectionPaths(); if (paths == null) return new Change[0]; for (TreePath path : paths) { ChangesBrowserNode node = (ChangesBrowserNode) path.getLastPathComponent(); if (node instanceof ChangesBrowserChangeNode) { changes.add(((ChangesBrowserChangeNode) node).getUserObject()); } } return changes.toArray(new Change[changes.size()]); } @NotNull public Change[] getSelectedChanges() { Set<Change> changes = new LinkedHashSet<Change>(); final TreePath[] paths = getSelectionPaths(); if (paths == null) { return new Change[0]; } for (TreePath path : paths) { ChangesBrowserNode<?> node = (ChangesBrowserNode) path.getLastPathComponent(); changes.addAll(node.getAllChangesUnder()); } if (changes.isEmpty()) { final List<VirtualFile> selectedModifiedWithoutEditing = getSelectedModifiedWithoutEditing(); if (selectedModifiedWithoutEditing != null && !selectedModifiedWithoutEditing.isEmpty()) { for (VirtualFile file : selectedModifiedWithoutEditing) { AbstractVcs vcs = ProjectLevelVcsManager.getInstance(myProject).getVcsFor(file); if (vcs == null) continue; final VcsCurrentRevisionProxy before = VcsCurrentRevisionProxy.create(file, myProject, vcs.getKeyInstanceMethod()); if (before != null) { ContentRevision afterRevision = new CurrentContentRevision(new FilePathImpl(file)); changes.add(new Change(before, afterRevision, FileStatus.HIJACKED)); } } } } return changes.toArray(new Change[changes.size()]); } @NotNull private ChangeList[] getSelectedChangeLists() { Set<ChangeList> lists = new HashSet<ChangeList>(); final TreePath[] paths = getSelectionPaths(); if (paths == null) return new ChangeList[0]; for (TreePath path : paths) { ChangesBrowserNode node = (ChangesBrowserNode) path.getLastPathComponent(); final Object userObject = node.getUserObject(); if (userObject instanceof ChangeList) { lists.add((ChangeList) userObject); } } return lists.toArray(new ChangeList[lists.size()]); } public void setMenuActions(final ActionGroup menuGroup) { myMenuGroup = menuGroup; updateMenu(); editSourceRegistration(); } protected void editSourceRegistration() { EditSourceOnDoubleClickHandler.install(this); EditSourceOnEnterKeyHandler.install(this); } private void updateMenu() { PopupHandler.installPopupHandler( this, myMenuGroup, ActionPlaces.CHANGES_VIEW_POPUP, ActionManager.getInstance()); } @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"}) private static class DragImageFactory { private static void drawSelection(JTable table, int column, Graphics g, final int width) { int y = 0; final int[] rows = table.getSelectedRows(); final int height = table.getRowHeight(); for (int row : rows) { final TableCellRenderer renderer = table.getCellRenderer(row, column); final Component component = renderer.getTableCellRendererComponent( table, table.getValueAt(row, column), false, false, row, column); g.translate(0, y); component.setBounds(0, 0, width, height); boolean wasOpaque = false; if (component instanceof JComponent) { final JComponent j = (JComponent) component; if (j.isOpaque()) wasOpaque = true; j.setOpaque(false); } component.paint(g); if (wasOpaque) { ((JComponent) component).setOpaque(true); } y += height; g.translate(0, -y); } } private static void drawSelection(JTree tree, Graphics g, final int width) { int y = 0; final int[] rows = tree.getSelectionRows(); final int height = tree.getRowHeight(); for (int row : rows) { final TreeCellRenderer renderer = tree.getCellRenderer(); final Object value = tree.getPathForRow(row).getLastPathComponent(); if (value == null) continue; final Component component = renderer.getTreeCellRendererComponent(tree, value, false, false, false, row, false); if (component.getFont() == null) { component.setFont(tree.getFont()); } g.translate(0, y); component.setBounds(0, 0, width, height); boolean wasOpaque = false; if (component instanceof JComponent) { final JComponent j = (JComponent) component; if (j.isOpaque()) wasOpaque = true; j.setOpaque(false); } component.paint(g); if (wasOpaque) { ((JComponent) component).setOpaque(true); } y += height; g.translate(0, -y); } } public static Image createImage(final JTable table, int column) { final int height = Math.max(20, Math.min(100, table.getSelectedRowCount() * table.getRowHeight())); final int width = table.getColumnModel().getColumn(column).getWidth(); final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D) image.getGraphics(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f)); drawSelection(table, column, g2, width); return image; } public static Image createImage(final JTree tree) { final TreeSelectionModel model = tree.getSelectionModel(); final TreePath[] paths = model.getSelectionPaths(); int count = 0; final List<ChangesBrowserNode> nodes = new ArrayList<ChangesBrowserNode>(); for (final TreePath path : paths) { final ChangesBrowserNode node = (ChangesBrowserNode) path.getLastPathComponent(); if (!node.isLeaf()) { nodes.add(node); count += node.getCount(); } } for (TreePath path : paths) { final ChangesBrowserNode element = (ChangesBrowserNode) path.getLastPathComponent(); boolean child = false; for (final ChangesBrowserNode node : nodes) { if (node.isNodeChild(element)) { child = true; break; } } if (!child) { if (element.isLeaf()) count++; } else if (!element.isLeaf()) { count -= element.getCount(); } } final JLabel label = new JLabel(VcsBundle.message("changes.view.dnd.label", count)); label.setOpaque(true); label.setForeground(tree.getForeground()); label.setBackground(tree.getBackground()); label.setFont(tree.getFont()); label.setSize(label.getPreferredSize()); final BufferedImage image = new BufferedImage(label.getWidth(), label.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D) image.getGraphics(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f)); label.paint(g2); g2.dispose(); return image; } } public class DropTarget implements DnDTarget { @Override public boolean update(DnDEvent aEvent) { aEvent.hideHighlighter(); aEvent.setDropPossible(false, ""); Object attached = aEvent.getAttachedObject(); if (!(attached instanceof ChangeListDragBean)) return false; final ChangeListDragBean dragBean = (ChangeListDragBean) attached; if (dragBean.getSourceComponent() != ChangesListView.this) return false; dragBean.setTargetNode(null); RelativePoint dropPoint = aEvent.getRelativePoint(); Point onTree = dropPoint.getPoint(ChangesListView.this); final TreePath dropPath = getPathForLocation(onTree.x, onTree.y); if (dropPath == null) return false; ChangesBrowserNode dropNode = (ChangesBrowserNode) dropPath.getLastPathComponent(); while (!((ChangesBrowserNode) dropNode.getParent()).isRoot()) { dropNode = (ChangesBrowserNode) dropNode.getParent(); } if (!dropNode.canAcceptDrop(dragBean)) { return false; } final Rectangle tableCellRect = getPathBounds(new TreePath(dropNode.getPath())); if (fitsInBounds(tableCellRect)) { aEvent.setHighlighting( new RelativeRectangle(ChangesListView.this, tableCellRect), DnDEvent.DropTargetHighlightingType.RECTANGLE); } aEvent.setDropPossible(true); dragBean.setTargetNode(dropNode); return false; } @Override public void drop(DnDEvent aEvent) { Object attached = aEvent.getAttachedObject(); if (!(attached instanceof ChangeListDragBean)) return; final ChangeListDragBean dragBean = (ChangeListDragBean) attached; final ChangesBrowserNode changesBrowserNode = dragBean.getTargetNode(); if (changesBrowserNode != null) { changesBrowserNode.acceptDrop(myDragOwner, dragBean); } } @Override public void cleanUpOnLeave() {} @Override public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) {} } private boolean fitsInBounds(final Rectangle rect) { final Container container = getParent(); if (container instanceof JViewport) { final Container scrollPane = container.getParent(); if (scrollPane instanceof JScrollPane) { final Rectangle rectangle = SwingUtilities.convertRectangle(this, rect, scrollPane.getParent()); return scrollPane.getBounds().contains(rectangle); } } return true; } private static class NodeToTextConvertor implements Convertor<TreePath, String> { @Override public String convert(final TreePath path) { ChangesBrowserNode node = (ChangesBrowserNode) path.getLastPathComponent(); return node.getTextPresentation(); } } @Override public boolean canStartDragging(DnDAction action, Point dragOrigin) { return action == DnDAction.MOVE && (getSelectedChanges().length > 0 || !getSelectedUnversionedFiles().isEmpty() || !getSelectedIgnoredFiles().isEmpty()); } @Override public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) { return new DnDDragStartBean( new ChangeListDragBean( this, getSelectedChanges(), getSelectedUnversionedFiles(), getSelectedIgnoredFiles())); } @Override @Nullable public Pair<Image, Point> createDraggedImage(DnDAction action, Point dragOrigin) { final Image image = DragImageFactory.createImage(this); return new Pair<Image, Point>(image, new Point(-image.getWidth(null), -image.getHeight(null))); } @Override public void dragDropEnd() {} @Override public void dropActionChanged(final int gestureModifiers) {} @Override @NotNull public JComponent getComponent() { return this; } @Override public void processMouseEvent(final MouseEvent e) { if (MouseEvent.MOUSE_RELEASED == e.getID() && !isSelectionEmpty() && !e.isShiftDown() && !e.isControlDown() && !e.isMetaDown() && !e.isPopupTrigger()) { if (isOverSelection(e.getPoint())) { clearSelection(); final TreePath path = getPathForLocation(e.getPoint().x, e.getPoint().y); if (path != null) { setSelectionPath(path); } } } super.processMouseEvent(e); } @Override public boolean isOverSelection(final Point point) { return TreeUtil.isOverSelection(this, point); } @Override public void dropSelectionButUnderPoint(final Point point) { TreeUtil.dropSelectionButUnderPoint(this, point); } }
/** @author spleaner */ public class Notification { private static final Logger LOG = Logger.getInstance("#com.intellij.notification.Notification"); private static final DataKey<Notification> KEY = DataKey.create("Notification"); public final String id; private final String myGroupId; private Icon myIcon; private final NotificationType myType; private String myTitle; private String mySubtitle; private String myContent; private NotificationListener myListener; private String myDropDownText; private List<AnAction> myActions; private boolean myExpired; private Runnable myWhenExpired; private Boolean myImportant; private WeakReference<Balloon> myBalloonRef; private final long myTimestamp; public Notification( @NotNull String groupDisplayId, @NotNull Icon icon, @NotNull NotificationType type) { this(groupDisplayId, icon, null, null, null, type, null); } public Notification( @NotNull String groupDisplayId, @NotNull Icon icon, @Nullable String title, @Nullable String subtitle, @Nullable String content, @NotNull NotificationType type, @Nullable NotificationListener listener) { myGroupId = groupDisplayId; myTitle = StringUtil.notNullize(title); myContent = StringUtil.notNullize(content); myType = type; myListener = listener; myTimestamp = System.currentTimeMillis(); myIcon = icon; mySubtitle = subtitle; LOG.assertTrue( isTitle() || isContent(), "Notification should have title: " + title + " and/or subtitle and/or content groupId: " + myGroupId); id = String.valueOf(System.currentTimeMillis()) + "." + String.valueOf(System.identityHashCode(this)); } public Notification( @NotNull String groupDisplayId, @NotNull String title, @NotNull String content, @NotNull NotificationType type) { this(groupDisplayId, title, content, type, null); } /** * @param groupDisplayId this should be a human-readable, capitalized string like "Facet * Detector". It will appear in "Notifications" configurable. * @param title notification title * @param content notification content * @param type notification type * @param listener notification lifecycle listener */ public Notification( @NotNull String groupDisplayId, @NotNull String title, @NotNull String content, @NotNull NotificationType type, @Nullable NotificationListener listener) { myGroupId = groupDisplayId; myTitle = title; myContent = content; myType = type; myListener = listener; myTimestamp = System.currentTimeMillis(); LOG.assertTrue( isContent(), "Notification should have content, title: " + title + ", groupId: " + myGroupId); id = String.valueOf(System.currentTimeMillis()) + "." + String.valueOf(hashCode()); } /** Returns the time (in milliseconds since Jan 1, 1970) when the notification was created. */ public long getTimestamp() { return myTimestamp; } @Nullable public Icon getIcon() { return myIcon; } @NotNull public Notification setIcon(@Nullable Icon icon) { myIcon = icon; return this; } @NotNull public String getGroupId() { return myGroupId; } public boolean isTitle() { return !StringUtil.isEmptyOrSpaces(myTitle) || !StringUtil.isEmptyOrSpaces(mySubtitle); } @NotNull public String getTitle() { return myTitle; } @NotNull public Notification setTitle(@Nullable String title) { myTitle = StringUtil.notNullize(title); return this; } @NotNull public Notification setTitle(@Nullable String title, @Nullable String subtitle) { return setTitle(title).setSubtitle(subtitle); } @Nullable public String getSubtitle() { return mySubtitle; } @NotNull public Notification setSubtitle(@Nullable String subtitle) { mySubtitle = subtitle; return this; } public boolean isContent() { return !StringUtil.isEmptyOrSpaces(myContent); } @NotNull public String getContent() { return myContent; } @NotNull public Notification setContent(@Nullable String content) { myContent = StringUtil.notNullize(content); return this; } @Nullable public NotificationListener getListener() { return myListener; } @NotNull public Notification setListener(@NotNull NotificationListener listener) { myListener = listener; return this; } @NotNull public List<AnAction> getActions() { return ContainerUtil.notNullize(myActions); } @NotNull public static Notification get(@NotNull AnActionEvent e) { //noinspection ConstantConditions return e.getData(KEY); } public static void fire(@NotNull final Notification notification, @NotNull AnAction action) { AnActionEvent event = AnActionEvent.createFromAnAction( action, null, ActionPlaces.UNKNOWN, new DataContext() { @Nullable @Override public Object getData(@NonNls String dataId) { return KEY.getName().equals(dataId) ? notification : null; } }); if (ActionUtil.lastUpdateAndCheckDumb(action, event, false)) { ActionUtil.performActionDumbAware(action, event); } } @NotNull public String getDropDownText() { if (myDropDownText == null) { myDropDownText = "Actions"; } return myDropDownText; } @NotNull public Notification setDropDownText(@NotNull String dropDownText) { myDropDownText = dropDownText; return this; } @NotNull public Notification addAction(@NotNull AnAction action) { if (myActions == null) { myActions = new ArrayList<>(); } myActions.add(action); return this; } @NotNull public NotificationType getType() { return myType; } public boolean isExpired() { return myExpired; } public void expire() { NotificationsManager.getNotificationsManager().expire(this); hideBalloon(); myExpired = true; Runnable whenExpired = myWhenExpired; if (whenExpired != null) whenExpired.run(); } public Notification whenExpired(@Nullable Runnable whenExpired) { myWhenExpired = whenExpired; return this; } public void hideBalloon() { if (myBalloonRef != null) { final Balloon balloon = myBalloonRef.get(); if (balloon != null) { balloon.hide(); } myBalloonRef = null; } } public void setBalloon(@NotNull final Balloon balloon) { hideBalloon(); myBalloonRef = new WeakReference<>(balloon); balloon.addListener( new JBPopupAdapter() { @Override public void onClosed(LightweightWindowEvent event) { if (SoftReference.dereference(myBalloonRef) == balloon) { myBalloonRef = null; } } }); } @Nullable public Balloon getBalloon() { return SoftReference.dereference(myBalloonRef); } public void notify(@Nullable Project project) { Notifications.Bus.notify(this, project); } public Notification setImportant(boolean important) { myImportant = important; return this; } public boolean isImportant() { if (myImportant != null) { return myImportant; } return getListener() != null || !ContainerUtil.isEmpty(myActions); } }
/** @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); } } } }
/** @author Konstantin Bulenkov */ @SuppressWarnings({"unchecked"}) public class DirDiffPanel implements Disposable, DataProvider { private static final Logger LOG = Logger.getInstance(DirDiffPanel.class); public static final String DIVIDER_PROPERTY = "dir.diff.panel.divider.location"; private static final int DIVIDER_PROPERTY_DEFAULT_VALUE = 200; private JPanel myDiffPanel; private JBTable myTable; private JPanel myComponent; private JSplitPane mySplitPanel; private TextFieldWithBrowseButton mySourceDirField; private TextFieldWithBrowseButton myTargetDirField; private JBLabel myTargetDirLabel; private JBLabel mySourceDirLabel; private JPanel myToolBarPanel; private JPanel myRootPanel; private JPanel myFilterPanel; private JBLabel myFilterLabel; private JPanel myFilesPanel; private JPanel myHeaderPanel; private FilterComponent myFilter; private final DirDiffTableModel myModel; private final DirDiffWindow myDiffWindow; private final MyDiffRequestProcessor myDiffRequestProcessor; private final PrevNextDifferenceIterable myPrevNextDifferenceIterable; private String oldFilter; public static final DataKey<DirDiffTableModel> DIR_DIFF_MODEL = DataKey.create("DIR_DIFF_MODEL"); public static final DataKey<JTable> DIR_DIFF_TABLE = DataKey.create("DIR_DIFF_TABLE"); public DirDiffPanel(DirDiffTableModel model, DirDiffWindow wnd) { myModel = model; myDiffWindow = wnd; mySourceDirField.setText(model.getSourceDir().getPath()); myTargetDirField.setText(model.getTargetDir().getPath()); mySourceDirField.setBorder(JBUI.Borders.emptyRight(8)); myTargetDirField.setBorder(JBUI.Borders.emptyRight(12)); mySourceDirLabel.setIcon(model.getSourceDir().getIcon()); myTargetDirLabel.setIcon(model.getTargetDir().getIcon()); myTargetDirLabel.setBorder(JBUI.Borders.emptyLeft(8)); myModel.setTable(myTable); myModel.setPanel(this); Disposer.register(this, myModel); myTable.setModel(myModel); new TableSpeedSearch(myTable); final DirDiffTableCellRenderer renderer = new DirDiffTableCellRenderer(); myTable.setExpandableItemsEnabled(false); myTable.setDefaultRenderer(Object.class, renderer); myTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); final Project project = myModel.getProject(); myTable .getSelectionModel() .addListSelectionListener( new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { final int lastIndex = e.getLastIndex(); final int firstIndex = e.getFirstIndex(); final DirDiffElementImpl last = myModel.getElementAt(lastIndex); final DirDiffElementImpl first = myModel.getElementAt(firstIndex); if (last == null || first == null) { update(false); return; } if (last.isSeparator()) { final int ind = lastIndex + ((lastIndex < firstIndex) ? 1 : -1); myTable.getSelectionModel().addSelectionInterval(ind, ind); } else if (first.isSeparator()) { final int ind = firstIndex + ((firstIndex < lastIndex) ? 1 : -1); myTable.getSelectionModel().addSelectionInterval(ind, ind); } else { update(false); } myDiffWindow.setTitle(myModel.getTitle()); } }); if (model.isOperationsEnabled()) { new AnAction("Change diff operation") { @Override public void actionPerformed(AnActionEvent e) { changeOperationForSelection(); } }.registerCustomShortcutSet(CustomShortcutSet.fromString("SPACE"), myTable); new ClickListener() { @Override public boolean onClick(@NotNull MouseEvent e, int clickCount) { if (e.getButton() == MouseEvent.BUTTON3) return false; if (myTable.getRowCount() > 0) { final int row = myTable.rowAtPoint(e.getPoint()); final int col = myTable.columnAtPoint(e.getPoint()); if (row != -1 && col == ((myTable.getColumnCount() - 1) / 2)) { changeOperationForSelection(); } } return true; } }.installOn(myTable); } myTable.addKeyListener( new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { final int keyCode = e.getKeyCode(); int row; if (keyCode == KeyEvent.VK_DOWN) { row = getNextRow(); } else if (keyCode == KeyEvent.VK_UP) { row = getPrevRow(); } else { row = -1; } if (row != -1) { selectRow(row, e.isShiftDown()); e.consume(); } } }); final TableColumnModel columnModel = myTable.getColumnModel(); final TableColumn operationColumn = columnModel.getColumn((columnModel.getColumnCount() - 1) / 2); operationColumn.setMaxWidth(JBUI.scale(25)); operationColumn.setMinWidth(JBUI.scale(25)); for (int i = 0; i < columnModel.getColumnCount(); i++) { final String name = myModel.getColumnName(i); final TableColumn column = columnModel.getColumn(i); if (DirDiffTableModel.COLUMN_DATE.equals(name)) { column.setMaxWidth(JBUI.scale(90)); column.setMinWidth(JBUI.scale(90)); } else if (DirDiffTableModel.COLUMN_SIZE.equals(name)) { column.setMaxWidth(JBUI.scale(120)); column.setMinWidth(JBUI.scale(120)); } } final DirDiffToolbarActions actions = new DirDiffToolbarActions(myModel, myDiffPanel); final ActionManager actionManager = ActionManager.getInstance(); final ActionToolbar toolbar = actionManager.createActionToolbar("DirDiff", actions, true); registerCustomShortcuts(actions, myTable); myToolBarPanel.add(toolbar.getComponent(), BorderLayout.CENTER); final JBLabel label = new JBLabel( "Use Space button or mouse click to change operation for the selected elements." + " Enter to perform.", SwingConstants.CENTER); label.setForeground(UIUtil.getInactiveTextColor()); UIUtil.applyStyle(UIUtil.ComponentStyle.MINI, label); DataManager.registerDataProvider(myFilesPanel, this); myTable.addMouseListener( new PopupHandler() { @Override public void invokePopup(Component comp, int x, int y) { final JPopupMenu popupMenu = actionManager .createActionPopupMenu( "DirDiffPanel", (ActionGroup) actionManager.getAction("DirDiffMenu")) .getComponent(); popupMenu.show(comp, x, y); } }); myFilesPanel.add(label, BorderLayout.SOUTH); final JBLoadingPanel loadingPanel = new JBLoadingPanel(new BorderLayout(), wnd.getDisposable()); loadingPanel.addListener( new JBLoadingPanelListener.Adapter() { boolean showHelp = true; @Override public void onLoadingFinish() { if (showHelp && myModel.isOperationsEnabled() && myModel.getRowCount() > 0) { final long count = PropertiesComponent.getInstance().getOrInitLong("dir.diff.space.button.info", 0); if (count < 3) { JBPopupFactory.getInstance() .createBalloonBuilder(new JLabel(" Use Space button to change operation")) .setFadeoutTime(5000) .setContentInsets(JBUI.insets(15)) .createBalloon() .show( new RelativePoint(myTable, new Point(myTable.getWidth() / 2, 0)), Balloon.Position.above); PropertiesComponent.getInstance() .setValue("dir.diff.space.button.info", String.valueOf(count + 1)); } } showHelp = false; } }); loadingPanel.add(myComponent, BorderLayout.CENTER); myTable.putClientProperty(myModel.DECORATOR, loadingPanel); myTable.addComponentListener( new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { myTable.removeComponentListener(this); myModel.reloadModel(false); } }); myRootPanel.removeAll(); myRootPanel.add(loadingPanel, BorderLayout.CENTER); myFilter = new FilterComponent("dir.diff.filter", 15, false) { @Override public void filter() { fireFilterUpdated(); } @Override protected void onEscape(KeyEvent e) { e.consume(); focusTable(); } @Override protected JComponent getPopupLocationComponent() { return UIUtil.findComponentOfType( super.getPopupLocationComponent(), JTextComponent.class); } }; myModel.addModelListener( new DirDiffModelListener() { @Override public void updateStarted() { myFilter.setEnabled(false); } @Override public void updateFinished() { myFilter.setEnabled(true); } }); myFilter.getTextEditor().setColumns(10); myFilter.setFilter(myModel.getSettings().getFilter()); // oldFilter = myFilter.getText(); oldFilter = myFilter.getFilter(); myFilterPanel.add(myFilter, BorderLayout.CENTER); myFilterLabel.setLabelFor(myFilter); final Callable<DiffElement> srcChooser = myModel.getSourceDir().getElementChooser(project); final Callable<DiffElement> trgChooser = myModel.getTargetDir().getElementChooser(project); mySourceDirField.setEditable(false); myTargetDirField.setEditable(false); if (srcChooser != null && myModel.getSettings().enableChoosers) { mySourceDirField.setButtonEnabled(true); mySourceDirField.addActionListener( new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { try { final Callable<DiffElement> chooser = myModel.getSourceDir().getElementChooser(project); if (chooser == null) return; final DiffElement newElement = chooser.call(); if (newElement != null) { if (!StringUtil.equals(mySourceDirField.getText(), newElement.getPath())) { myModel.setSourceDir(newElement); mySourceDirField.setText(newElement.getPath()); String shortcutsText = KeymapUtil.getShortcutsText( RefreshDirDiffAction.REFRESH_SHORTCUT.getShortcuts()); myModel.clearWithMessage( "Source or Target has been changed." + " Please run Refresh (" + shortcutsText + ")"); } } } catch (Exception ignored) { } } }); } else { Dimension preferredSize = mySourceDirField.getPreferredSize(); mySourceDirField.setButtonEnabled(false); mySourceDirField.getButton().setVisible(false); mySourceDirField.setPreferredSize(preferredSize); } if (trgChooser != null && myModel.getSettings().enableChoosers) { myTargetDirField.setButtonEnabled(true); myTargetDirField.addActionListener( new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { try { final Callable<DiffElement> chooser = myModel.getTargetDir().getElementChooser(project); if (chooser == null) return; final DiffElement newElement = chooser.call(); if (newElement != null) { myModel.setTargetDir(newElement); myTargetDirField.setText(newElement.getPath()); } } catch (Exception ignored) { } } }); } else { Dimension preferredSize = myTargetDirField.getPreferredSize(); myTargetDirField.setButtonEnabled(false); myTargetDirField.getButton().setVisible(false); myTargetDirField.setPreferredSize(preferredSize); } myDiffRequestProcessor = new MyDiffRequestProcessor(project); Disposer.register(this, myDiffRequestProcessor); myDiffPanel.add(myDiffRequestProcessor.getComponent(), BorderLayout.CENTER); myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable(); } private int getNextRow() { if (myTable.getSelectedRows().length == 0) return -1; int rowCount = myTable.getRowCount(); int row = myTable.getSelectionModel().getLeadSelectionIndex(); while (true) { if (row >= rowCount) return -1; row++; DirDiffElementImpl element = myModel.getElementAt(row); if (element == null) return -1; if (!element.isSeparator()) break; } return row; } private int getPrevRow() { if (myTable.getSelectedRows().length == 0) return -1; int row = myTable.getSelectionModel().getLeadSelectionIndex(); while (true) { if (row <= 0) return -1; row--; DirDiffElementImpl element = myModel.getElementAt(row); if (element == null) return -1; if (!element.isSeparator()) break; } return row; } private void selectRow(int row, boolean extend) { if (row == -1) return; DirDiffElementImpl element = myModel.getElementAt(row); if (element == null || element.isSeparator()) return; myTable.changeSelection(row, (myModel.getColumnCount() - 1) / 2, false, extend); } public AnAction[] getActions() { return new DirDiffToolbarActions(myModel, myDiffPanel).getChildren(null); } public JComponent extractFilterPanel() { myHeaderPanel.setVisible(false); return myFilterPanel; } private void changeOperationForSelection() { for (int row : myTable.getSelectedRows()) { if (row != -1) { final DirDiffElementImpl element = myModel.getElementAt(row); if (element != null) { element.setNextOperation(); myModel.fireTableRowsUpdated(row, row); } } } } public void update(boolean force) { myDiffRequestProcessor.updateRequest(force); } private void registerCustomShortcuts(DirDiffToolbarActions actions, JComponent component) { for (AnAction action : actions.getChildren(null)) { if (action instanceof ShortcutProvider) { final ShortcutSet shortcut = ((ShortcutProvider) action).getShortcut(); if (shortcut != null) { action.registerCustomShortcutSet(shortcut, component); } } } } public void focusTable() { final Project project = myModel.getProject(); final IdeFocusManager focusManager = project == null || project.isDefault() ? IdeFocusManager.getGlobalInstance() : IdeFocusManager.getInstance(project); focusManager.requestFocus(myTable, true); } public String getFilter() { return myFilter.getFilter(); } private void fireFilterUpdated() { final String newFilter = myFilter.getFilter(); if (!StringUtil.equals(oldFilter, newFilter)) { oldFilter = newFilter; myModel.getSettings().setFilter(newFilter); myModel.applySettings(); } } public JComponent getPanel() { return myRootPanel; } public JBTable getTable() { return myTable; } public void dispose() { myModel.stopUpdating(); PropertiesComponent.getInstance() .setValue( DIVIDER_PROPERTY, mySplitPanel.getDividerLocation(), DIVIDER_PROPERTY_DEFAULT_VALUE); } private void createUIComponents() { mySourceDirField = new TextFieldWithBrowseButton(null, this); myTargetDirField = new TextFieldWithBrowseButton(null, this); final AtomicBoolean callUpdate = new AtomicBoolean(true); myRootPanel = new JPanel(new BorderLayout()) { @Override protected void paintChildren(Graphics g) { super.paintChildren(g); if (callUpdate.get()) { callUpdate.set(false); myModel.reloadModel(false); } } }; } public void setupSplitter() { int value = PropertiesComponent.getInstance().getInt(DIVIDER_PROPERTY, DIVIDER_PROPERTY_DEFAULT_VALUE); mySplitPanel.setDividerLocation(Integer.valueOf(value)); } @Override public Object getData(@NonNls String dataId) { if (CommonDataKeys.PROJECT.is(dataId)) { return myModel.getProject(); } else if (DIR_DIFF_MODEL.is(dataId)) { return myModel; } else if (DIR_DIFF_TABLE.is(dataId)) { return myTable; } else if (DiffDataKeys.NAVIGATABLE_ARRAY.is(dataId)) { return getNavigatableArray(); } else if (DiffDataKeys.PREV_NEXT_DIFFERENCE_ITERABLE.is(dataId)) { return myPrevNextDifferenceIterable; } return null; } @Nullable private Navigatable[] getNavigatableArray() { Project project = myModel.getProject(); List<DirDiffElementImpl> elements = myModel.getSelectedElements(); List<Navigatable> navigatables = new ArrayList<>(); for (DirDiffElementImpl element : elements) { DiffElement source = element.getSource(); DiffElement target = element.getTarget(); Navigatable navigatable1 = source != null ? source.getNavigatable(project) : null; Navigatable navigatable2 = target != null ? target.getNavigatable(project) : null; if (navigatable1 != null) navigatables.add(navigatable1); if (navigatable2 != null) navigatables.add(navigatable2); } return ContainerUtil.toArray(navigatables, new Navigatable[navigatables.size()]); } private class MyPrevNextDifferenceIterable implements PrevNextDifferenceIterable { @Override public boolean canGoPrev() { return getPrevRow() != -1; } @Override public boolean canGoNext() { return getNextRow() != -1; } @Override public void goPrev() { selectRow(getPrevRow(), false); } @Override public void goNext() { selectRow(getNextRow(), false); } } private class MyDiffRequestProcessor extends CacheDiffRequestProcessor<ElementWrapper> { public MyDiffRequestProcessor(@Nullable Project project) { super(project, DiffPlaces.DIR_DIFF); } @Nullable @Override protected String getRequestName(@NotNull ElementWrapper element) { return null; } @Override protected ElementWrapper getCurrentRequestProvider() { DirDiffElementImpl element = myModel.getElementAt(myTable.getSelectedRow()); return element != null ? new ElementWrapper(element) : null; } @NotNull @Override protected DiffRequest loadRequest( @NotNull ElementWrapper element, @NotNull ProgressIndicator indicator) throws ProcessCanceledException, DiffRequestProducerException { final Project project = myModel.getProject(); DiffElement sourceElement = element.sourceElement; DiffElement targetElement = element.targetElement; DiffContent sourceContent = sourceElement != null ? sourceElement.createDiffContent(project, indicator) : DiffContentFactory.getInstance().createEmpty(); DiffContent targetContent = targetElement != null ? targetElement.createDiffContent(project, indicator) : DiffContentFactory.getInstance().createEmpty(); return new SimpleDiffRequest(null, sourceContent, targetContent, null, null); } // // Navigation // @Override protected boolean hasNextChange() { return getNextRow() != -1; } @Override protected boolean hasPrevChange() { return getPrevRow() != -1; } @Override protected void goToNextChange(boolean fromDifferences) { selectRow(getNextRow(), false); updateRequest(false, fromDifferences ? ScrollToPolicy.FIRST_CHANGE : null); } @Override protected void goToPrevChange(boolean fromDifferences) { selectRow(getPrevRow(), false); updateRequest(false, fromDifferences ? ScrollToPolicy.LAST_CHANGE : null); } @Override protected boolean isNavigationEnabled() { return myModel.getRowCount() > 0; } } private static class ElementWrapper { @Nullable public final DiffElement sourceElement; @Nullable public final DiffElement targetElement; public ElementWrapper(@NotNull DirDiffElementImpl element) { sourceElement = element.getSource(); targetElement = element.getTarget(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ElementWrapper wrapper = (ElementWrapper) o; if (sourceElement != null ? !sourceElement.equals(wrapper.sourceElement) : wrapper.sourceElement != null) return false; if (targetElement != null ? !targetElement.equals(wrapper.targetElement) : wrapper.targetElement != null) return false; return true; } @Override public int hashCode() { int result = sourceElement != null ? sourceElement.hashCode() : 0; result = 31 * result + (targetElement != null ? targetElement.hashCode() : 0); return result; } } }
public class DomModelTreeView extends Wrapper implements DataProvider, Disposable { public static final DataKey<DomModelTreeView> DATA_KEY = DataKey.create("DOM_MODEL_TREE_VIEW_KEY"); @Deprecated @NonNls public static String DOM_MODEL_TREE_VIEW_KEY = DATA_KEY.getName(); @NonNls public static String DOM_MODEL_TREE_VIEW_POPUP = "DOM_MODEL_TREE_VIEW_POPUP"; private final SimpleTree myTree; private final AbstractTreeBuilder myBuilder; private DomManager myDomManager; @Nullable private DomElement myRootElement; public DomModelTreeView(@NotNull DomElement rootElement) { this(rootElement, rootElement.getManager(), new DomModelTreeStructure(rootElement)); } protected DomModelTreeView( DomElement rootElement, DomManager manager, SimpleTreeStructure treeStructure) { myDomManager = manager; myRootElement = rootElement; myTree = new SimpleTree(new DefaultTreeModel(new DefaultMutableTreeNode())); myTree.setRootVisible(isRootVisible()); myTree.setShowsRootHandles(true); myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); ToolTipManager.sharedInstance().registerComponent(myTree); TreeUtil.installActions(myTree); myBuilder = new AbstractTreeBuilder( myTree, (DefaultTreeModel) myTree.getModel(), treeStructure, WeightBasedComparator.INSTANCE, false); Disposer.register(this, myBuilder); myBuilder.setNodeDescriptorComparator(null); myBuilder.initRootNode(); add(myTree, BorderLayout.CENTER); myTree.addTreeExpansionListener( new TreeExpansionListener() { @Override public void treeExpanded(TreeExpansionEvent event) { final SimpleNode simpleNode = myTree.getNodeFor(event.getPath()); if (simpleNode instanceof AbstractDomElementNode) { ((AbstractDomElementNode) simpleNode).setExpanded(true); } } @Override public void treeCollapsed(TreeExpansionEvent event) { final SimpleNode simpleNode = myTree.getNodeFor(event.getPath()); if (simpleNode instanceof AbstractDomElementNode) { ((AbstractDomElementNode) simpleNode).setExpanded(false); simpleNode.update(); } } }); myDomManager.addDomEventListener( new DomChangeAdapter() { @Override protected void elementChanged(DomElement element) { if (element.isValid()) { queueUpdate(DomUtil.getFile(element).getVirtualFile()); } else if (element instanceof DomFileElement) { final XmlFile xmlFile = ((DomFileElement) element).getFile(); queueUpdate(xmlFile.getVirtualFile()); } } }, this); final Project project = myDomManager.getProject(); DomElementAnnotationsManager.getInstance(project) .addHighlightingListener( new DomElementAnnotationsManager.DomHighlightingListener() { @Override public void highlightingFinished(@NotNull DomFileElement element) { if (element.isValid()) { queueUpdate(DomUtil.getFile(element).getVirtualFile()); } } }, this); myTree.setPopupGroup(getPopupActions(), DOM_MODEL_TREE_VIEW_POPUP); } protected boolean isRightFile(final VirtualFile file) { return myRootElement == null || (myRootElement.isValid() && file.equals(DomUtil.getFile(myRootElement).getVirtualFile())); } private void queueUpdate(final VirtualFile file) { if (file == null) return; if (getProject().isDisposed()) return; ApplicationManager.getApplication() .invokeLater( () -> { if (getProject().isDisposed()) return; if (!file.isValid() || isRightFile(file)) { myBuilder.updateFromRoot(); } }); } protected boolean isRootVisible() { return true; } public final void updateTree() { myBuilder.updateFromRoot(); } public DomElement getRootElement() { return myRootElement; } protected final Project getProject() { return myDomManager.getProject(); } public AbstractTreeBuilder getBuilder() { return myBuilder; } @Override public void dispose() {} public SimpleTree getTree() { return myTree; } protected ActionGroup getPopupActions() { DefaultActionGroup group = new DefaultActionGroup(); group.add(ActionManager.getInstance().getAction("DomElementsTreeView.TreePopup")); group.addSeparator(); group.add(new ExpandAllAction(myTree)); group.add(new CollapseAllAction(myTree)); return group; } @Override @Nullable public Object getData(String dataId) { if (DATA_KEY.is(dataId)) { return this; } final SimpleNode simpleNode = getTree().getSelectedNode(); if (simpleNode instanceof AbstractDomElementNode) { final DomElement domElement = ((AbstractDomElementNode) simpleNode).getDomElement(); if (domElement != null && domElement.isValid()) { if (CommonDataKeys.NAVIGATABLE.is(dataId)) { final XmlElement tag = domElement.getXmlElement(); if (tag instanceof Navigatable) { return tag; } } } } return null; } public void setSelectedDomElement(final DomElement domElement) { if (domElement == null) return; final SimpleNode node = getNodeFor(domElement); if (node != null) { getTree().setSelectedNode(getBuilder(), node, true); } } @Nullable private SimpleNode getNodeFor(final DomElement domElement) { return visit((SimpleNode) myBuilder.getTreeStructure().getRootElement(), domElement); } @Nullable private SimpleNode visit(SimpleNode simpleNode, DomElement domElement) { boolean validCandidate = false; if (simpleNode instanceof AbstractDomElementNode) { final DomElement nodeElement = ((AbstractDomElementNode) simpleNode).getDomElement(); if (nodeElement != null) { validCandidate = !(simpleNode instanceof DomElementsGroupNode); if (validCandidate && nodeElement.equals(domElement)) { return simpleNode; } if (!(nodeElement instanceof MergedObject) && !isParent(nodeElement, domElement)) { return null; } } } final Object[] childElements = myBuilder.getTreeStructure().getChildElements(simpleNode); if (childElements.length == 0 && validCandidate) { // leaf return simpleNode; } for (Object child : childElements) { SimpleNode result = visit((SimpleNode) child, domElement); if (result != null) { return result; } } return validCandidate ? simpleNode : null; } private static boolean isParent(final DomElement potentialParent, final DomElement domElement) { DomElement currParent = domElement; while (currParent != null) { if (currParent.equals(potentialParent)) return true; currParent = currParent.getParent(); } return false; } }
public class ProjectStructureConfigurable extends BaseConfigurable implements SearchableConfigurable, Place.Navigator { public static final DataKey<ProjectStructureConfigurable> KEY = DataKey.create("ProjectStructureConfiguration"); protected final UIState myUiState = new UIState(); private JBSplitter mySplitter; private JComponent myToolbarComponent; @NonNls public static final String CATEGORY = "category"; private JComponent myToFocus; private boolean myWasUiDisposed; private ConfigurationErrorsComponent myErrorsComponent; public static class UIState { public float proportion; public float sideProportion; public String lastEditedConfigurable; } private final Project myProject; private final FacetStructureConfigurable myFacetStructureConfigurable; private final ArtifactsStructureConfigurable myArtifactsStructureConfigurable; private History myHistory = new History(this); private SidePanel mySidePanel; private JPanel myComponent; private final Wrapper myDetails = new Wrapper(); private Configurable mySelectedConfigurable; private final ProjectSdksModel myProjectJdksModel = new ProjectSdksModel(); private ProjectConfigurable myProjectConfig; private final ProjectLibrariesConfigurable myProjectLibrariesConfig; private final GlobalLibrariesConfigurable myGlobalLibrariesConfig; private ModuleStructureConfigurable myModulesConfig; private boolean myUiInitialized; private final List<Configurable> myName2Config = new ArrayList<Configurable>(); private final StructureConfigurableContext myContext; private final ModulesConfigurator myModuleConfigurator; private JdkListConfigurable myJdkListConfig; private final JLabel myEmptySelection = new JLabel( "<html><body><center>Select a setting to view or edit its details here</center></body></html>", JLabel.CENTER); public ProjectStructureConfigurable( final Project project, final ProjectLibrariesConfigurable projectLibrariesConfigurable, final GlobalLibrariesConfigurable globalLibrariesConfigurable, final ModuleStructureConfigurable moduleStructureConfigurable, FacetStructureConfigurable facetStructureConfigurable, ArtifactsStructureConfigurable artifactsStructureConfigurable) { myProject = project; myFacetStructureConfigurable = facetStructureConfigurable; myArtifactsStructureConfigurable = artifactsStructureConfigurable; myModuleConfigurator = new ModulesConfigurator(myProject); myContext = new StructureConfigurableContext(myProject, myModuleConfigurator); myModuleConfigurator.setContext(myContext); myProjectLibrariesConfig = projectLibrariesConfigurable; myGlobalLibrariesConfig = globalLibrariesConfigurable; myModulesConfig = moduleStructureConfigurable; myProjectLibrariesConfig.init(myContext); myGlobalLibrariesConfig.init(myContext); myModulesConfig.init(myContext); myFacetStructureConfigurable.init(myContext); if (!project.isDefault()) { myArtifactsStructureConfigurable.init( myContext, myModulesConfig, myProjectLibrariesConfig, myGlobalLibrariesConfig); } final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(myProject); myUiState.lastEditedConfigurable = propertiesComponent.getValue("project.structure.last.edited"); final String proportion = propertiesComponent.getValue("project.structure.proportion"); myUiState.proportion = proportion != null ? Float.parseFloat(proportion) : 0; final String sideProportion = propertiesComponent.getValue("project.structure.side.proportion"); myUiState.sideProportion = sideProportion != null ? Float.parseFloat(sideProportion) : 0; } @Override @NotNull @NonNls public String getId() { return "project.structure"; } @Override @Nullable public Runnable enableSearch(final String option) { return null; } @Override @Nls public String getDisplayName() { return ProjectBundle.message("project.settings.display.name"); } @Override @Nullable @NonNls public String getHelpTopic() { return mySelectedConfigurable != null ? mySelectedConfigurable.getHelpTopic() : ""; } @Override public JComponent createComponent() { myComponent = new MyPanel(); mySplitter = new JBSplitter(false, .15f); mySplitter.setSplitterProportionKey("ProjectStructure.TopLevelElements"); mySplitter.setHonorComponentsMinimumSize(true); if (Registry.is("ide.new.project.settings")) { mySplitter.setOnePixelMode(); } initSidePanel(); final JPanel left = new JPanel(new BorderLayout()) { @Override public Dimension getMinimumSize() { final Dimension original = super.getMinimumSize(); return new Dimension(Math.max(original.width, 100), original.height); } }; final DefaultActionGroup toolbarGroup = new DefaultActionGroup(); toolbarGroup.add(new BackAction(myComponent)); toolbarGroup.add(new ForwardAction(myComponent)); final ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarGroup, true); toolbar.setTargetComponent(myComponent); myToolbarComponent = toolbar.getComponent(); if (Registry.is("ide.new.project.settings")) { left.setBackground(UIUtil.getSidePanelColor()); } else { left.add(myToolbarComponent, BorderLayout.NORTH); } left.add(mySidePanel, BorderLayout.CENTER); mySplitter.setFirstComponent(left); mySplitter.setSecondComponent(myDetails); myComponent.add(mySplitter, BorderLayout.CENTER); myErrorsComponent = new ConfigurationErrorsComponent(myProject); if (!Registry.is("ide.new.project.settings")) { myComponent.add(myErrorsComponent, BorderLayout.SOUTH); } myUiInitialized = true; return myComponent; } private void initSidePanel() { boolean isDefaultProject = myProject == ProjectManager.getInstance().getDefaultProject(); mySidePanel = new SidePanel(this, myHistory); mySidePanel.addSeparator("Project Settings"); addProjectConfig(); if (!isDefaultProject) { addModulesConfig(); } addProjectLibrariesConfig(); if (!isDefaultProject) { addFacetsConfig(); addArtifactsConfig(); } ProjectStructureConfigurableContributor[] adders = ProjectStructureConfigurableContributor.EP_NAME.getExtensions(); for (ProjectStructureConfigurableContributor adder : adders) { for (Configurable configurable : adder.getExtraProjectConfigurables(myProject, myContext)) { addConfigurable(configurable, true); } } mySidePanel.addSeparator("Platform Settings"); addJdkListConfig(); addGlobalLibrariesConfig(); for (ProjectStructureConfigurableContributor adder : adders) { for (Configurable configurable : adder.getExtraPlatformConfigurables(myProject, myContext)) { addConfigurable(configurable, true); } } if (Registry.is("ide.new.project.settings")) { mySidePanel.addSeparator("--"); addErrorPane(); } } private void addArtifactsConfig() { addConfigurable(myArtifactsStructureConfigurable, ConfigurableId.ARTIFACTS); } private void addConfigurable( final Configurable configurable, final ConfigurableId configurableId) { addConfigurable(configurable, isAvailable(configurableId)); } private boolean isAvailable(ConfigurableId id) { for (ProjectStructureConfigurableFilter filter : ProjectStructureConfigurableFilter.EP_NAME.getExtensions()) { if (!filter.isAvailable(id, myProject)) { return false; } } return true; } public ArtifactsStructureConfigurable getArtifactsStructureConfigurable() { return myArtifactsStructureConfigurable; } private void addFacetsConfig() { if (myFacetStructureConfigurable.isVisible()) { addConfigurable(myFacetStructureConfigurable, ConfigurableId.FACETS); } } private void addJdkListConfig() { if (myJdkListConfig == null) { myJdkListConfig = JdkListConfigurable.getInstance(myProject); myJdkListConfig.init(myContext); } addConfigurable(myJdkListConfig, ConfigurableId.JDK_LIST); } private void addProjectConfig() { myProjectConfig = new ProjectConfigurable(myProject, myContext, myModuleConfigurator, myProjectJdksModel); addConfigurable(myProjectConfig, ConfigurableId.PROJECT); } private void addProjectLibrariesConfig() { addConfigurable(myProjectLibrariesConfig, ConfigurableId.PROJECT_LIBRARIES); } private void addErrorPane() { addConfigurable(new ErrorPaneConfigurable(myProject, myContext), true); } private void addGlobalLibrariesConfig() { addConfigurable(myGlobalLibrariesConfig, ConfigurableId.GLOBAL_LIBRARIES); } private void addModulesConfig() { myModulesConfig = ModuleStructureConfigurable.getInstance(myProject); addConfigurable(myModulesConfig, ConfigurableId.MODULES); } @Override public boolean isModified() { for (Configurable each : myName2Config) { if (each.isModified()) return true; } return false; } @Override public void apply() throws ConfigurationException { for (Configurable each : myName2Config) { if (each instanceof BaseStructureConfigurable && each.isModified()) { ((BaseStructureConfigurable) each).checkCanApply(); } } for (Configurable each : myName2Config) { if (each.isModified()) { each.apply(); } } myContext.getDaemonAnalyzer().clearCaches(); SwingUtilities.invokeLater( new Runnable() { public void run() { BuildManager.getInstance().scheduleAutoMake(); } }); } @Override public void reset() { // need this to ensure VFS operations will not block because of storage flushing // and other maintenance IO tasks run in background HeavyProcessLatch.INSTANCE.processStarted(); try { myWasUiDisposed = false; myContext.reset(); myProjectJdksModel.reset(myProject); Configurable toSelect = null; for (Configurable each : myName2Config) { if (myUiState.lastEditedConfigurable != null && myUiState.lastEditedConfigurable.equals(each.getDisplayName())) { toSelect = each; } if (each instanceof MasterDetailsComponent) { ((MasterDetailsComponent) each).setHistory(myHistory); } each.reset(); } myHistory.clear(); if (toSelect == null && myName2Config.size() > 0) { toSelect = myName2Config.iterator().next(); } removeSelected(); navigateTo(toSelect != null ? createPlaceFor(toSelect) : null, false); if (myUiState.proportion > 0) { mySplitter.setProportion(myUiState.proportion); } } finally { HeavyProcessLatch.INSTANCE.processFinished(); } } public void hideSidePanel() { mySplitter.getFirstComponent().setVisible(false); } @Override public void disposeUIResources() { if (!myUiInitialized) return; final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(myProject); propertiesComponent.setValue("project.structure.last.edited", myUiState.lastEditedConfigurable); propertiesComponent.setValue( "project.structure.proportion", String.valueOf(myUiState.proportion)); propertiesComponent.setValue( "project.structure.side.proportion", String.valueOf(myUiState.sideProportion)); myWasUiDisposed = true; myUiState.proportion = mySplitter.getProportion(); saveSideProportion(); myContext.getDaemonAnalyzer().stop(); for (Configurable each : myName2Config) { each.disposeUIResources(); } myContext.clear(); myName2Config.clear(); myModuleConfigurator.getFacetsConfigurator().clearMaps(); Disposer.dispose(myErrorsComponent); myUiInitialized = false; } public boolean isUiInitialized() { return myUiInitialized; } public History getHistory() { return myHistory; } @Override public void setHistory(final History history) { myHistory = history; } @Override public void queryPlace(@NotNull final Place place) { place.putPath(CATEGORY, mySelectedConfigurable); Place.queryFurther(mySelectedConfigurable, place); } public ActionCallback selectProjectGeneralSettings(final boolean requestFocus) { Place place = createPlaceFor(myProjectConfig); return navigateTo(place, requestFocus); } public ActionCallback select( @Nullable final String moduleToSelect, @Nullable String editorNameToSelect, final boolean requestFocus) { Place place = createModulesPlace(); if (moduleToSelect != null) { final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleToSelect); assert module != null; place = place .putPath(ModuleStructureConfigurable.TREE_OBJECT, module) .putPath(ModuleEditor.SELECTED_EDITOR_NAME, editorNameToSelect); } return navigateTo(place, requestFocus); } public Place createModulesPlace() { return createPlaceFor(myModulesConfig); } public Place createModulePlace(@NotNull Module module) { return createModulesPlace().putPath(ModuleStructureConfigurable.TREE_OBJECT, module); } public ActionCallback select(@Nullable final Facet facetToSelect, final boolean requestFocus) { Place place = createModulesPlace(); if (facetToSelect != null) { place = place.putPath(ModuleStructureConfigurable.TREE_OBJECT, facetToSelect); } return navigateTo(place, requestFocus); } public ActionCallback select(@NotNull Sdk sdk, final boolean requestFocus) { Place place = createPlaceFor(myJdkListConfig); place.putPath(BaseStructureConfigurable.TREE_NAME, sdk.getName()); return navigateTo(place, requestFocus); } public ActionCallback selectGlobalLibraries(final boolean requestFocus) { Place place = createPlaceFor(myGlobalLibrariesConfig); return navigateTo(place, requestFocus); } public ActionCallback selectProjectOrGlobalLibrary( @NotNull Library library, boolean requestFocus) { Place place = createProjectOrGlobalLibraryPlace(library); return navigateTo(place, requestFocus); } public Place createProjectOrGlobalLibraryPlace(Library library) { Place place = createPlaceFor(getConfigurableFor(library)); place.putPath(BaseStructureConfigurable.TREE_NAME, library.getName()); return place; } public ActionCallback select(@Nullable Artifact artifact, boolean requestFocus) { Place place = createArtifactPlace(artifact); return navigateTo(place, requestFocus); } public Place createArtifactPlace(Artifact artifact) { Place place = createPlaceFor(myArtifactsStructureConfigurable); if (artifact != null) { place.putPath(BaseStructureConfigurable.TREE_NAME, artifact.getName()); } return place; } public ActionCallback select( @NotNull LibraryOrderEntry libraryOrderEntry, final boolean requestFocus) { final Library lib = libraryOrderEntry.getLibrary(); if (lib == null || lib.getTable() == null) { return selectOrderEntry(libraryOrderEntry.getOwnerModule(), libraryOrderEntry); } Place place = createPlaceFor(getConfigurableFor(lib)); place.putPath(BaseStructureConfigurable.TREE_NAME, libraryOrderEntry.getLibraryName()); return navigateTo(place, requestFocus); } public ActionCallback selectOrderEntry( @NotNull final Module module, @Nullable final OrderEntry orderEntry) { return ModuleStructureConfigurable.getInstance(myProject).selectOrderEntry(module, orderEntry); } @Override public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) { final Configurable toSelect = (Configurable) place.getPath(CATEGORY); JComponent detailsContent = myDetails.getTargetComponent(); if (mySelectedConfigurable != toSelect) { if (mySelectedConfigurable instanceof BaseStructureConfigurable) { ((BaseStructureConfigurable) mySelectedConfigurable).onStructureUnselected(); } saveSideProportion(); removeSelected(); if (toSelect != null) { detailsContent = toSelect.createComponent(); myDetails.setContent(detailsContent); } mySelectedConfigurable = toSelect; if (mySelectedConfigurable != null) { myUiState.lastEditedConfigurable = mySelectedConfigurable.getDisplayName(); } if (toSelect instanceof MasterDetailsComponent) { final MasterDetailsComponent masterDetails = (MasterDetailsComponent) toSelect; if (myUiState.sideProportion > 0) { masterDetails.getSplitter().setProportion(myUiState.sideProportion); } masterDetails.setHistory(myHistory); } if (toSelect instanceof DetailsComponent.Facade) { ((DetailsComponent.Facade) toSelect) .getDetailsComponent() .setBannerMinHeight(myToolbarComponent.getPreferredSize().height); } if (toSelect instanceof BaseStructureConfigurable) { ((BaseStructureConfigurable) toSelect).onStructureSelected(); } } if (detailsContent != null) { JComponent toFocus = IdeFocusTraversalPolicy.getPreferredFocusedComponent(detailsContent); if (toFocus == null) { toFocus = detailsContent; } if (requestFocus) { myToFocus = toFocus; UIUtil.requestFocus(toFocus); } } final ActionCallback result = new ActionCallback(); Place.goFurther(toSelect, place, requestFocus).notifyWhenDone(result); myDetails.revalidate(); myDetails.repaint(); if (toSelect != null) { mySidePanel.select(createPlaceFor(toSelect)); } if (!myHistory.isNavigatingNow() && mySelectedConfigurable != null) { myHistory.pushQueryPlace(); } return result; } private void saveSideProportion() { if (mySelectedConfigurable instanceof MasterDetailsComponent) { myUiState.sideProportion = ((MasterDetailsComponent) mySelectedConfigurable).getSplitter().getProportion(); } } private void removeSelected() { myDetails.removeAll(); mySelectedConfigurable = null; myUiState.lastEditedConfigurable = null; myDetails.add(myEmptySelection, BorderLayout.CENTER); } public static ProjectStructureConfigurable getInstance(final Project project) { return ServiceManager.getService(project, ProjectStructureConfigurable.class); } public ProjectSdksModel getProjectJdksModel() { return myProjectJdksModel; } public JdkListConfigurable getJdkConfig() { return myJdkListConfig; } public ProjectLibrariesConfigurable getProjectLibrariesConfig() { return myProjectLibrariesConfig; } public GlobalLibrariesConfigurable getGlobalLibrariesConfig() { return myGlobalLibrariesConfig; } public ModuleStructureConfigurable getModulesConfig() { return myModulesConfig; } public ProjectConfigurable getProjectConfig() { return myProjectConfig; } private void addConfigurable(Configurable configurable, boolean addToSidePanel) { myName2Config.add(configurable); if (addToSidePanel) { mySidePanel.addPlace( createPlaceFor(configurable), new Presentation(configurable.getDisplayName())); } } private static Place createPlaceFor(final Configurable configurable) { return new Place().putPath(CATEGORY, configurable); } public StructureConfigurableContext getContext() { return myContext; } private class MyPanel extends JPanel implements DataProvider { public MyPanel() { super(new BorderLayout()); } @Override @Nullable public Object getData(@NonNls final String dataId) { if (KEY.is(dataId)) { return ProjectStructureConfigurable.this; } else if (History.KEY.is(dataId)) { return getHistory(); } else { return null; } } @Override public Dimension getPreferredSize() { return new Dimension(1024, 768); } } public BaseLibrariesConfigurable getConfigurableFor(final Library library) { if (LibraryTablesRegistrar.PROJECT_LEVEL.equals(library.getTable().getTableLevel())) { return myProjectLibrariesConfig; } else { return myGlobalLibrariesConfig; } } @Override public JComponent getPreferredFocusedComponent() { return myToFocus; } protected void hideErrorsComponent() { myErrorsComponent.setVisible(false); } }
public abstract class ChangesBrowserBase<T> extends JPanel implements TypeSafeDataProvider, Disposable { private static final Logger LOG = Logger.getInstance(ChangesBrowserBase.class); // for backgroundable rollback to mark private boolean myDataIsDirty; protected final Class<T> myClass; protected final ChangesTreeList<T> myViewer; protected final JScrollPane myViewerScrollPane; protected ChangeList mySelectedChangeList; protected List<T> myChangesToDisplay; protected final Project myProject; private final boolean myCapableOfExcludingChanges; protected final JPanel myHeaderPanel; private JComponent myBottomPanel; private DefaultActionGroup myToolBarGroup; private String myToggleActionTitle = VcsBundle.message("commit.dialog.include.action.name"); private JComponent myDiffBottomComponent; public static DataKey<ChangesBrowserBase> DATA_KEY = DataKey.create("com.intellij.openapi.vcs.changes.ui.ChangesBrowser"); private AnAction myDiffAction; private final VirtualFile myToSelect; @NotNull private final DeleteProvider myDeleteProvider = new VirtualFileDeleteProvider(); public void setChangesToDisplay(final List<T> changes) { myChangesToDisplay = changes; myViewer.setChangesToDisplay(changes); } public void setDecorator(final ChangeNodeDecorator decorator) { myViewer.setChangeDecorator(decorator); } protected ChangesBrowserBase( final Project project, @NotNull List<T> changes, final boolean capableOfExcludingChanges, final boolean highlightProblems, @Nullable final Runnable inclusionListener, ChangesBrowser.MyUseCase useCase, @Nullable VirtualFile toSelect, Class<T> clazz) { super(new BorderLayout()); setFocusable(false); myClass = clazz; myDataIsDirty = false; myProject = project; myCapableOfExcludingChanges = capableOfExcludingChanges; myToSelect = toSelect; ChangeNodeDecorator decorator = ChangesBrowser.MyUseCase.LOCAL_CHANGES.equals(useCase) ? RemoteRevisionsCache.getInstance(myProject).getChangesNodeDecorator() : null; myViewer = new ChangesTreeList<T>( myProject, changes, capableOfExcludingChanges, highlightProblems, inclusionListener, decorator) { protected DefaultTreeModel buildTreeModel( final List<T> changes, ChangeNodeDecorator changeNodeDecorator) { return ChangesBrowserBase.this.buildTreeModel( changes, changeNodeDecorator, isShowFlatten()); } protected List<T> getSelectedObjects(final ChangesBrowserNode<T> node) { return ChangesBrowserBase.this.getSelectedObjects(node); } @Nullable protected T getLeadSelectedObject(final ChangesBrowserNode node) { return ChangesBrowserBase.this.getLeadSelectedObject(node); } @Override public void setScrollPaneBorder(Border border) { myViewerScrollPane.setBorder(border); } }; myViewerScrollPane = ScrollPaneFactory.createScrollPane(myViewer); myHeaderPanel = new JPanel(new BorderLayout()); } protected void init() { add(myViewerScrollPane, BorderLayout.CENTER); myHeaderPanel.add(createToolbar(), BorderLayout.CENTER); add(myHeaderPanel, BorderLayout.NORTH); myBottomPanel = new JPanel(new BorderLayout()); add(myBottomPanel, BorderLayout.SOUTH); myViewer.installPopupHandler(myToolBarGroup); myViewer.setDoubleClickHandler(getDoubleClickHandler()); } @NotNull protected abstract DefaultTreeModel buildTreeModel( final List<T> changes, ChangeNodeDecorator changeNodeDecorator, boolean showFlatten); @NotNull protected abstract List<T> getSelectedObjects(@NotNull ChangesBrowserNode<T> node); @Nullable protected abstract T getLeadSelectedObject(@NotNull ChangesBrowserNode node); @NotNull protected Runnable getDoubleClickHandler() { return new Runnable() { public void run() { showDiff(); } }; } protected void setInitialSelection( final List<? extends ChangeList> changeLists, @NotNull List<T> changes, final ChangeList initialListSelection) { mySelectedChangeList = initialListSelection; } public void dispose() {} public void addToolbarAction(AnAction action) { myToolBarGroup.add(action); } public void setDiffBottomComponent(JComponent diffBottomComponent) { myDiffBottomComponent = diffBottomComponent; } public void setToggleActionTitle(final String toggleActionTitle) { myToggleActionTitle = toggleActionTitle; } public JPanel getHeaderPanel() { return myHeaderPanel; } public ChangesTreeList<T> getViewer() { return myViewer; } @NotNull public JScrollPane getViewerScrollPane() { return myViewerScrollPane; } public void calcData(DataKey key, DataSink sink) { if (key == VcsDataKeys.CHANGES) { List<Change> list = getSelectedChanges(); if (list.isEmpty()) list = getAllChanges(); sink.put(VcsDataKeys.CHANGES, list.toArray(new Change[list.size()])); } else if (key == VcsDataKeys.CHANGES_SELECTION) { sink.put(VcsDataKeys.CHANGES_SELECTION, getChangesSelection()); } else if (key == VcsDataKeys.CHANGE_LISTS) { sink.put(VcsDataKeys.CHANGE_LISTS, getSelectedChangeLists()); } else if (key == VcsDataKeys.CHANGE_LEAD_SELECTION) { final Change highestSelection = ObjectUtils.tryCast(myViewer.getHighestLeadSelection(), Change.class); sink.put( VcsDataKeys.CHANGE_LEAD_SELECTION, (highestSelection == null) ? new Change[] {} : new Change[] {highestSelection}); } else if (key == CommonDataKeys.VIRTUAL_FILE_ARRAY) { sink.put(CommonDataKeys.VIRTUAL_FILE_ARRAY, getSelectedFiles().toArray(VirtualFile[]::new)); } else if (key == CommonDataKeys.NAVIGATABLE_ARRAY) { sink.put( CommonDataKeys.NAVIGATABLE_ARRAY, getNavigatableArray(myProject, getSelectedFiles())); } else if (VcsDataKeys.IO_FILE_ARRAY.equals(key)) { sink.put(VcsDataKeys.IO_FILE_ARRAY, getSelectedIoFiles()); } else if (key == DATA_KEY) { sink.put(DATA_KEY, this); } else if (VcsDataKeys.SELECTED_CHANGES_IN_DETAILS.equals(key)) { final List<Change> selectedChanges = getSelectedChanges(); sink.put( VcsDataKeys.SELECTED_CHANGES_IN_DETAILS, selectedChanges.toArray(new Change[selectedChanges.size()])); } else if (UNVERSIONED_FILES_DATA_KEY.equals(key)) { sink.put( UNVERSIONED_FILES_DATA_KEY, getVirtualFiles(myViewer.getSelectionPaths(), UNVERSIONED_FILES_TAG)); } else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.equals(key)) { sink.put(PlatformDataKeys.DELETE_ELEMENT_PROVIDER, myDeleteProvider); } } public void select(List<T> changes) { myViewer.select(changes); } public JComponent getBottomPanel() { return myBottomPanel; } private class ToggleChangeAction extends CheckboxAction { public ToggleChangeAction() { super(myToggleActionTitle); } public boolean isSelected(AnActionEvent e) { T change = ObjectUtils.tryCast(e.getData(VcsDataKeys.CURRENT_CHANGE), myClass); if (change == null) return false; return myViewer.isIncluded(change); } public void setSelected(AnActionEvent e, boolean state) { T change = ObjectUtils.tryCast(e.getData(VcsDataKeys.CURRENT_CHANGE), myClass); if (change == null) return; if (state) { myViewer.includeChange(change); } else { myViewer.excludeChange(change); } } } protected void showDiffForChanges(Change[] changesArray, final int indexInSelection) { final ShowDiffContext context = new ShowDiffContext(isInFrame() ? DiffDialogHints.FRAME : DiffDialogHints.MODAL); context.addActions(createDiffActions()); if (myDiffBottomComponent != null) { context.putChainContext(DiffUserDataKeysEx.BOTTOM_PANEL, myDiffBottomComponent); } updateDiffContext(context); ShowDiffAction.showDiffForChange( myProject, Arrays.asList(changesArray), indexInSelection, context); } protected void updateDiffContext(@NotNull ShowDiffContext context) {} private boolean canShowDiff() { return ShowDiffAction.canShowDiff(myProject, getChangesSelection().getChanges()); } private void showDiff() { ChangesSelection selection = getChangesSelection(); List<Change> changes = selection.getChanges(); Change[] changesArray = changes.toArray(new Change[changes.size()]); showDiffForChanges(changesArray, selection.getIndex()); afterDiffRefresh(); } @NotNull protected ChangesSelection getChangesSelection() { final Change leadSelection = ObjectUtils.tryCast(myViewer.getLeadSelection(), Change.class); List<Change> changes = getSelectedChanges(); if (changes.size() < 2) { List<Change> allChanges = getAllChanges(); if (allChanges.size() > 1 || changes.isEmpty()) { changes = allChanges; } } if (leadSelection != null) { int indexInSelection = changes.indexOf(leadSelection); if (indexInSelection == -1) { return new ChangesSelection(Collections.singletonList(leadSelection), 0); } else { return new ChangesSelection(changes, indexInSelection); } } else { return new ChangesSelection(changes, 0); } } protected void afterDiffRefresh() {} private static boolean isInFrame() { return ModalityState.current().equals(ModalityState.NON_MODAL); } protected List<AnAction> createDiffActions() { List<AnAction> actions = new ArrayList<>(); if (myCapableOfExcludingChanges) { actions.add(new ToggleChangeAction()); } return actions; } public void rebuildList() { myViewer.setChangesToDisplay(getCurrentDisplayedObjects(), myToSelect); } public void setAlwayExpandList(final boolean value) { myViewer.setAlwaysExpandList(value); } @NotNull protected JComponent createToolbar() { DefaultActionGroup toolbarGroups = new DefaultActionGroup(); myToolBarGroup = new DefaultActionGroup(); toolbarGroups.add(myToolBarGroup); buildToolBar(myToolBarGroup); toolbarGroups.addSeparator(); DefaultActionGroup treeActionsGroup = new DefaultActionGroup(); toolbarGroups.add(treeActionsGroup); for (AnAction action : myViewer.getTreeActions()) { treeActionsGroup.add(action); } ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.TOOLBAR, toolbarGroups, true); toolbar.setTargetComponent(this); return toolbar.getComponent(); } protected void buildToolBar(final DefaultActionGroup toolBarGroup) { myDiffAction = new DumbAwareAction() { public void update(AnActionEvent e) { e.getPresentation().setEnabled(canShowDiff()); } public void actionPerformed(AnActionEvent e) { showDiff(); } }; ActionUtil.copyFrom(myDiffAction, "ChangesView.Diff"); myDiffAction.registerCustomShortcutSet(myViewer, null); toolBarGroup.add(myDiffAction); } @NotNull public Set<AbstractVcs> getAffectedVcses() { return ChangesUtil.getAffectedVcses(getCurrentDisplayedChanges(), myProject); } @NotNull public abstract List<Change> getCurrentIncludedChanges(); @NotNull public List<Change> getCurrentDisplayedChanges() { return mySelectedChangeList != null ? ContainerUtil.newArrayList(mySelectedChangeList.getChanges()) : Collections.emptyList(); } @NotNull public abstract List<T> getCurrentDisplayedObjects(); @NotNull public List<VirtualFile> getIncludedUnversionedFiles() { return Collections.emptyList(); } public int getUnversionedFilesCount() { return 0; } public ChangeList getSelectedChangeList() { return mySelectedChangeList; } public JComponent getPreferredFocusedComponent() { return myViewer.getPreferredFocusedComponent(); } private ChangeList[] getSelectedChangeLists() { if (mySelectedChangeList != null) { return new ChangeList[] {mySelectedChangeList}; } return null; } private File[] getSelectedIoFiles() { final List<Change> changes = getSelectedChanges(); final List<File> files = new ArrayList<>(); for (Change change : changes) { final ContentRevision afterRevision = change.getAfterRevision(); if (afterRevision != null) { final FilePath file = afterRevision.getFile(); final File ioFile = file.getIOFile(); files.add(ioFile); } } return files.toArray(new File[files.size()]); } @NotNull public abstract List<Change> getSelectedChanges(); @NotNull public abstract List<Change> getAllChanges(); @NotNull protected Stream<VirtualFile> getSelectedFiles() { return Stream.concat( getAfterRevisionsFiles(getSelectedChanges().stream()), getVirtualFiles(myViewer.getSelectionPaths(), null)) .distinct(); } public AnAction getDiffAction() { return myDiffAction; } public boolean isDataIsDirty() { return myDataIsDirty; } public void setDataIsDirty(boolean dataIsDirty) { myDataIsDirty = dataIsDirty; } public void setSelectionMode(@JdkConstants.TreeSelectionMode int mode) { myViewer.setSelectionMode(mode); } @Contract(pure = true) @NotNull protected static <T> List<Change> findChanges(@NotNull Collection<T> items) { return ContainerUtil.findAll(items, Change.class); } static boolean isUnderUnversioned(@NotNull ChangesBrowserNode node) { return isUnderTag(new TreePath(node.getPath()), UNVERSIONED_FILES_TAG); } }
/** @author michael.golubev */ public class ServersToolWindowContent extends JPanel implements Disposable { public static final DataKey<ServersToolWindowContent> KEY = DataKey.create("serversToolWindowContent"); @NonNls private static final String PLACE_TOOLBAR = "ServersToolWindowContent#Toolbar"; @NonNls private static final String SERVERS_TOOL_WINDOW_TOOLBAR = "RemoteServersViewToolbar"; @NonNls private static final String HELP_ID = "Application_Servers_tool_window"; private static final String MESSAGE_CARD = "message"; private static final String EMPTY_SELECTION_MESSAGE = "Select a server or deployment in the tree to view details"; private final Tree myTree; private final CardLayout myPropertiesPanelLayout; private final JPanel myPropertiesPanel; private final JLabel myMessageLabel; private final Map<String, JComponent> myLogComponents = new HashMap<String, JComponent>(); private final DefaultTreeModel myTreeModel; private TreeBuilderBase myBuilder; private AbstractTreeNode<?> myLastSelection; private final Project myProject; public ServersToolWindowContent(@NotNull Project project) { super(new BorderLayout()); myProject = project; myTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode()); myTree = new Tree(myTreeModel); myTree.setRootVisible(false); myTree.setShowsRootHandles(true); myTree.setCellRenderer(new NodeRenderer()); myTree.setLineStyleAngled(); getMainPanel().add(createToolbar(), BorderLayout.WEST); Splitter splitter = new Splitter(false, 0.3f); splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT)); myPropertiesPanelLayout = new CardLayout(); myPropertiesPanel = new JPanel(myPropertiesPanelLayout); myMessageLabel = new JLabel(EMPTY_SELECTION_MESSAGE, SwingConstants.CENTER); myPropertiesPanel.add(MESSAGE_CARD, new Wrapper(myMessageLabel)); splitter.setSecondComponent(myPropertiesPanel); getMainPanel().add(splitter, BorderLayout.CENTER); setupBuilder(project); for (RemoteServersViewContributor contributor : RemoteServersViewContributor.EP_NAME.getExtensions()) { contributor.setupTree(myProject, myTree, myBuilder); } myTree.addTreeSelectionListener( new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { onSelectionChanged(); } }); new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent event) { Set<ServersTreeStructure.RemoteServerNode> nodes = getSelectedRemoteServerNodes(); if (nodes.size() == 1) { RemoteServer<?> server = nodes.iterator().next().getValue(); ServerConnectionManager.getInstance().getOrCreateConnection(server); return true; } return false; } }.installOn(myTree); } private void onSelectionChanged() { Set<AbstractTreeNode> nodes = myBuilder.getSelectedElements(AbstractTreeNode.class); if (nodes.size() != 1) { showMessageLabel(EMPTY_SELECTION_MESSAGE); myLastSelection = null; return; } AbstractTreeNode<?> node = nodes.iterator().next(); if (Comparing.equal(node, myLastSelection)) { return; } myLastSelection = node; if (node instanceof ServersTreeStructure.LogProvidingNode) { ServersTreeStructure.LogProvidingNode logNode = (ServersTreeStructure.LogProvidingNode) node; LoggingHandlerImpl loggingHandler = logNode.getLoggingHandler(); if (loggingHandler != null) { String cardName = logNode.getLogId(); JComponent oldComponent = myLogComponents.get(cardName); JComponent logComponent = loggingHandler.getConsole().getComponent(); if (!logComponent.equals(oldComponent)) { myLogComponents.put(cardName, logComponent); if (oldComponent != null) { myPropertiesPanel.remove(oldComponent); } myPropertiesPanel.add(cardName, logComponent); } myPropertiesPanelLayout.show(myPropertiesPanel, cardName); } else { showMessageLabel(""); } } else if (node instanceof ServersTreeStructure.RemoteServerNode) { updateServerDetails((ServersTreeStructure.RemoteServerNode) node); } else { showMessageLabel(""); } } private void updateServerDetails(ServersTreeStructure.RemoteServerNode node) { RemoteServer<?> server = ((ServersTreeStructure.RemoteServerNode) node).getValue(); ServerConnection connection = ServerConnectionManager.getInstance().getConnection(server); if (connection == null || connection.getStatus() == ConnectionStatus.DISCONNECTED) { showMessageLabel("Double-click on the server node to connect"); } else { showMessageLabel(connection.getStatusText()); } } private void showMessageLabel(final String text) { myMessageLabel.setText(UIUtil.toHtml(text)); myPropertiesPanelLayout.show(myPropertiesPanel, MESSAGE_CARD); } private void setupBuilder(final @NotNull Project project) { ServersTreeStructure structure = new ServersTreeStructure(project); myBuilder = new TreeBuilderBase(myTree, structure, myTreeModel) { @Override protected boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { return nodeDescriptor instanceof ServersTreeStructure.RemoteServerNode || nodeDescriptor instanceof ServersTreeStructure.DeploymentNodeImpl; } }; Disposer.register(this, myBuilder); project .getMessageBus() .connect() .subscribe( ServerConnectionListener.TOPIC, new ServerConnectionListener() { @Override public void onConnectionCreated(@NotNull ServerConnection<?> connection) { getBuilder().queueUpdate(); } @Override public void onConnectionStatusChanged(@NotNull ServerConnection<?> connection) { getBuilder().queueUpdate(); updateSelectedServerDetails(); } @Override public void onDeploymentsChanged(@NotNull ServerConnection<?> connection) { getBuilder().queueUpdate(); updateSelectedServerDetails(); } }); } private void updateSelectedServerDetails() { if (myLastSelection instanceof ServersTreeStructure.RemoteServerNode) { updateServerDetails((ServersTreeStructure.RemoteServerNode) myLastSelection); } } private JComponent createToolbar() { DefaultActionGroup group = new DefaultActionGroup(); group.add(ActionManager.getInstance().getAction(SERVERS_TOOL_WINDOW_TOOLBAR)); group.add(new Separator()); group.add(new ContextHelpAction(HELP_ID)); ActionToolbar actionToolBar = ActionManager.getInstance().createActionToolbar(PLACE_TOOLBAR, group, false); myTree.putClientProperty( DataManager.CLIENT_PROPERTY_DATA_PROVIDER, new DataProvider() { @Override public Object getData(@NonNls String dataId) { if (KEY.getName().equals(dataId)) { return ServersToolWindowContent.this; } for (RemoteServersViewContributor contributor : RemoteServersViewContributor.EP_NAME.getExtensions()) { Object data = contributor.getData(dataId, ServersToolWindowContent.this); if (data != null) { return data; } } return null; } }); actionToolBar.setTargetComponent(myTree); return actionToolBar.getComponent(); } public JPanel getMainPanel() { return this; } public Set<ServerNode> getSelectedServerNodes() { return myBuilder.getSelectedElements(ServerNode.class); } public Set<DeploymentNode> getSelectedDeploymentNodes() { return myBuilder.getSelectedElements(DeploymentNode.class); } public Set<ServersTreeStructure.RemoteServerNode> getSelectedRemoteServerNodes() { return myBuilder.getSelectedElements(ServersTreeStructure.RemoteServerNode.class); } @Override public void dispose() {} public TreeBuilderBase getBuilder() { return myBuilder; } @NotNull public Project getProject() { return myProject; } public void select(@NotNull final ServerConnection<?> connection) { myBuilder.select( ServersTreeStructure.RemoteServerNode.class, new TreeVisitor<ServersTreeStructure.RemoteServerNode>() { @Override public boolean visit(@NotNull ServersTreeStructure.RemoteServerNode node) { return node.getValue().equals(connection.getServer()); } }, null, false); } public void select( @NotNull final ServerConnection<?> connection, @NotNull final String deploymentName) { myBuilder .getUi() .queueUpdate(connection) .doWhenDone( new Runnable() { @Override public void run() { myBuilder.select( ServersTreeStructure.DeploymentNodeImpl.class, new TreeVisitor<ServersTreeStructure.DeploymentNodeImpl>() { @Override public boolean visit(@NotNull ServersTreeStructure.DeploymentNodeImpl node) { AbstractTreeNode parent = node.getParent(); return parent instanceof ServersTreeStructure.RemoteServerNode && ((ServersTreeStructure.RemoteServerNode) parent) .getValue() .equals(connection.getServer()) && node.getValue().getName().equals(deploymentName); } }, null, false); } }); } }
public class FavoritesTreeViewPanel extends JPanel implements DataProvider { @NonNls public static final String ABSTRACT_TREE_NODE_TRANSFERABLE = "AbstractTransferable"; private final FavoritesTreeStructure myFavoritesTreeStructure; private FavoritesViewTreeBuilder myBuilder; private final CopyPasteDelegator myCopyPasteDelegator; private final MouseListener myTreePopupHandler; public static final DataKey<FavoritesTreeNodeDescriptor[]> CONTEXT_FAVORITES_ROOTS_DATA_KEY = DataKey.create("FavoritesRoot"); @Deprecated public static final String CONTEXT_FAVORITES_ROOTS = CONTEXT_FAVORITES_ROOTS_DATA_KEY.getName(); public static final DataKey<String> FAVORITES_LIST_NAME_DATA_KEY = DataKey.create("FavoritesListName"); @Deprecated public static final String FAVORITES_LIST_NAME = FAVORITES_LIST_NAME_DATA_KEY.getName(); protected Project myProject; private final String myHelpId; protected DnDAwareTree myTree; private final MyDeletePSIElementProvider myDeletePSIElementProvider = new MyDeletePSIElementProvider(); private final ModuleDeleteProvider myDeleteModuleProvider = new ModuleDeleteProvider(); private String myListName; private final IdeView myIdeView = new MyIdeView(); public FavoritesTreeViewPanel(Project project, String helpId, String name) { super(new BorderLayout()); myProject = project; myHelpId = helpId; myListName = name; myFavoritesTreeStructure = new FavoritesTreeStructure(project, myListName); DefaultMutableTreeNode root = new DefaultMutableTreeNode(); root.setUserObject(myFavoritesTreeStructure.getRootElement()); final DefaultTreeModel treeModel = new DefaultTreeModel(root); myTree = new DnDAwareTree(treeModel); myBuilder = new FavoritesViewTreeBuilder( myProject, myTree, treeModel, myFavoritesTreeStructure, myListName); TreeUtil.installActions(myTree); UIUtil.setLineStyleAngled(myTree); myTree.setRootVisible(false); myTree.setShowsRootHandles(true); myTree.setLargeModel(true); new TreeSpeedSearch(myTree); ToolTipManager.sharedInstance().registerComponent(myTree); myTree.setCellRenderer( new NodeRenderer() { public void customizeCellRenderer( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.customizeCellRenderer(tree, value, selected, expanded, leaf, row, hasFocus); if (value instanceof DefaultMutableTreeNode) { final DefaultMutableTreeNode node = (DefaultMutableTreeNode) value; // only favorites roots to explain final Object userObject = node.getUserObject(); if (userObject instanceof FavoritesTreeNodeDescriptor) { final FavoritesTreeNodeDescriptor favoritesTreeNodeDescriptor = (FavoritesTreeNodeDescriptor) userObject; AbstractTreeNode treeNode = favoritesTreeNodeDescriptor.getElement(); final ItemPresentation presentation = treeNode.getPresentation(); String locationString = presentation.getLocationString(); if (locationString != null && locationString.length() > 0) { append(" (" + locationString + ")", SimpleTextAttributes.GRAY_ATTRIBUTES); } else if (node.getParent() != null && node.getParent().getParent() == null) { final String location = favoritesTreeNodeDescriptor.getLocation(); if (location != null && location.length() > 0) { append(" (" + location + ")", SimpleTextAttributes.GRAY_ATTRIBUTES); } } } } } }); JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree); myTreePopupHandler = CustomizationUtil.installPopupHandler( myTree, IdeActions.GROUP_FAVORITES_VIEW_POPUP, ActionPlaces.FAVORITES_VIEW_POPUP); add(scrollPane, BorderLayout.CENTER); // add(createActionsToolbar(), BorderLayout.NORTH); EditSourceOnDoubleClickHandler.install(myTree); EditSourceOnEnterKeyHandler.install(myTree); myCopyPasteDelegator = new CopyPasteDelegator(myProject, this) { @NotNull protected PsiElement[] getSelectedElements() { return getSelectedPsiElements(); } }; } public void selectElement( final Object selector, final VirtualFile file, final boolean requestFocus) { myBuilder.select(selector, file, requestFocus); } public void dispose() { Disposer.dispose(myBuilder); myBuilder = null; } public DnDAwareTree getTree() { return myTree; } public String getName() { return myListName; } public void setName(String name) { myListName = name; } @NotNull private PsiElement[] getSelectedPsiElements() { final Object[] elements = getSelectedNodeElements(); if (elements == null) { return PsiElement.EMPTY_ARRAY; } ArrayList<PsiElement> result = new ArrayList<PsiElement>(); for (Object element : elements) { if (element instanceof PsiElement) { result.add((PsiElement) element); } else if (element instanceof SmartPsiElementPointer) { PsiElement psiElement = ((SmartPsiElementPointer) element).getElement(); if (psiElement != null) { result.add(psiElement); } } else { for (FavoriteNodeProvider provider : Extensions.getExtensions(FavoriteNodeProvider.EP_NAME, myProject)) { final PsiElement psiElement = provider.getPsiElement(element); if (psiElement != null) { result.add(psiElement); break; } } } } return result.toArray(new PsiElement[result.size()]); } public FavoritesTreeStructure getFavoritesTreeStructure() { return myFavoritesTreeStructure; } public Object getData(String dataId) { if (PlatformDataKeys.PROJECT.is(dataId)) { return myProject; } if (PlatformDataKeys.NAVIGATABLE.is(dataId)) { final FavoritesTreeNodeDescriptor[] selectedNodeDescriptors = getSelectedNodeDescriptors(); return selectedNodeDescriptors.length == 1 ? selectedNodeDescriptors[0].getElement() : null; } if (PlatformDataKeys.NAVIGATABLE_ARRAY.is(dataId)) { final List<Navigatable> selectedElements = getSelectedElements(Navigatable.class); return selectedElements.toArray(new Navigatable[selectedElements.size()]); } if (PlatformDataKeys.CUT_PROVIDER.is(dataId)) { return myCopyPasteDelegator.getCutProvider(); } if (PlatformDataKeys.COPY_PROVIDER.is(dataId)) { return myCopyPasteDelegator.getCopyProvider(); } if (PlatformDataKeys.PASTE_PROVIDER.is(dataId)) { return myCopyPasteDelegator.getPasteProvider(); } if (PlatformDataKeys.HELP_ID.is(dataId)) { return myHelpId; } if (LangDataKeys.PSI_ELEMENT.is(dataId)) { PsiElement[] elements = getSelectedPsiElements(); if (elements.length != 1) { return null; } return elements[0] != null && elements[0].isValid() ? elements[0] : null; } if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) { final PsiElement[] elements = getSelectedPsiElements(); ArrayList<PsiElement> result = new ArrayList<PsiElement>(); for (PsiElement element : elements) { if (element.isValid()) { result.add(element); } } return result.isEmpty() ? null : result.toArray(new PsiElement[result.size()]); } if (LangDataKeys.IDE_VIEW.is(dataId)) { return myIdeView; } if (LangDataKeys.TARGET_PSI_ELEMENT.is(dataId)) { return null; } if (LangDataKeys.MODULE_CONTEXT.is(dataId)) { Module[] selected = getSelectedModules(); return selected != null && selected.length == 1 ? selected[0] : null; } if (LangDataKeys.MODULE_CONTEXT_ARRAY.is(dataId)) { return getSelectedModules(); } if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) { final Object[] elements = getSelectedNodeElements(); return elements != null && elements.length >= 1 && elements[0] instanceof Module ? myDeleteModuleProvider : myDeletePSIElementProvider; } if (ModuleGroup.ARRAY_DATA_KEY.is(dataId)) { final List<ModuleGroup> selectedElements = getSelectedElements(ModuleGroup.class); return selectedElements.isEmpty() ? null : selectedElements.toArray(new ModuleGroup[selectedElements.size()]); } if (LibraryGroupElement.ARRAY_DATA_KEY.is(dataId)) { final List<LibraryGroupElement> selectedElements = getSelectedElements(LibraryGroupElement.class); return selectedElements.isEmpty() ? null : selectedElements.toArray(new LibraryGroupElement[selectedElements.size()]); } if (NamedLibraryElement.ARRAY_DATA_KEY.is(dataId)) { final List<NamedLibraryElement> selectedElements = getSelectedElements(NamedLibraryElement.class); return selectedElements.isEmpty() ? null : selectedElements.toArray(new NamedLibraryElement[selectedElements.size()]); } if (CONTEXT_FAVORITES_ROOTS_DATA_KEY.is(dataId)) { List<FavoritesTreeNodeDescriptor> result = new ArrayList<FavoritesTreeNodeDescriptor>(); FavoritesTreeNodeDescriptor[] selectedNodeDescriptors = getSelectedNodeDescriptors(); for (FavoritesTreeNodeDescriptor selectedNodeDescriptor : selectedNodeDescriptors) { FavoritesTreeNodeDescriptor root = selectedNodeDescriptor.getFavoritesRoot(); if (root != null && !(root.getElement().getValue() instanceof String)) { result.add(root); } } return result.toArray(new FavoritesTreeNodeDescriptor[result.size()]); } if (FAVORITES_LIST_NAME_DATA_KEY.is(dataId)) { return myListName; } FavoritesTreeNodeDescriptor[] descriptors = getSelectedNodeDescriptors(); if (descriptors.length > 0) { List<AbstractTreeNode> nodes = new ArrayList<AbstractTreeNode>(); for (FavoritesTreeNodeDescriptor descriptor : descriptors) { nodes.add(descriptor.getElement()); } return myFavoritesTreeStructure.getDataFromProviders(nodes, dataId); } return null; } private <T> List<T> getSelectedElements(Class<T> klass) { final Object[] elements = getSelectedNodeElements(); ArrayList<T> result = new ArrayList<T>(); if (elements == null) { return result; } for (Object element : elements) { if (element == null) continue; if (klass.isAssignableFrom(element.getClass())) { result.add((T) element); } } return result; } private Module[] getSelectedModules() { final Object[] elements = getSelectedNodeElements(); if (elements == null) { return null; } ArrayList<Module> result = new ArrayList<Module>(); for (Object element : elements) { if (element instanceof Module) { result.add((Module) element); } else if (element instanceof ModuleGroup) { result.addAll(((ModuleGroup) element).modulesInGroup(myProject, true)); } } return result.isEmpty() ? null : result.toArray(new Module[result.size()]); } private Object[] getSelectedNodeElements() { final FavoritesTreeNodeDescriptor[] selectedNodeDescriptors = getSelectedNodeDescriptors(); ArrayList<Object> result = new ArrayList<Object>(); for (FavoritesTreeNodeDescriptor selectedNodeDescriptor : selectedNodeDescriptors) { if (selectedNodeDescriptor != null) { Object value = selectedNodeDescriptor.getElement().getValue(); if (value instanceof SmartPsiElementPointer) { value = ((SmartPsiElementPointer) value).getElement(); } result.add(value); } } return ArrayUtil.toObjectArray(result); } @NotNull public FavoritesTreeNodeDescriptor[] getSelectedNodeDescriptors() { TreePath[] path = myTree.getSelectionPaths(); if (path == null) { return FavoritesTreeNodeDescriptor.EMPTY_ARRAY; } ArrayList<FavoritesTreeNodeDescriptor> result = new ArrayList<FavoritesTreeNodeDescriptor>(); for (TreePath treePath : path) { DefaultMutableTreeNode lastPathNode = (DefaultMutableTreeNode) treePath.getLastPathComponent(); Object userObject = lastPathNode.getUserObject(); if (!(userObject instanceof FavoritesTreeNodeDescriptor)) { continue; } FavoritesTreeNodeDescriptor treeNodeDescriptor = (FavoritesTreeNodeDescriptor) userObject; result.add(treeNodeDescriptor); } return result.toArray(new FavoritesTreeNodeDescriptor[result.size()]); } public static String getQualifiedName(final VirtualFile file) { return file.getPresentableUrl(); } public FavoritesViewTreeBuilder getBuilder() { return myBuilder; } private final class MyDeletePSIElementProvider implements DeleteProvider { public boolean canDeleteElement(DataContext dataContext) { final PsiElement[] elements = getElementsToDelete(); return DeleteHandler.shouldEnableDeleteAction(elements); } public void deleteElement(DataContext dataContext) { List<PsiElement> allElements = Arrays.asList(getElementsToDelete()); List<PsiElement> validElements = new ArrayList<PsiElement>(); for (PsiElement psiElement : allElements) { if (psiElement != null && psiElement.isValid()) validElements.add(psiElement); } final PsiElement[] elements = validElements.toArray(new PsiElement[validElements.size()]); LocalHistoryAction a = LocalHistory.getInstance().startAction(IdeBundle.message("progress.deleting")); try { DeleteHandler.deletePsiElement(elements, myProject); } finally { a.finish(); } } private PsiElement[] getElementsToDelete() { ArrayList<PsiElement> result = new ArrayList<PsiElement>(); Object[] elements = getSelectedNodeElements(); for (int idx = 0; elements != null && idx < elements.length; idx++) { if (elements[idx] instanceof PsiElement) { final PsiElement element = (PsiElement) elements[idx]; result.add(element); if (element instanceof PsiDirectory) { final VirtualFile virtualFile = ((PsiDirectory) element).getVirtualFile(); final String path = virtualFile.getPath(); if (path.endsWith(JarFileSystem.JAR_SEPARATOR)) { // if is jar-file root final VirtualFile vFile = LocalFileSystem.getInstance() .findFileByPath( path.substring(0, path.length() - JarFileSystem.JAR_SEPARATOR.length())); if (vFile != null) { final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vFile); if (psiFile != null) { elements[idx] = psiFile; } } } } } } return result.toArray(new PsiElement[result.size()]); } } private final class MyIdeView implements IdeView { public void selectElement(final PsiElement element) { if (element != null) { selectPsiElement(element, false); boolean requestFocus = true; final boolean isDirectory = element instanceof PsiDirectory; if (!isDirectory) { Editor editor = EditorHelper.openInEditor(element); if (editor != null) { ToolWindowManager.getInstance(myProject).activateEditorComponent(); requestFocus = false; } } if (requestFocus) { selectPsiElement(element, true); } } } private void selectPsiElement(PsiElement element, boolean requestFocus) { VirtualFile virtualFile = PsiUtilBase.getVirtualFile(element); FavoritesTreeViewPanel.this.selectElement(element, virtualFile, requestFocus); } @Nullable private PsiDirectory[] getSelectedDirectories() { if (myBuilder == null) return null; final Object[] selectedNodeElements = getSelectedNodeElements(); if (selectedNodeElements.length != 1) return null; for (FavoriteNodeProvider nodeProvider : Extensions.getExtensions(FavoriteNodeProvider.EP_NAME, myProject)) { final PsiElement psiElement = nodeProvider.getPsiElement(selectedNodeElements[0]); if (psiElement instanceof PsiDirectory) { return new PsiDirectory[] {(PsiDirectory) psiElement}; } else if (psiElement instanceof PsiDirectoryContainer) { final String moduleName = nodeProvider.getElementModuleName(selectedNodeElements[0]); GlobalSearchScope searchScope = GlobalSearchScope.projectScope(myProject); if (moduleName != null) { final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleName); if (module != null) { searchScope = GlobalSearchScope.moduleScope(module); } } return ((PsiDirectoryContainer) psiElement).getDirectories(searchScope); } } return selectedNodeElements[0] instanceof PsiDirectory ? new PsiDirectory[] {(PsiDirectory) selectedNodeElements[0]} : null; } public PsiDirectory[] getDirectories() { final PsiDirectory[] directories = getSelectedDirectories(); return directories == null ? PsiDirectory.EMPTY_ARRAY : directories; } public PsiDirectory getOrChooseDirectory() { return DirectoryChooserUtil.getOrChooseDirectory(this); } } }