VCS: asynch load of committed changes when do "Browse changes"
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / committed / CommittedChangesPanel.java
blob57f3aa19d79cc069c4d1ec0ef546ee1800605042
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
19 * User: yole
20 * Date: 05.12.2006
21 * Time: 19:39:22
23 package com.intellij.openapi.vcs.changes.committed;
25 import com.intellij.openapi.Disposable;
26 import com.intellij.openapi.actionSystem.*;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.application.ModalityState;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.progress.ProgressIndicator;
31 import com.intellij.openapi.progress.ProgressManager;
32 import com.intellij.openapi.progress.Task;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.ui.Messages;
35 import com.intellij.openapi.util.text.StringUtil;
36 import com.intellij.openapi.vcs.*;
37 import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
38 import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
39 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.ui.FilterComponent;
42 import com.intellij.util.AsynchConsumer;
43 import com.intellij.util.BufferedListConsumer;
44 import com.intellij.util.Consumer;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import javax.swing.*;
49 import javax.swing.event.ChangeEvent;
50 import javax.swing.event.ChangeListener;
51 import java.awt.*;
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.List;
56 public class CommittedChangesPanel extends JPanel implements TypeSafeDataProvider, Disposable {
57 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.committed.CommittedChangesPanel");
59 private final CommittedChangesTreeBrowser myBrowser;
60 private final Project myProject;
61 private CommittedChangesProvider myProvider;
62 private ChangeBrowserSettings mySettings;
63 private final RepositoryLocation myLocation;
64 private int myMaxCount = 0;
65 private final MyFilterComponent myFilterComponent = new MyFilterComponent();
66 private List<CommittedChangeList> myChangesFromProvider;
67 private final JLabel myErrorLabel = new JLabel();
68 private final List<Runnable> myShouldBeCalledOnDispose;
69 private volatile boolean myDisposed;
70 private volatile boolean myInLoad;
72 public CommittedChangesPanel(Project project, final CommittedChangesProvider provider, final ChangeBrowserSettings settings,
73 @Nullable final RepositoryLocation location, @Nullable ActionGroup extraActions) {
74 super(new BorderLayout());
75 mySettings = settings;
76 myProject = project;
77 myProvider = provider;
78 myLocation = location;
79 myShouldBeCalledOnDispose = new ArrayList<Runnable>();
80 myBrowser = new CommittedChangesTreeBrowser(project, new ArrayList<CommittedChangeList>());
81 add(myBrowser, BorderLayout.CENTER);
83 myErrorLabel.setForeground(Color.red);
84 add(myErrorLabel, BorderLayout.SOUTH);
86 final VcsCommittedViewAuxiliary auxiliary = provider.createActions(myBrowser, location);
88 JPanel toolbarPanel = new JPanel(new BorderLayout());
89 ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction("CommittedChangesToolbar");
91 ActionToolbar toolBar = myBrowser.createGroupFilterToolbar(project, group, extraActions,
92 auxiliary != null ? auxiliary.getToolbarActions() : Collections.<AnAction>emptyList());
93 toolbarPanel.add(toolBar.getComponent(), BorderLayout.WEST);
95 toolbarPanel.add(myFilterComponent, BorderLayout.EAST);
96 myBrowser.addToolBar(toolbarPanel);
98 if (auxiliary != null) {
99 myShouldBeCalledOnDispose.add(auxiliary.getCalledOnViewDispose());
100 myBrowser.setTableContextMenu(group, (auxiliary.getPopupActions() == null) ? Collections.<AnAction>emptyList() : auxiliary.getPopupActions());
101 } else {
102 myBrowser.setTableContextMenu(group, Collections.<AnAction>emptyList());
105 final AnAction anAction = ActionManager.getInstance().getAction("CommittedChanges.Refresh");
106 anAction.registerCustomShortcutSet(CommonShortcuts.getRerun(), this);
107 myBrowser.addFilter(myFilterComponent);
110 public RepositoryLocation getRepositoryLocation() {
111 return myLocation;
114 public void setMaxCount(final int maxCount) {
115 myMaxCount = maxCount;
118 public void setProvider(final CommittedChangesProvider provider) {
119 if (myProvider != provider) {
120 myProvider = provider;
121 mySettings = provider.createDefaultSettings();
125 public void refreshChanges(final boolean cacheOnly) {
126 if (myLocation != null) {
127 refreshChangesFromLocation();
129 else {
130 refreshChangesFromCache(cacheOnly);
134 private void refreshChangesFromLocation() {
135 myBrowser.reset();
137 myInLoad = true;
138 myBrowser.setLoading(true);
139 ProgressManager.getInstance().run(new Task.Backgroundable(myProject, "Loading changes", true, BackgroundFromStartOption.getInstance()) {
140 @Override
141 public void run(@NotNull final ProgressIndicator indicator) {
142 try {
143 final AsynchConsumer<List<CommittedChangeList>> appender = new AsynchConsumer<List<CommittedChangeList>>() {
144 public void finished() {
147 public void consume(final List<CommittedChangeList> list) {
148 new AbstractCalledLater(myProject, ModalityState.stateForComponent(myBrowser)) {
149 public void run() {
150 myBrowser.append(list);
152 }.callMe();
155 final BufferedListConsumer<CommittedChangeList> bufferedListConsumer = new BufferedListConsumer<CommittedChangeList>(30, appender);
157 myProvider.loadCommittedChanges(mySettings, myLocation, myMaxCount, new AsynchConsumer<CommittedChangeList>() {
158 public void finished() {
159 bufferedListConsumer.flush();
161 public void consume(CommittedChangeList committedChangeList) {
162 if (myDisposed) {
163 indicator.cancel();
165 ProgressManager.checkCanceled();
166 bufferedListConsumer.consumeOne(committedChangeList);
170 catch (final VcsException e) {
171 ApplicationManager.getApplication().invokeLater(new Runnable() {
172 public void run() {
173 LOG.info(e);
174 Messages.showErrorDialog(myProject, "Error refreshing view: " + StringUtil.join(e.getMessages(), "\n"), "Committed Changes");
177 } finally {
178 myInLoad = false;
179 myBrowser.setLoading(false);
185 private void refreshChangesFromCache(final boolean cacheOnly) {
186 final CommittedChangesCache cache = CommittedChangesCache.getInstance(myProject);
187 if (!cache.hasCachesForAnyRoot()) {
188 if (cacheOnly) {
189 myBrowser.setEmptyText(VcsBundle.message("committed.changes.not.loaded.message"));
190 return;
192 if (!CacheSettingsDialog.showSettingsDialog(myProject)) return;
194 cache.getProjectChangesAsync(mySettings, myMaxCount, cacheOnly,
195 new Consumer<List<CommittedChangeList>>() {
196 public void consume(final List<CommittedChangeList> committedChangeLists) {
197 myChangesFromProvider = committedChangeLists;
198 updateFilteredModel(false);
201 new Consumer<List<VcsException>>() {
202 public void consume(final List<VcsException> vcsExceptions) {
203 AbstractVcsHelper.getInstance(myProject).showErrors(vcsExceptions, "Error refreshing VCS history");
208 private static class FilterHelper {
209 private final String[] myParts;
211 FilterHelper(final String filterString) {
212 myParts = filterString.split(" ");
213 for(int i = 0; i < myParts.length; ++ i) {
214 myParts [i] = myParts [i].toLowerCase();
218 public boolean filter(@NotNull final CommittedChangeList cl) {
219 return changeListMatches(cl, myParts);
222 private static boolean changeListMatches(@NotNull final CommittedChangeList changeList, final String[] filterWords) {
223 for(String word: filterWords) {
224 final String comment = changeList.getComment();
225 final String committer = changeList.getCommitterName();
226 if ((comment != null && comment.toLowerCase().indexOf(word) >= 0) ||
227 (committer != null && committer.toLowerCase().indexOf(word) >= 0) ||
228 Long.toString(changeList.getNumber()).indexOf(word) >= 0) {
229 return true;
232 return false;
236 private void updateFilteredModel(final boolean keepFilter) {
237 if (myChangesFromProvider == null) {
238 return;
240 myBrowser.setEmptyText(VcsBundle.message("committed.changes.empty.message"));
241 if (StringUtil.isEmpty(myFilterComponent.getFilter())) {
242 myBrowser.setItems(myChangesFromProvider, keepFilter, CommittedChangesBrowserUseCase.COMMITTED);
244 else {
245 final FilterHelper filterHelper = new FilterHelper(myFilterComponent.getFilter());
246 List<CommittedChangeList> filteredChanges = new ArrayList<CommittedChangeList>();
247 for(CommittedChangeList changeList: myChangesFromProvider) {
248 if (filterHelper.filter(changeList)) {
249 filteredChanges.add(changeList);
252 myBrowser.setItems(filteredChanges, keepFilter, CommittedChangesBrowserUseCase.COMMITTED);
256 public void setChangesFilter() {
257 CommittedChangesFilterDialog filterDialog = new CommittedChangesFilterDialog(myProject, myProvider.createFilterUI(true), mySettings);
258 filterDialog.show();
259 if (filterDialog.isOK()) {
260 mySettings = filterDialog.getSettings();
261 refreshChanges(false);
265 public void calcData(DataKey key, DataSink sink) {
266 if (key.equals(VcsDataKeys.CHANGES) || key.equals(VcsDataKeys.CHANGE_LISTS)) {
267 myBrowser.calcData(key, sink);
271 public void dispose() {
272 myBrowser.dispose();
273 for (Runnable runnable : myShouldBeCalledOnDispose) {
274 runnable.run();
276 myDisposed = true;
279 public void setErrorText(String text) {
280 myErrorLabel.setText(text);
283 private class MyFilterComponent extends FilterComponent implements ChangeListFilteringStrategy {
284 private final List<ChangeListener> myList;
286 public MyFilterComponent() {
287 super("COMMITTED_CHANGES_FILTER_HISTORY", 20);
288 myList = new ArrayList<ChangeListener>();
291 public void filter() {
292 for (ChangeListener changeListener : myList) {
293 changeListener.stateChanged(new ChangeEvent(this));
296 public JComponent getFilterUI() {
297 return null;
299 public void setFilterBase(List<CommittedChangeList> changeLists) {
301 public void addChangeListener(ChangeListener listener) {
302 myList.add(listener);
304 public void removeChangeListener(ChangeListener listener) {
305 myList.remove(listener);
307 public void resetFilterBase() {
309 public void appendFilterBase(List<CommittedChangeList> changeLists) {
311 @NotNull
312 public List<CommittedChangeList> filterChangeLists(List<CommittedChangeList> changeLists) {
313 final FilterHelper filterHelper = new FilterHelper(myFilterComponent.getFilter());
314 final List<CommittedChangeList> result = new ArrayList<CommittedChangeList>();
315 for (CommittedChangeList list : changeLists) {
316 if (filterHelper.filter(list)) {
317 result.add(list);
320 return result;
324 public void passCachedListsToListener(final VcsConfigurationChangeListener.DetailedNotification notification,
325 final Project project, final VirtualFile root) {
326 if ((myChangesFromProvider != null) && (! myChangesFromProvider.isEmpty())) {
327 notification.execute(project, root, myChangesFromProvider);
331 public boolean isInLoad() {
332 return myInLoad;