IDEA-81134 'Untracked Files Preventing Checkout': add action to delete files
authorKirill Likhodedov <Kirill.Likhodedov@jetbrains.com>
Tue, 21 Aug 2012 15:46:20 +0000 (19:46 +0400)
committerKirill Likhodedov <Kirill.Likhodedov@jetbrains.com>
Fri, 24 Aug 2012 09:22:55 +0000 (13:22 +0400)
* ChangesTreeList: extract the Tree as inner class; make it a TypeSafeDataProvider; delegate to the ChangesTreeList.
* AbstractSelectFilesDialog: extract toolbar action group creation to a protected method that can be overridden.
* SelectFilesDialog: optionally add the Delete action to the toolbar (and register the shortcut as well).
  Provide the standard VirtualFileDeleteProvider.
  Refresh the file list on delete.
* Use this functionality in the UntrackedFilesNotifier.

platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/AbstractSelectFilesDialog.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/SelectFilesDialog.java
platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java
platform/vcs-impl/vcs-impl.iml
plugins/git4idea/src/git4idea/branch/GitBranchOperation.java
plugins/git4idea/src/git4idea/util/UntrackedFilesNotifier.java

index 933c95c13b22857c5f35efd0d1e1855f6034ef19..0ab185b48da529f6618a4cea0f6f850a8a6d88f3 100644 (file)
@@ -100,13 +100,18 @@ public abstract class AbstractSelectFilesDialog<T> extends DialogWrapper {
   }
 
   private JComponent createToolbar() {
+    DefaultActionGroup group = createToolbarActions();
+    ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true);
+    return toolbar.getComponent();
+  }
+
+  @NotNull
+  protected DefaultActionGroup createToolbarActions() {
     DefaultActionGroup group = new DefaultActionGroup();
     final AnAction[] actions = getFileList().getTreeActions();
     for(AnAction action: actions) {
       group.add(action);
     }
-    ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true);
-    return toolbar.getComponent();
+    return group;
   }
-
 }
index d70d799dc3ecbd753e906277b28318c68ac412ab..bc1a30e0509cec7c8c9806936c2106ab11168619 100644 (file)
@@ -69,7 +69,7 @@ import java.util.List;
 /**
  * @author max
  */
