blob: f6da269832f5f4b9f8da7faca7022b9b285a666f [file] [log] [blame]
/*
* Copyright 2000-2009 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.committed;
import com.intellij.ide.util.treeView.TreeState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangesUtil;
import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode;
import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNodeRenderer;
import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder;
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.util.*;
/**
* @author yole
*/
public class StructureFilteringStrategy implements ChangeListFilteringStrategy {
private final List<ChangeListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private MyUI myUI;
private final Project myProject;
private final List<FilePath> mySelection = new ArrayList<FilePath>();
public StructureFilteringStrategy(final Project project) {
myProject = project;
}
@Override
public CommittedChangesFilterKey getKey() {
return new CommittedChangesFilterKey(toString(), CommittedChangesFilterPriority.STRUCTURE);
}
public String toString() {
return VcsBundle.message("filter.structure.name");
}
@Nullable
public JComponent getFilterUI() {
if (myUI == null) {
myUI = new MyUI();
}
return myUI.getComponent();
}
public void setFilterBase(List<CommittedChangeList> changeLists) {
// todo cycle here
if (myUI == null) {
myUI = new MyUI();
}
myUI.reset();
myUI.append(changeLists);
}
public void addChangeListener(ChangeListener listener) {
myListeners.add(listener);
}
public void removeChangeListener(ChangeListener listener) {
myListeners.remove(listener);
}
public void resetFilterBase() {
myUI.reset();
}
public void appendFilterBase(List<CommittedChangeList> changeLists) {
myUI.append(changeLists);
}
@NotNull
public List<CommittedChangeList> filterChangeLists(List<CommittedChangeList> changeLists) {
if (mySelection.size() == 0) {
return changeLists;
}
final ArrayList<CommittedChangeList> result = new ArrayList<CommittedChangeList>();
for (CommittedChangeList list : changeLists) {
if (listMatchesSelection(list)) {
result.add(list);
}
}
return result;
}
private boolean listMatchesSelection(final CommittedChangeList list) {
for (Change change : list.getChanges()) {
FilePath path = ChangesUtil.getFilePath(change);
for (FilePath selPath : mySelection) {
if (path.isUnder(selPath, false)) {
return true;
}
}
}
return false;
}
private class MyUI {
private final JComponent myScrollPane;
private final Tree myStructureTree;
private boolean myRendererInitialized;
private final TreeModelBuilder myBuilder;
private TreeState myState;
public MyUI() {
myStructureTree = new Tree();
myStructureTree.setRootVisible(false);
myStructureTree.setShowsRootHandles(true);
myStructureTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(final TreeSelectionEvent e) {
final List<FilePath> filePaths = new ArrayList<FilePath>(mySelection);
mySelection.clear();
final TreePath[] selectionPaths = myStructureTree.getSelectionPaths();
if (selectionPaths != null) {
for (TreePath selectionPath : selectionPaths) {
Collections.addAll(mySelection, ((ChangesBrowserNode)selectionPath.getLastPathComponent()).getFilePathsUnder());
}
}
if (Comparing.haveEqualElements(filePaths, mySelection)) return;
for (ChangeListener listener : myListeners) {
listener.stateChanged(new ChangeEvent(this));
}
}
});
myScrollPane = ScrollPaneFactory.createScrollPane(myStructureTree);
myBuilder = new TreeModelBuilder(myProject, false);
}
public void initRenderer() {
if (!myRendererInitialized) {
myRendererInitialized = true;
myStructureTree.setCellRenderer(new ChangesBrowserNodeRenderer(myProject, false, false));
}
}
public JComponent getComponent() {
return myScrollPane;
}
public void reset() {
myState = TreeState.createOn(myStructureTree, (DefaultMutableTreeNode)myStructureTree.getModel().getRoot());
myStructureTree.setModel(myBuilder.clearAndGetModel());
}
public void append(final List<CommittedChangeList> changeLists) {
final TreeState localState = (myState != null) && myBuilder.isEmpty()
? myState
: TreeState.createOn(myStructureTree, (DefaultMutableTreeNode)myStructureTree.getModel().getRoot());
final Set<FilePath> filePaths = new HashSet<FilePath>();
for (CommittedChangeList changeList : changeLists) {
for (Change change : changeList.getChanges()) {
final FilePath path = ChangesUtil.getFilePath(change);
if (path.getParentPath() != null) {
filePaths.add(path.getParentPath());
}
}
}
final DefaultTreeModel model = myBuilder.buildModelFromFilePaths(filePaths);
myStructureTree.setModel(model);
localState.applyTo(myStructureTree, (DefaultMutableTreeNode)myStructureTree.getModel().getRoot());
myStructureTree.revalidate();
myStructureTree.repaint();
initRenderer();
}
}
}