ws
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / ui / MultipleChangeListBrowser.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * Created by IntelliJ IDEA.
19  * User: yole
20  * Date: 28.11.2006
21  * Time: 14:15:18
22  */
23 package com.intellij.openapi.vcs.changes.ui;
24
25 import com.intellij.openapi.actionSystem.*;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.application.ModalityState;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.ui.DialogWrapper;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.openapi.util.IconLoader;
32 import com.intellij.openapi.vcs.AbstractVcs;
33 import com.intellij.openapi.vcs.VcsBundle;
34 import com.intellij.openapi.vcs.changes.*;
35 import com.intellij.openapi.vcs.changes.actions.MoveChangesToAnotherListAction;
36 import com.intellij.openapi.vcs.changes.actions.RollbackDialogAction;
37 import com.intellij.ui.ColoredListCellRenderer;
38 import com.intellij.ui.SimpleTextAttributes;
39 import com.intellij.util.EventDispatcher;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
42
43 import javax.swing.*;
44 import java.awt.*;
45 import java.awt.event.ItemEvent;
46 import java.awt.event.ItemListener;
47 import java.util.*;
48 import java.util.List;
49
50 public class MultipleChangeListBrowser extends ChangesBrowser {
51   private final ChangeListChooser myChangeListChooser;
52   private final ChangeListListener myChangeListListener = new MyChangeListListener();
53   private final boolean myShowingAllChangeLists;
54   private final EventDispatcher<SelectedListChangeListener> myDispatcher = EventDispatcher.create(SelectedListChangeListener.class);
55   private Collection<Change> myAllChanges;
56   private Map<Change, LocalChangeList> myChangeListsMap;
57
58   private final ChangesBrowserExtender myExtender;
59   private final Runnable myRebuildListListener;
60
61   public MultipleChangeListBrowser(final Project project, final List<? extends ChangeList> changeLists, final List<Change> changes,
62                                    final ChangeList initialListSelection,
63                                    final boolean capableOfExcludingChanges,
64                                    final boolean highlightProblems, final Runnable rebuildListListener, @Nullable final Runnable inclusionListener) {
65     super(project, changeLists, changes, initialListSelection, capableOfExcludingChanges, highlightProblems, inclusionListener, MyUseCase.LOCAL_CHANGES);
66     myRebuildListListener = rebuildListListener;
67
68     myChangeListChooser = new ChangeListChooser(changeLists);
69     myHeaderPanel.add(myChangeListChooser, BorderLayout.EAST);
70     myShowingAllChangeLists = Comparing.haveEqualElements((List<LocalChangeList>) changeLists, ChangeListManager.getInstance(project).getChangeLists());
71     ChangeListManager.getInstance(myProject).addChangeListListener(myChangeListListener);
72
73     myExtender = new Extender(project, this);
74   }
75
76   @Override
77   protected void setInitialSelection(final List<? extends ChangeList> changeLists, final List<Change> changes, final ChangeList initialListSelection) {
78     myAllChanges = new ArrayList<Change>();
79     mySelectedChangeList = initialListSelection;
80
81     for (ChangeList list : changeLists) {
82       if (list instanceof LocalChangeList) {
83         myAllChanges.addAll(list.getChanges());
84         if (initialListSelection == null) {
85           for(Change c: list.getChanges()) {
86             if (changes.contains(c)) {
87               mySelectedChangeList = list;
88               break;
89             }
90           }
91         }
92       }
93     }
94
95     if (mySelectedChangeList == null) {
96       for(ChangeList list: changeLists) {
97         if (list instanceof LocalChangeList && ((LocalChangeList) list).isDefault()) {
98           mySelectedChangeList = list;
99           break;
100         }
101       }
102       if (mySelectedChangeList == null && !changeLists.isEmpty()) {
103         mySelectedChangeList = changeLists.get(0);
104       }
105     }
106   }
107
108   @Override
109   public void dispose() {
110     ChangeListManager.getInstance(myProject).removeChangeListListener(myChangeListListener);
111   }
112
113   public Collection<Change> getAllChanges() {
114     return myAllChanges;
115   }
116
117   public ChangesBrowserExtender getExtender() {
118     return myExtender;
119   }
120
121   public void addSelectedListChangeListener(SelectedListChangeListener listener) {
122     myDispatcher.addListener(listener);
123   }
124
125   public void removeSelectedListChangeListener(SelectedListChangeListener listener) {
126     myDispatcher.removeListener(listener);
127   }
128
129   private void setSelectedList(final ChangeList list) {
130     mySelectedChangeList = list;
131     rebuildList();
132     myDispatcher.getMulticaster().selectedListChanged();
133   }
134
135   private boolean myInRebuildList;
136
137   @Override
138   public void rebuildList() {
139     if (myInRebuildList) return;
140     try {
141       myInRebuildList = true;
142       if (myChangesToDisplay == null) {
143         // changes set not fixed === local changes
144         final ChangeListManager manager = ChangeListManager.getInstance(myProject);
145         myChangeListsMap = new HashMap<Change, LocalChangeList>();
146         final List<LocalChangeList> lists = manager.getChangeListsCopy();
147         Collection<Change> allChanges = new ArrayList<Change>();
148         for (LocalChangeList list : lists) {
149           final Collection<Change> changes = list.getChanges();
150           allChanges.addAll(changes);
151           for (Change change : changes) {
152             myChangeListsMap.put(change, list);
153           }
154         }
155         myAllChanges = allChanges;
156         // refresh selected list also
157         updateListsInChooser();
158       }
159
160       super.rebuildList();
161       if (myRebuildListListener != null) {
162         myRebuildListListener.run();
163       }
164     } finally {
165       myInRebuildList = false;
166     }
167   }
168
169   @Override
170   public List<Change> getCurrentDisplayedChanges() {
171     if (myChangesToDisplay == null) {
172       return sortChanges(filterBySelectedChangeList(myAllChanges));
173     }
174     return super.getCurrentDisplayedChanges();
175   }
176
177   @NotNull
178   public List<Change> getCurrentIncludedChanges() {
179     return filterBySelectedChangeList(myViewer.getIncludedChanges());
180   }
181
182   private List<Change> filterBySelectedChangeList(final Collection<Change> changes) {
183     List<Change> filtered = new ArrayList<Change>();
184     for (Change change : changes) {
185       if (Comparing.equal(getList(change), mySelectedChangeList)) {
186         filtered.add(change);
187       }
188     }
189     return filtered;
190   }
191
192   private ChangeList getList(final Change change) {
193     return myChangeListsMap.get(change);
194   }
195
196   @Override
197   protected void buildToolBar(final DefaultActionGroup toolBarGroup) {
198     super.buildToolBar(toolBarGroup);
199
200     final MoveChangesToAnotherListAction moveAction = new MoveChangesToAnotherListAction() {
201       public void actionPerformed(AnActionEvent e) {
202         super.actionPerformed(e);
203         rebuildList();
204       }
205     };
206
207     moveAction.registerCustomShortcutSet(CommonShortcuts.getMove(), myViewer);
208     toolBarGroup.add(moveAction);
209   }
210
211   @Override
212   protected List<AnAction> createDiffActions(final Change change) {
213     List<AnAction> actions = super.createDiffActions(change);
214     actions.add(new MoveAction(change));
215     return actions;
216   }
217
218   private class ChangeListChooser extends JPanel {
219     private final JComboBox myChooser;
220     private final static int MAX_LEN = 35;
221
222     public ChangeListChooser(List<? extends ChangeList> lists) {
223       super(new BorderLayout());
224       myChooser = new JComboBox() {
225         public Dimension getMinimumSize() {
226           return new Dimension(0, 0);
227         }
228       };
229       myChooser.setRenderer(new ColoredListCellRenderer() {
230         protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
231           final LocalChangeList l = ((LocalChangeList)value);
232           if (l != null) {
233             String name = l.getName().trim();
234             if (name.length() > MAX_LEN) {
235               name = name.substring(0, MAX_LEN - 3) + "...";
236             }
237             append(name, l.isDefault() ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
238           }
239         }
240       });
241
242       myChooser.addItemListener(new ItemListener() {
243         public void itemStateChanged(ItemEvent e) {
244           if (e.getStateChange() == ItemEvent.SELECTED) {
245             final LocalChangeList changeList = (LocalChangeList)myChooser.getSelectedItem();
246             setSelectedList(changeList);
247             myChooser.setToolTipText(changeList == null ? "" : (changeList.getName()));
248           }
249         }
250       });
251
252       updateLists(lists);
253       myChooser.setEditable(false);
254       add(myChooser, BorderLayout.CENTER);
255
256       JLabel label = new JLabel(VcsBundle.message("commit.dialog.changelist.label"));
257       label.setDisplayedMnemonic('l');
258       label.setLabelFor(myChooser);
259       add(label, BorderLayout.WEST);
260     }
261
262     public void updateLists(List<? extends ChangeList> lists) {
263       myChooser.setModel(new DefaultComboBoxModel(lists.toArray()));
264       myChooser.setEnabled(lists.size() > 1);
265       if (lists.contains(mySelectedChangeList)) {
266         myChooser.setSelectedItem(mySelectedChangeList);
267       } else {
268         if (myChooser.getItemCount() > 0) {
269           myChooser.setSelectedIndex(0);
270         }
271       }
272       mySelectedChangeList = (ChangeList) myChooser.getSelectedItem();
273     }
274   }
275
276   private class MyChangeListListener extends ChangeListAdapter {
277     public void changeListAdded(ChangeList list) {
278       updateListsInChooser();
279     }
280   }
281
282   private void updateListsInChooser() {
283     Runnable runnable = new Runnable() {
284       public void run() {
285         if (myChangeListChooser != null && myShowingAllChangeLists) {
286           myChangeListChooser.updateLists(ChangeListManager.getInstance(myProject).getChangeListsCopy());
287         }
288       }
289     };
290     if (SwingUtilities.isEventDispatchThread()) {
291       runnable.run();
292     }
293     else {
294       ApplicationManager.getApplication().invokeLater(runnable, ModalityState.stateForComponent(MultipleChangeListBrowser.this));
295     }
296   }
297
298   private class MoveAction extends MoveChangesToAnotherListAction {
299     private final Change myChange;
300
301     public MoveAction(final Change change) {
302       myChange = change;
303     }
304
305     public void actionPerformed(AnActionEvent e) {
306       askAndMove(myProject, new Change[]{myChange}, null);
307     }
308   }
309
310   private static class Extender implements ChangesBrowserExtender {
311     private final Project myProject;
312     private final MultipleChangeListBrowser myBrowser;
313
314     private Extender(final Project project, final MultipleChangeListBrowser browser) {
315       myProject = project;
316       myBrowser = browser;
317     }
318
319     public void addToolbarActions(final DialogWrapper dialogWrapper) {
320       final Icon icon = IconLoader.getIcon("/vcs/refresh.png");
321       if (myBrowser.myChangesToDisplay == null) {
322         myBrowser.addToolbarAction(new AnAction("Refresh Changes") {
323           @Override
324           public void actionPerformed(AnActionEvent e) {
325             myBrowser.rebuildList();
326           }
327
328           @Override
329           public void update(AnActionEvent e) {
330             e.getPresentation().setIcon(icon);
331           }
332         });
333       }
334       myBrowser.addToolbarAction(new RollbackDialogAction());
335       final EditSourceInCommitAction editSourceAction = new EditSourceInCommitAction(dialogWrapper);
336       editSourceAction.registerCustomShortcutSet(CommonShortcuts.getEditSource(), myBrowser);
337       myBrowser.addToolbarAction(editSourceAction);
338
339       myBrowser.addToolbarAction(ActionManager.getInstance().getAction("Vcs.CheckinProjectToolbar"));
340
341       final List<AnAction> actions = AdditionalLocalChangeActionsInstaller.calculateActions(myProject, myBrowser.getAllChanges());
342       if (actions != null) {
343         for (AnAction action : actions) {
344           myBrowser.addToolbarAction(action);
345         }
346       }
347     }
348
349     public void addSelectedListChangeListener(final SelectedListChangeListener listener) {
350       myBrowser.addSelectedListChangeListener(listener);
351     }
352
353     public List<AbstractVcs> getAffectedVcses() {
354       Set<AbstractVcs> result = new HashSet<AbstractVcs>();
355       for (Change change : myBrowser.myAllChanges) {
356         final AbstractVcs vcs = ChangesUtil.getVcsForChange(change, myBrowser.myProject);
357         if (vcs != null) {
358           result.add(vcs);
359         }
360       }
361       return new ArrayList<AbstractVcs>(result);
362     }
363
364     public List<Change> getCurrentIncludedChanges() {
365       return myBrowser.getCurrentIncludedChanges();
366     }
367   }
368 }