-public abstract class ChangesTreeList<T> extends JPanel {
+public abstract class ChangesTreeList<T> extends JPanel implements TypeSafeDataProvider {
   private final Tree myTree;
   private final JBList myList;
   private final JScrollPane myTreeScrollPane;
@@ -109,63 +109,7 @@ public abstract class ChangesTreeList<T> extends JPanel {
     setLayout(myCards);
 
     final int checkboxWidth = new JCheckBox().getPreferredSize().width;
-    myTree = new Tree(ChangesBrowserNode.create(myProject, ROOT)) {
-
-      @Override
-      public boolean isFileColorsEnabled() {
-        final boolean enabled = Registry.is("file.colors.in.commit.dialog")
-                          && FileColorManager.getInstance(project).isEnabled()
-                          && FileColorManager.getInstance(project).isEnabledForProjectView();
-        final boolean opaque = isOpaque();
-        if (enabled && opaque) {
-          setOpaque(false);
-        } else if (!enabled && !opaque) {
-          setOpaque(true);
-        }
-        return enabled;
-      }
-
-      @Override
-      public Color getFileColorFor(Object object) {
-        VirtualFile file = null;
-        if (object instanceof FilePathImpl) {
-          file = LocalFileSystem.getInstance().findFileByPath(((FilePathImpl)object).getPath());
-        } else if (object instanceof Change) {
-          file = ((Change)object).getVirtualFile();
-        }
-
-        if (file != null) {
-          return FileColorManager.getInstance(project).getFileColor(file);
-        }
-        return super.getFileColorFor(object);
-      }
-
-      public Dimension getPreferredScrollableViewportSize() {
-        Dimension size = super.getPreferredScrollableViewportSize();
-        size = new Dimension(size.width + 10, size.height);
-        return size;
-      }
-
-      protected void processMouseEvent(MouseEvent e) {
-        if (e.getID() == MouseEvent.MOUSE_PRESSED) {
-          if (! myTree.isEnabled()) return;
-          int row = myTree.getRowForLocation(e.getX(), e.getY());
-          if (row >= 0) {
-            final Rectangle baseRect = myTree.getRowBounds(row);
-            baseRect.setSize(checkboxWidth, baseRect.height);
-            if (baseRect.contains(e.getPoint())) {
-              myTree.setSelectionRow(row);
-              toggleSelection();
-            }
-          }
-        }
-        super.processMouseEvent(e);
-      }
-
-      public int getToggleClickCount() {
-        return -1;
-      }
-    };
+    myTree = new MyTree(project, checkboxWidth);
 
     myTree.setRootVisible(false);
     myTree.setShowsRootHandles(true);
@@ -939,4 +883,81 @@ public abstract class ChangesTreeList<T> extends JPanel {
     myTree.setPaintBusy(value);
     myList.setPaintBusy(value);
   }
+
+  @Override
+  public void calcData(DataKey key, DataSink sink) {
+  }
+
+  private class MyTree extends Tree implements TypeSafeDataProvider {
+
+    private final Project myProject;
+    private final int myCheckboxWidth;
+
+    public MyTree(Project project, int checkboxWidth) {
+      super(ChangesBrowserNode.create(ChangesTreeList.this.myProject, ChangesTreeList.ROOT));
+      myProject = project;
+      myCheckboxWidth = checkboxWidth;
+    }
+
+    @Override
+    public boolean isFileColorsEnabled() {
+      final boolean enabled = Registry.is("file.colors.in.commit.dialog")
+                        && FileColorManager.getInstance(myProject).isEnabled()
+                        && FileColorManager.getInstance(myProject).isEnabledForProjectView();
+      final boolean opaque = isOpaque();
+      if (enabled && opaque) {
+        setOpaque(false);
+      } else if (!enabled && !opaque) {
+        setOpaque(true);
+      }
+      return enabled;
+    }
+
+    @Override
+    public Color getFileColorFor(Object object) {
+      VirtualFile file = null;
+      if (object instanceof FilePathImpl) {
+        file = LocalFileSystem.getInstance().findFileByPath(((FilePathImpl)object).getPath());
+      } else if (object instanceof Change) {
+        file = ((Change)object).getVirtualFile();
+      }
+
+      if (file != null) {
+        return FileColorManager.getInstance(myProject).getFileColor(file);
+      }
+      return super.getFileColorFor(object);
+    }
+
+    public Dimension getPreferredScrollableViewportSize() {
+      Dimension size = super.getPreferredScrollableViewportSize();
+      size = new Dimension(size.width + 10, size.height);
+      return size;
+    }
+
+    protected void processMouseEvent(MouseEvent e) {
+      if (e.getID() == MouseEvent.MOUSE_PRESSED) {
+        if (! myTree.isEnabled()) return;
+        int row = myTree.getRowForLocation(e.getX(), e.getY());
+        if (row >= 0) {
+          final Rectangle baseRect = myTree.getRowBounds(row);
+          baseRect.setSize(myCheckboxWidth, baseRect.height);
+          if (baseRect.contains(e.getPoint())) {
+            myTree.setSelectionRow(row);
+            toggleSelection();
+          }
+        }
+      }
+      super.processMouseEvent(e);
+    }
+
+    public int getToggleClickCount() {
+      return -1;
+    }
+
+    @Override
+    public void calcData(DataKey key, DataSink sink) {
+      // just delegate to the change list
+      ChangesTreeList.this.calcData(key, sink);
+    }
+  }
 }
index 1fffb9f7e45cfb0315048b39eed040bd5db34826..750b816f339fbc4998b5771d9685848aaa92e3b8 100644 (file)
 
 package com.intellij.openapi.vcs.changes.ui;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.intellij.ide.DeleteProvider;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.fileChooser.actions.VirtualFileDeleteProvider;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.vcs.VcsShowConfirmationOption;
+import com.intellij.openapi.vcs.changes.actions.DeleteUnversionedFilesAction;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ArrayUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.tree.DefaultTreeModel;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -30,28 +39,14 @@ import java.util.List;
  */
 public class SelectFilesDialog extends AbstractSelectFilesDialog<VirtualFile> {
 
-  private ChangesTreeList<VirtualFile> myFileList;
+  @NotNull private final VirtualFileList myFileList;
+  private final boolean myDeletableFiles;
 
-  public SelectFilesDialog(final Project project, List<VirtualFile> originalFiles, final String prompt,
-                           final VcsShowConfirmationOption confirmationOption, boolean selectableFiles, boolean showDoNotAskOption) {
+  public SelectFilesDialog(Project project, List<VirtualFile> originalFiles, String prompt, VcsShowConfirmationOption confirmationOption,
+                           boolean selectableFiles, boolean showDoNotAskOption, boolean deletableFiles) {
     super(project, false, confirmationOption, prompt, showDoNotAskOption);
-    myFileList = new ChangesTreeList<VirtualFile>(project, originalFiles, selectableFiles, true, null, null) {
-      protected DefaultTreeModel buildTreeModel(final List<VirtualFile> changes, ChangeNodeDecorator changeNodeDecorator) {
-        return new TreeModelBuilder(project, false).buildModelFromFiles(changes);
-      }
-
-      protected List<VirtualFile> getSelectedObjects(final ChangesBrowserNode node) {
-        return node.getAllFilesUnder();
-      }
-
-      protected VirtualFile getLeadSelectedObject(final ChangesBrowserNode node) {
-        final Object o = node.getUserObject();
-        if (o instanceof VirtualFile) {
-          return (VirtualFile) o;
-        }
-        return null;
-      }
-    };
+    myDeletableFiles = deletableFiles;
+    myFileList = new VirtualFileList(project, originalFiles, selectableFiles, deletableFiles);
     myFileList.setChangesToDisplay(originalFiles);
     init();
   }
@@ -65,4 +60,70 @@ public class SelectFilesDialog extends AbstractSelectFilesDialog<VirtualFile> {
   protected ChangesTreeList getFileList() {
     return myFileList;
   }
+
+  @NotNull
+  @Override
+  protected DefaultActionGroup createToolbarActions() {
+    DefaultActionGroup defaultGroup = super.createToolbarActions();
+    if (myDeletableFiles) {
+      AnAction deleteAction = new DeleteUnversionedFilesAction() {
+        @Override
+        public void actionPerformed(AnActionEvent e) {
+          super.actionPerformed(e);
+          myFileList.refresh();
+        }
+      };
+      defaultGroup.add(deleteAction);
+      deleteAction.registerCustomShortcutSet(CommonShortcuts.DELETE, this.getFileList());
+    }
+    return defaultGroup;
+  }
+
+  private static class VirtualFileList extends ChangesTreeList<VirtualFile> {
+    private final Project myProject;
+    @Nullable private final DeleteProvider myDeleteProvider;
+
+    public VirtualFileList(Project project, List<VirtualFile> originalFiles, boolean selectableFiles, boolean deletableFiles) {
+      super(project, originalFiles, selectableFiles, true, null, null);
+      myProject = project;
+      myDeleteProvider = (deletableFiles ?  new VirtualFileDeleteProvider() : null);
+    }
+
+    protected DefaultTreeModel buildTreeModel(final List<VirtualFile> changes, ChangeNodeDecorator changeNodeDecorator) {
+      return new TreeModelBuilder(myProject, false).buildModelFromFiles(changes);
+    }
+
+    protected List<VirtualFile> getSelectedObjects(final ChangesBrowserNode node) {
+      return node.getAllFilesUnder();
+    }
+
+    protected VirtualFile getLeadSelectedObject(final ChangesBrowserNode node) {
+      final Object o = node.getUserObject();
+      if (o instanceof VirtualFile) {
+        return (VirtualFile) o;
+      }
+      return null;
+    }
+
+    @Override
+    public void calcData(DataKey key, DataSink sink) {
+      super.calcData(key, sink);
+      if (key.equals(PlatformDataKeys.DELETE_ELEMENT_PROVIDER) && myDeleteProvider != null) {
+        sink.put(key, myDeleteProvider);
+      }
+      else if (key.equals(PlatformDataKeys.VIRTUAL_FILE_ARRAY)) {
+        sink.put(key, ArrayUtil.toObjectArray(getSelectedChanges(), VirtualFile.class));
+      }
+    }
+
+    public void refresh() {
+      setChangesToDisplay(new ArrayList<VirtualFile>(Collections2.filter(getIncludedChanges(), new Predicate<VirtualFile>() {
+        @Override
+        public boolean apply(@Nullable VirtualFile input) {
+          return input != null && input.isValid();
+        }
+      })));
+    }
+
+  }
 }
index f6747b33d9cfc153cd6d6f13a8c5c2572c0887e2..c02e9cfcccee436cb53541444884b6217857ba6d 100644 (file)
@@ -151,7 +151,7 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper {
       return null;
     }
 
-    SelectFilesDialog dlg = new SelectFilesDialog(myProject, files, prompt, confirmationOption, true, true);
+    SelectFilesDialog dlg = new SelectFilesDialog(myProject, files, prompt, confirmationOption, true, true, false);
     dlg.setTitle(title);
     if (! confirmationOption.isPersistent()) {
       dlg.setDoNotAskOption(null);
index 3655b5cdf1bebb946c940020bbc47fa7aca3bc94..ba158983c2ac473a19da1210f5a5c4677f10a8d3 100644 (file)
@@ -13,6 +13,7 @@
     <orderEntry type="library" name="commons-codec" level="project" />
     <orderEntry type="module" module-name="lang-api" />
     <orderEntry type="module" module-name="jps-model-serialization" />
+    <orderEntry type="library" name="Guava" level="project" />
   </component>
 </module>
 
index 4af329bf5945949ae9d8670dc851443932ceb4c2..fb85ca9e1541a2b0820beacc7bd959a4cd92682c 100644 (file)
@@ -398,7 +398,7 @@ abstract class GitBranchOperation {
   private class UntrackedFilesDialog extends SelectFilesDialog {
 
     public UntrackedFilesDialog(@NotNull Project project, @NotNull List<VirtualFile> originalFiles, @NotNull String prompt) {
-      super(project, originalFiles, prompt, null, false, false);
+      super(project, originalFiles, prompt, null, false, false, false);
       setOKButtonText("Rollback");
       setCancelButtonText("Don't rollback");
     }
index 98aaee11fd8fa14cb92e5a12903357ef44e7118a..1ef29d746bab2022f94b530401db3e41365b4891 100644 (file)
@@ -56,13 +56,7 @@ public class UntrackedFilesNotifier {
       public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
         if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
           final String dialogDesc = createUntrackedFilesOverwrittenDescription(operation, false);
-          SelectFilesDialog dlg = new SelectFilesDialog(project, new ArrayList<VirtualFile>(untrackedFiles),
-                                                        StringUtil.stripHtml(dialogDesc, true), null, false, false) {
-            @Override
-            protected Action[] createActions() {
-              return new Action[]{getOKAction()};
-            }
-          };
+          SelectFilesDialog dlg = new UntrackedFilesDialog(project, untrackedFiles, dialogDesc);
           dlg.setTitle("Untracked Files Preventing " + StringUtil.capitalize(operation));
           dlg.show();
         }
@@ -82,4 +76,17 @@ public class UntrackedFilesNotifier {
     }
     return notificationDesc;
   }
+
+  private static class UntrackedFilesDialog extends SelectFilesDialog {
+
+    public UntrackedFilesDialog(Project project, Collection<VirtualFile> untrackedFiles, String dialogDesc) {
+      super(project, new ArrayList<VirtualFile>(untrackedFiles), StringUtil.stripHtml(dialogDesc, true), null, false, false, true);
+    }
+
+    @Override
+    protected Action[] createActions() {
+      return new Action[]{getOKAction()};
+    }
+
+  }
 }