@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; }
@Override public void calcData(final DataKey key, final DataSink sink) { if (key.equals(LangDataKeys.PSI_ELEMENT)) { if (mySelectedElements != null && !mySelectedElements.isEmpty()) { T selectedElement = mySelectedElements.iterator().next(); if (selectedElement instanceof ClassMemberWithElement) { sink.put( LangDataKeys.PSI_ELEMENT, ((ClassMemberWithElement) selectedElement).getElement()); } } } }
@Override @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; }
@Override public void calcData(final DataKey key, final DataSink sink) { Node node = getSelectedNode(); if (key == PlatformDataKeys.PROJECT) { sink.put(PlatformDataKeys.PROJECT, myProject); } else if (key == USAGE_VIEW_KEY) { sink.put(USAGE_VIEW_KEY, UsageViewImpl.this); } else if (key == PlatformDataKeys.NAVIGATABLE_ARRAY) { sink.put(PlatformDataKeys.NAVIGATABLE_ARRAY, getNavigatablesForNodes(getSelectedNodes())); } else if (key == PlatformDataKeys.EXPORTER_TO_TEXT_FILE) { sink.put(PlatformDataKeys.EXPORTER_TO_TEXT_FILE, myTextFileExporter); } else if (key == USAGES_KEY) { final Set<Usage> selectedUsages = getSelectedUsages(); sink.put( USAGES_KEY, selectedUsages != null ? selectedUsages.toArray(new Usage[selectedUsages.size()]) : null); } else if (key == USAGE_TARGETS_KEY) { sink.put(USAGE_TARGETS_KEY, getSelectedUsageTargets()); } else if (key == PlatformDataKeys.VIRTUAL_FILE_ARRAY) { final Set<Usage> usages = getSelectedUsages(); Usage[] ua = usages != null ? usages.toArray(new Usage[usages.size()]) : null; VirtualFile[] data = UsageDataUtil.provideVirtualFileArray(ua, getSelectedUsageTargets()); sink.put(PlatformDataKeys.VIRTUAL_FILE_ARRAY, data); } else if (key == PlatformDataKeys.HELP_ID) { sink.put(PlatformDataKeys.HELP_ID, HELP_ID); } else if (key == PlatformDataKeys.COPY_PROVIDER) { sink.put(PlatformDataKeys.COPY_PROVIDER, this); } else if (node != null) { Object userObject = node.getUserObject(); if (userObject instanceof TypeSafeDataProvider) { ((TypeSafeDataProvider) userObject).calcData(key, sink); } else if (userObject instanceof DataProvider) { DataProvider dataProvider = (DataProvider) userObject; Object data = dataProvider.getData(key.getName()); if (data != null) { sink.put(key, data); } } } }
/** @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()); } } }); } }
@Nullable public static XDebuggerTree getTree(DataContext context) { return XDEBUGGER_TREE_KEY.getData(context); }
/** @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; } } }