}
List<VirtualFile> getUnversionedFiles() {
- return new ArrayList<VirtualFile>(myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).getFiles());
+ return myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).getFiles();
+ }
+
+ Pair<Integer, Integer> getUnversionedFilesSize() {
+ final VirtualFileHolder holder = myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED);
+ return new Pair<Integer, Integer>(holder.getSize(), holder.getNumDirs());
}
List<VirtualFile> getModifiedWithoutEditing() {
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
public class ChangesViewManager extends AbstractProjectComponent implements JDOMExternalizable {
+ public static final int UNVERSIONED_MAX_SIZE = 50;
private boolean SHOW_FLATTEN_MODE = true;
private boolean SHOW_IGNORED_MODE = false;
void refreshView() {
if (myDisposed || ! myProject.isInitialized() || ApplicationManager.getApplication().isUnitTestMode()) return;
ChangeListManagerImpl changeListManager = ChangeListManagerImpl.getInstanceImpl(myProject);
- myView.updateModel(changeListManager.getChangeListsCopy(),
- changeListManager.getUnversionedFiles(),
+
+ final Pair<Integer, Integer> unv = changeListManager.getUnversionedFilesSize();
+ final boolean manyUnversioned = unv.getFirst() > UNVERSIONED_MAX_SIZE;
+ final Trinity<List<VirtualFile>, Integer, Integer> unversionedPair =
+ new Trinity<List<VirtualFile>, Integer, Integer>(manyUnversioned ? Collections.<VirtualFile>emptyList() : changeListManager.getUnversionedFiles(), unv.getFirst(),
+ unv.getSecond());
+
+ myView.updateModel(changeListManager.getChangeListsCopy(), unversionedPair,
changeListManager.getDeletedFiles(),
changeListManager.getModifiedWithoutEditing(),
changeListManager.getSwitchedFilesMap(),
--- /dev/null
+/*
+ * Copyright 2000-2010 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;
+
+import com.intellij.ide.CommonActionsManager;
+import com.intellij.ide.TreeExpander;
+import com.intellij.ide.util.treeView.TreeState;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.openapi.vcs.changes.actions.MoveChangesToAnotherListAction;
+import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode;
+import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNodeRenderer;
+import com.intellij.openapi.vcs.changes.ui.ChangesListView;
+import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Icons;
+import com.intellij.util.ui.tree.TreeUtil;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+import java.util.Arrays;
+import java.util.List;
+
+public class UnversionedViewDialog extends DialogWrapper {
+ private JPanel myPanel;
+ private final ChangesListView myView;
+ private final ChangeListManager myChangeListManager;
+ private boolean myInRefresh;
+ private final Project myProject;
+ private boolean myFlattenState;
+
+ public UnversionedViewDialog(final Project project) {
+ super(project, true);
+ setTitle("Unversioned files");
+ myProject = project;
+ myView = new ChangesListView(project) {
+ @Override
+ public void calcData(DataKey key, DataSink sink) {
+ super.calcData(key, sink);
+ if (ChangesListView.UNVERSIONED_FILES_DATA_KEY.is(key.getName())) {
+ sink.put(key, Arrays.asList(getSelectedFiles()));
+ }
+ }
+ };
+ myChangeListManager = ChangeListManager.getInstance(project);
+ createPanel();
+ setOKButtonText("Close");
+
+ init();
+ initData(((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles());
+ myView.setMinimumSize(new Dimension(100, 100));
+ }
+
+ @Override
+ protected Action[] createActions() {
+ return new Action[]{getOKAction()};
+ }
+
+ private void initData(final List<VirtualFile> files) {
+ final TreeState state = TreeState.createOn(myView, (ChangesBrowserNode)myView.getModel().getRoot());
+
+ TreeModelBuilder builder = new TreeModelBuilder(myProject, myFlattenState);
+ final DefaultTreeModel model = builder.buildModelFromFiles(files);
+ myView.setModel(model);
+ myView.setCellRenderer(new ChangesBrowserNodeRenderer(myProject, myFlattenState, true));
+ myView.expandPath(new TreePath(((ChangesBrowserNode)model.getRoot()).getPath()));
+
+ state.applyTo(myView);
+ }
+
+ private void createPanel() {
+ myPanel = new JPanel(new BorderLayout());
+
+ final DefaultActionGroup group = new DefaultActionGroup();
+ final CommonActionsManager cam = CommonActionsManager.getInstance();
+ final Expander expander = new Expander();
+ final AnAction expandAction = cam.createExpandAllAction(expander, myView);
+ group.add(expandAction);
+ final AnAction collapseAction = cam.createCollapseAllAction(expander, myView);
+ group.add(collapseAction);
+ group.add(new ToggleShowFlattenAction());
+ group.add(new MoveChangesToAnotherListAction() {
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ super.actionPerformed(e);
+ refreshView();
+ }
+ });
+
+ myView.setMenuActions(group);
+ myView.setShowFlatten(false);
+
+ final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar("UNVERSIONED_DIALOG", group, false);
+ myPanel.add(actionToolbar.getComponent(), BorderLayout.WEST);
+ myPanel.add(new JScrollPane(myView), BorderLayout.CENTER);
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "com.intellij.openapi.vcs.changes.UnversionedViewDialog";
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return myView;
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return myPanel;
+ }
+
+ private class Expander implements TreeExpander {
+ public void expandAll() {
+ TreeUtil.expandAll(myView);
+ }
+
+ public boolean canExpand() {
+ return true;
+ }
+
+ public void collapseAll() {
+ TreeUtil.collapseAll(myView, 1);
+ TreeUtil.expand(myView, 0);
+ }
+
+ public boolean canCollapse() {
+ return true;
+ }
+ }
+
+ private void refreshView() {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+
+ if (myInRefresh) return;
+ myInRefresh = true;
+
+ myChangeListManager.invokeAfterUpdate(new Runnable() {
+ public void run() {
+ try {
+ initData(((ChangeListManagerImpl) myChangeListManager).getUnversionedFiles());
+ } finally {
+ myInRefresh = false;
+ }
+ }
+ }, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE, "", ModalityState.current());
+ }
+
+ public class ToggleShowFlattenAction extends ToggleAction implements DumbAware {
+ public ToggleShowFlattenAction() {
+ super(VcsBundle.message("changes.action.show.directories.text"),
+ VcsBundle.message("changes.action.show.directories.description"),
+ Icons.DIRECTORY_CLOSED_ICON);
+ myFlattenState = false;
+ }
+
+ public boolean isSelected(AnActionEvent e) {
+ return !myFlattenState;
+ }
+
+ public void setSelected(AnActionEvent e, boolean state) {
+ myFlattenState = !state;
+ myView.setShowFlatten(myFlattenState);
+ refreshView();
+ }
+ }
+}
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
private final Set<VirtualFile> myFiles = new HashSet<VirtualFile>();
private final Project myProject;
private final HolderType myType;
+ private int myNumDirs;
public VirtualFileHolder(Project project, final HolderType type) {
myProject = project;
public void cleanAll() {
myFiles.clear();
+ myNumDirs = 0;
}
- static void cleanScope(final Project project, final Collection<VirtualFile> files, final VcsDirtyScope scope) {
- ApplicationManager.getApplication().runReadAction(new Runnable() {
- public void run() {
+ // returns number of removed directories
+ static int cleanScope(final Project project, final Collection<VirtualFile> files, final VcsDirtyScope scope) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
+ public Integer compute() {
+ int result = 0;
// to avoid deadlocks caused by incorrect lock ordering, need to lock on this after taking read action
- if (project.isDisposed() || files.isEmpty()) return;
+ if (project.isDisposed() || files.isEmpty()) return 0;
final List<VirtualFile> currentFiles = new ArrayList<VirtualFile>(files);
if (scope.getRecursivelyDirtyDirectories().size() == 0) {
final Set<FilePath> dirtyFiles = scope.getDirtyFiles();
VirtualFile f = dirtyFile.getVirtualFile();
if (f != null) {
files.remove(f);
+ if (f.isDirectory()) ++ result;
}
else {
if (!cleanedDroppedFiles) {
cleanedDroppedFiles = true;
for(VirtualFile file: currentFiles) {
- if (fileDropped(project, file)) files.remove(file);
+ if (fileDropped(project, file)) {
+ files.remove(file);
+ if (file.isDirectory()) ++ result;
+ }
}
}
}
for (VirtualFile file : currentFiles) {
if (fileDropped(project, file) || scope.belongsTo(new FilePathImpl(file))) {
files.remove(file);
+ if (file.isDirectory()) ++ result;
}
}
}
+ return result;
}
});
}
public void cleanScope(final VcsDirtyScope scope) {
- cleanScope(myProject, myFiles, scope);
+ myNumDirs -= cleanScope(myProject, myFiles, scope);
}
private static boolean fileDropped(final Project project, final VirtualFile file) {
public void addFile(VirtualFile file) {
myFiles.add(file);
+ if (file.isDirectory()) ++ myNumDirs;
}
public void removeFile(VirtualFile file) {
myFiles.remove(file);
+ if (file.isDirectory()) -- myNumDirs;
}
public List<VirtualFile> getFiles() {
public VirtualFileHolder copy() {
final VirtualFileHolder copyHolder = new VirtualFileHolder(myProject, myType);
copyHolder.myFiles.addAll(myFiles);
+ copyHolder.myNumDirs = myNumDirs;
return copyHolder;
}
public int hashCode() {
return myFiles.hashCode();
}
+
+ public int getSize() {
+ return myFiles.size();
+ }
+
+ public int getNumDirs() {
+ return myNumDirs;
+ }
}
--- /dev/null
+/*
+ * Copyright 2000-2010 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.openapi.project.Project;
+import com.intellij.openapi.vcs.changes.ChangeListOwner;
+import com.intellij.openapi.vcs.changes.UnversionedViewDialog;
+import com.intellij.ui.SimpleTextAttributes;
+
+public class ChangesBrowserManyUnversionedFilesNode extends ChangesBrowserNode {
+ private final Project myProject;
+ private final int myUnversionedSize;
+ private final int myDirsSize;
+ private final MyUnversionedShower myShower;
+
+ public ChangesBrowserManyUnversionedFilesNode(Project project, int unversionedSize, int dirsSize) {
+ super(UNVERSIONED_FILES_TAG);
+ myProject = project;
+ myUnversionedSize = unversionedSize;
+ myDirsSize = dirsSize;
+ myShower = new MyUnversionedShower(myProject);
+ }
+
+ public boolean canAcceptDrop(final ChangeListDragBean dragBean) {
+ return false;
+ }
+
+ public void acceptDrop(final ChangeListOwner dragOwner, final ChangeListDragBean dragBean) {
+ }
+
+ @Override
+ public void render(ChangesBrowserNodeRenderer renderer, boolean selected, boolean expanded, boolean hasFocus) {
+ renderer.append(userObject.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ final String s = "(" + (myDirsSize > 0 ? myDirsSize + " directories and " : "") + (myUnversionedSize - myDirsSize) + " files) ";
+ renderer.append(s, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES);
+ renderer.append(" Click to browse", SimpleTextAttributes.LINK_ATTRIBUTES, myShower);
+ }
+
+ private static class MyUnversionedShower implements Runnable {
+ private final Project myProject;
+
+ public MyUnversionedShower(Project project) {
+ myProject = project;
+ }
+
+ public void run() {
+ final UnversionedViewDialog dialog = new UnversionedViewDialog(myProject);
+ dialog.show();
+ }
+ }
+}
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;
myShowFlatten = showFlatten;
}
- public void updateModel(List<? extends ChangeList> changeLists, List<VirtualFile> unversionedFiles, final List<LocallyDeletedChange> locallyDeletedFiles,
+ 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,
return files;
}
- private VirtualFile[] getSelectedFiles() {
+ protected VirtualFile[] getSelectedFiles() {
final Change[] changes = getSelectedChanges();
Collection<VirtualFile> files = new HashSet<VirtualFile>();
for (Change change : changes) {
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.FileStatus;
}
public DefaultTreeModel buildModel(final List<? extends ChangeList> changeLists,
- final List<VirtualFile> unversionedFiles,
+ final Trinity<List<VirtualFile>, Integer, Integer> unversionedFiles,
final List<LocallyDeletedChange> locallyDeletedFiles,
final List<VirtualFile> modifiedWithoutEditing,
final MultiMap<String, VirtualFile> switchedFiles,
resetGrouping();
buildVirtualFiles(modifiedWithoutEditing, ChangesBrowserNode.MODIFIED_WITHOUT_EDITING_TAG);
}
- if (!unversionedFiles.isEmpty()) {
+ final boolean manyUnversioned = unversionedFiles.getSecond() > unversionedFiles.getFirst().size();
+ if (manyUnversioned || (! unversionedFiles.getFirst().isEmpty())) {
resetGrouping();
- buildVirtualFiles(unversionedFiles, ChangesBrowserNode.UNVERSIONED_FILES_TAG);
+
+ if (manyUnversioned) {
+ final ChangesBrowserNode baseNode = new ChangesBrowserManyUnversionedFilesNode(myProject, unversionedFiles.getSecond(), unversionedFiles.getThird());
+ model.insertNodeInto(baseNode, root, root.getChildCount());
+ } else {
+ buildVirtualFiles(unversionedFiles.getFirst(), ChangesBrowserNode.UNVERSIONED_FILES_TAG);
+ }
}
if (switchedRoots != null && (! switchedRoots.isEmpty())) {
resetGrouping();