blob: e38afbb2e961729e1ef0d5bcb6ccd1f6d506e388 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.changes.ui;
import com.intellij.ide.CopyProvider;
import com.intellij.ide.dnd.*;
import com.intellij.ide.util.treeView.TreeState;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.fileChooser.actions.VirtualFileDeleteProvider;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.issueLinks.TreeLinkMouseListener;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.SmartExpander;
import com.intellij.ui.TreeCopyProvider;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.awt.RelativeRectangle;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.EditSourceOnEnterKeyHandler;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;
/**
* @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 == CommonDataKeys.VIRTUAL_FILE_ARRAY) {
sink.put(CommonDataKeys.VIRTUAL_FILE_ARRAY, getSelectedFiles());
}
else if (key == CommonDataKeys.NAVIGATABLE) {
final VirtualFile[] files = getSelectedFiles();
if (files.length == 1 && !files [0].isDirectory()) {
sink.put(CommonDataKeys.NAVIGATABLE, new OpenFileDescriptor(myProject, files[0], 0));
}
}
else if (key == CommonDataKeys.NAVIGATABLE_ARRAY) {
sink.put(CommonDataKeys.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 = UIUtil.createImage(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 = UIUtil.createImage(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 Pair.create(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);
}
}