IDEA-27603 (Indicate which branch is being worked on)
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / ChangesViewManager.java
blobfffb8c462249df20942b1ac2547b608c4a815b10
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: 11.07.2006
21 * Time: 15:29:25
23 package com.intellij.openapi.vcs.changes;
25 import com.intellij.ide.CommonActionsManager;
26 import com.intellij.ide.TreeExpander;
27 import com.intellij.ide.actions.ContextHelpAction;
28 import com.intellij.openapi.Disposable;
29 import com.intellij.openapi.actionSystem.*;
30 import com.intellij.openapi.application.ApplicationManager;
31 import com.intellij.openapi.application.ModalityState;
32 import com.intellij.openapi.components.AbstractProjectComponent;
33 import com.intellij.openapi.progress.ProcessCanceledException;
34 import com.intellij.openapi.project.DumbAware;
35 import com.intellij.openapi.project.Project;
36 import com.intellij.openapi.ui.SimpleToolWindowPanel;
37 import com.intellij.openapi.util.*;
38 import com.intellij.openapi.vcs.VcsBundle;
39 import com.intellij.openapi.vcs.VcsException;
40 import com.intellij.openapi.vcs.changes.actions.IgnoredSettingsAction;
41 import com.intellij.openapi.vcs.changes.ui.ChangesListView;
42 import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
43 import com.intellij.openapi.vfs.VirtualFile;
44 import com.intellij.ui.content.Content;
45 import com.intellij.ui.content.ContentFactory;
46 import com.intellij.util.Alarm;
47 import com.intellij.util.Icons;
48 import com.intellij.util.messages.MessageBusConnection;
49 import com.intellij.util.ui.UIUtil;
50 import com.intellij.util.ui.tree.TreeUtil;
51 import org.jdom.Element;
52 import org.jetbrains.annotations.NonNls;
53 import org.jetbrains.annotations.NotNull;
55 import javax.swing.*;
56 import javax.swing.tree.DefaultMutableTreeNode;
57 import java.awt.*;
58 import java.awt.event.InputEvent;
59 import java.awt.event.KeyEvent;
60 import java.util.Collection;
62 public class ChangesViewManager extends AbstractProjectComponent implements JDOMExternalizable {
63 private boolean SHOW_FLATTEN_MODE = true;
64 private boolean SHOW_IGNORED_MODE = false;
66 private final ChangesListView myView;
67 private JLabel myProgressLabel;
69 private final Alarm myRepaintAlarm;
71 private boolean myDisposed = false;
73 private final ChangeListListener myListener = new MyChangeListListener();
74 private final ChangesViewContentManager myContentManager;
76 @NonNls private static final String ATT_FLATTENED_VIEW = "flattened_view";
77 @NonNls private static final String ATT_SHOW_IGNORED = "show_ignored";
79 public static ChangesViewManager getInstance(Project project) {
80 return project.getComponent(ChangesViewManager.class);
83 public static ChangesViewManager getInstanceChecked(final Project project) {
84 return ApplicationManager.getApplication().runReadAction(new Computable<ChangesViewManager>() {
85 public ChangesViewManager compute() {
86 if (project.isDisposed()) throw new ProcessCanceledException();
87 return project.getComponent(ChangesViewManager.class);
89 });
92 public ChangesViewManager(Project project, ChangesViewContentManager contentManager) {
93 super(project);
94 myContentManager = contentManager;
95 myView = new ChangesListView(project);
96 Disposer.register(project, myView);
97 myRepaintAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project);
100 public void projectOpened() {
101 ChangeListManager.getInstance(myProject).addChangeListListener(myListener);
102 Disposer.register(myProject, new Disposable() {
103 public void dispose() {
104 ChangeListManager.getInstance(myProject).removeChangeListListener(myListener);
107 if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
108 final Content content = ContentFactory.SERVICE.getInstance().createContent(createChangeViewComponent(), "Local", false);
109 content.setCloseable(false);
110 myContentManager.addContent(content);
112 scheduleRefresh();
113 final MessageBusConnection connection = myProject.getMessageBus().connect(myProject);
114 connection.subscribe(RemoteRevisionsCache.REMOTE_VERSION_CHANGED, new Runnable() {
115 public void run() {
116 ApplicationManager.getApplication().invokeLater(new Runnable() {
117 public void run() {
118 refreshView();
120 }, ModalityState.NON_MODAL, myProject.getDisposed());
125 public void projectClosed() {
126 myDisposed = true;
127 myRepaintAlarm.cancelAllRequests();
130 @NonNls @NotNull
131 public String getComponentName() {
132 return "ChangesViewManager";
135 private JComponent createChangeViewComponent() {
136 SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true);
138 DefaultActionGroup group = (DefaultActionGroup) ActionManager.getInstance().getAction("ChangesViewToolbar");
140 ActionManager.getInstance().getAction("ChangesView.Refresh").registerCustomShortcutSet(CommonShortcuts.getRerun(), panel);
141 ActionManager.getInstance().getAction("ChangesView.NewChangeList").registerCustomShortcutSet(CommonShortcuts.getNew(), panel);
142 ActionManager.getInstance().getAction("ChangesView.RemoveChangeList").registerCustomShortcutSet(CommonShortcuts.DELETE, panel);
143 ActionManager.getInstance().getAction("ChangesView.Move").registerCustomShortcutSet(CommonShortcuts.getMove(), panel);
144 ActionManager.getInstance().getAction("ChangesView.Rename").registerCustomShortcutSet(CommonShortcuts.getRename(), panel);
146 final CustomShortcutSet diffShortcut =
147 new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_D, SystemInfo.isMac ? InputEvent.META_DOWN_MASK : InputEvent.CTRL_DOWN_MASK));
148 ActionManager.getInstance().getAction("ChangesView.Diff").registerCustomShortcutSet(diffShortcut, panel);
150 JPanel toolbarPanel = new JPanel(new BorderLayout());
151 toolbarPanel.add(createToolbarComponent(group), BorderLayout.WEST);
153 DefaultActionGroup visualActionsGroup = new DefaultActionGroup();
154 final Expander expander = new Expander();
155 visualActionsGroup.add(CommonActionsManager.getInstance().createExpandAllAction(expander, panel));
156 visualActionsGroup.add(CommonActionsManager.getInstance().createCollapseAllAction(expander, panel));
158 ToggleShowFlattenAction showFlattenAction = new ToggleShowFlattenAction();
159 showFlattenAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_P,
160 SystemInfo.isMac
161 ? InputEvent.META_DOWN_MASK
162 : InputEvent.CTRL_DOWN_MASK)),
163 panel);
164 visualActionsGroup.add(showFlattenAction);
165 visualActionsGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_COPY));
166 visualActionsGroup.add(new ToggleShowIgnoredAction());
167 visualActionsGroup.add(new IgnoredSettingsAction());
168 visualActionsGroup.add(new ContextHelpAction(ChangesListView.ourHelpId));
169 toolbarPanel.add(createToolbarComponent(visualActionsGroup), BorderLayout.CENTER);
172 DefaultActionGroup menuGroup = (DefaultActionGroup) ActionManager.getInstance().getAction("ChangesViewPopupMenu");
173 myView.setMenuActions(menuGroup);
175 myView.setShowFlatten(SHOW_FLATTEN_MODE);
177 myProgressLabel = new JLabel();
179 panel.setToolbar(toolbarPanel);
181 final JPanel content = new JPanel(new BorderLayout());
182 content.add(new JScrollPane(myView), BorderLayout.CENTER);
183 content.add(myProgressLabel, BorderLayout.SOUTH);
184 panel.setContent(content);
186 myView.installDndSupport(ChangeListManagerImpl.getInstanceImpl(myProject));
187 return panel;
190 private static JComponent createToolbarComponent(final DefaultActionGroup group) {
191 final ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.CHANGES_VIEW_TOOLBAR, group, false);
192 return actionToolbar.getComponent();
195 void updateProgressText(final String text, final boolean isError) {
196 if (myProgressLabel != null) {
197 SwingUtilities.invokeLater(new Runnable() {
198 public void run() {
199 myProgressLabel.setText(text);
200 myProgressLabel.setForeground(isError ? Color.red : UIUtil.getLabelForeground());
206 public void scheduleRefresh() {
207 if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
208 myRepaintAlarm.cancelAllRequests();
209 myRepaintAlarm.addRequest(new Runnable() {
210 public void run() {
211 refreshView();
213 }, 100, ModalityState.NON_MODAL);
216 void refreshView() {
217 if (myDisposed || ! myProject.isInitialized() || ApplicationManager.getApplication().isUnitTestMode()) return;
218 ChangeListManagerImpl changeListManager = ChangeListManagerImpl.getInstanceImpl(myProject);
219 myView.updateModel(changeListManager.getChangeListsCopy(),
220 changeListManager.getUnversionedFiles(),
221 changeListManager.getDeletedFiles(),
222 changeListManager.getModifiedWithoutEditing(),
223 changeListManager.getSwitchedFilesMap(),
224 changeListManager.getSwitchedRoots(),
225 SHOW_IGNORED_MODE ? changeListManager.getIgnoredFiles() : null, changeListManager.getLockedFolders(),
226 changeListManager.getLogicallyLockedFolders());
229 public void readExternal(Element element) throws InvalidDataException {
230 SHOW_FLATTEN_MODE = Boolean.valueOf(element.getAttributeValue(ATT_FLATTENED_VIEW)).booleanValue();
231 SHOW_IGNORED_MODE = Boolean.valueOf(element.getAttributeValue(ATT_SHOW_IGNORED)).booleanValue();
234 public void writeExternal(Element element) throws WriteExternalException {
235 element.setAttribute(ATT_FLATTENED_VIEW, String.valueOf(SHOW_FLATTEN_MODE));
236 element.setAttribute(ATT_SHOW_IGNORED, String.valueOf(SHOW_IGNORED_MODE));
239 public void selectFile(final VirtualFile vFile) {
240 if (vFile == null) return;
241 Change change = ChangeListManager.getInstance(myProject).getChange(vFile);
242 Object objectToFind = change != null ? change : vFile;
244 DefaultMutableTreeNode root = (DefaultMutableTreeNode)myView.getModel().getRoot();
245 DefaultMutableTreeNode node = TreeUtil.findNodeWithObject(root, objectToFind);
246 if (node != null) {
247 TreeUtil.selectNode(myView, node);
251 public void refreshChangesViewNodeAsync(final VirtualFile file) {
252 ApplicationManager.getApplication().invokeLater(new Runnable() {
253 public void run() {
254 if (myProject.isDisposed()) return;
255 refreshChangesViewNode(file);
260 private void refreshChangesViewNode(final VirtualFile file) {
261 DefaultMutableTreeNode root = (DefaultMutableTreeNode)myView.getModel().getRoot();
262 Object userObject;
263 final ChangeListManager changeListManager = ChangeListManager.getInstance(myProject);
264 if (changeListManager.isUnversioned(file)) {
265 userObject = file;
267 else {
268 userObject = changeListManager.getChange(file);
270 if (userObject != null) {
271 final DefaultMutableTreeNode node = TreeUtil.findNodeWithObject(root, userObject);
272 if (node != null) {
273 myView.getModel().nodeChanged(node);
278 private class MyChangeListListener extends ChangeListAdapter {
280 public void changeListAdded(ChangeList list) {
281 scheduleRefresh();
284 public void changeListRemoved(ChangeList list) {
285 scheduleRefresh();
288 public void changeListRenamed(ChangeList list, String oldName) {
289 scheduleRefresh();
292 public void changesMoved(Collection<Change> changes, ChangeList fromList, ChangeList toList) {
293 scheduleRefresh();
296 public void defaultListChanged(final ChangeList oldDefaultList, ChangeList newDefaultList) {
297 scheduleRefresh();
300 public void changeListUpdateDone() {
301 scheduleRefresh();
302 ChangeListManagerImpl changeListManager = ChangeListManagerImpl.getInstanceImpl(myProject);
303 VcsException updateException = changeListManager.getUpdateException();
304 if (updateException == null) {
305 updateProgressText("", false);
307 else {
308 updateProgressText(VcsBundle.message("error.updating.changes", updateException.getMessage()), true);
313 private class Expander implements TreeExpander {
314 public void expandAll() {
315 TreeUtil.expandAll(myView);
318 public boolean canExpand() {
319 return true;
322 public void collapseAll() {
323 TreeUtil.collapseAll(myView, 2);
324 TreeUtil.expand(myView, 1);
327 public boolean canCollapse() {
328 return true;
332 public class ToggleShowFlattenAction extends ToggleAction implements DumbAware {
333 public ToggleShowFlattenAction() {
334 super(VcsBundle.message("changes.action.show.directories.text"),
335 VcsBundle.message("changes.action.show.directories.description"),
336 Icons.DIRECTORY_CLOSED_ICON);
339 public boolean isSelected(AnActionEvent e) {
340 return !SHOW_FLATTEN_MODE;
343 public void setSelected(AnActionEvent e, boolean state) {
344 SHOW_FLATTEN_MODE = !state;
345 myView.setShowFlatten(SHOW_FLATTEN_MODE);
346 refreshView();
350 public class ToggleShowIgnoredAction extends ToggleAction implements DumbAware {
351 public ToggleShowIgnoredAction() {
352 super(VcsBundle.message("changes.action.show.ignored.text"),
353 VcsBundle.message("changes.action.show.ignored.description"),
354 IconLoader.getIcon("/actions/showHiddens.png"));
357 public boolean isSelected(AnActionEvent e) {
358 return SHOW_IGNORED_MODE;
361 public void setSelected(AnActionEvent e, boolean state) {
362 SHOW_IGNORED_MODE = state;
363 refreshView();