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.
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
;
49 import javax
.swing
.event
.ChangeEvent
;
50 import javax
.swing
.event
.ChangeListener
;
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
;
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());
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() {
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();
130 refreshChangesFromCache(cacheOnly
);
134 private void refreshChangesFromLocation() {
138 myBrowser
.setLoading(true);
139 ProgressManager
.getInstance().run(new Task
.Backgroundable(myProject
, "Loading changes", true, BackgroundFromStartOption
.getInstance()) {
141 public void run(@NotNull final ProgressIndicator indicator
) {
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
)) {
150 myBrowser
.append(list
);
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
) {
165 ProgressManager
.checkCanceled();
166 bufferedListConsumer
.consumeOne(committedChangeList
);
170 catch (final VcsException e
) {
171 ApplicationManager
.getApplication().invokeLater(new Runnable() {
174 Messages
.showErrorDialog(myProject
, "Error refreshing view: " + StringUtil
.join(e
.getMessages(), "\n"), "Committed Changes");
179 myBrowser
.setLoading(false);
185 private void refreshChangesFromCache(final boolean cacheOnly
) {
186 final CommittedChangesCache cache
= CommittedChangesCache
.getInstance(myProject
);
187 if (!cache
.hasCachesForAnyRoot()) {
189 myBrowser
.setEmptyText(VcsBundle
.message("committed.changes.not.loaded.message"));
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) {
236 private void updateFilteredModel(final boolean keepFilter
) {
237 if (myChangesFromProvider
== null) {
240 myBrowser
.setEmptyText(VcsBundle
.message("committed.changes.empty.message"));
241 if (StringUtil
.isEmpty(myFilterComponent
.getFilter())) {
242 myBrowser
.setItems(myChangesFromProvider
, keepFilter
, CommittedChangesBrowserUseCase
.COMMITTED
);
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
);
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() {
273 for (Runnable runnable
: myShouldBeCalledOnDispose
) {
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() {
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
) {
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
)) {
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() {