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
;
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
;
56 import javax
.swing
.tree
.DefaultMutableTreeNode
;
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);
92 public ChangesViewManager(Project project
, ChangesViewContentManager contentManager
) {
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
);
113 final MessageBusConnection connection
= myProject
.getMessageBus().connect(myProject
);
114 connection
.subscribe(RemoteRevisionsCache
.REMOTE_VERSION_CHANGED
, new Runnable() {
116 ApplicationManager
.getApplication().invokeLater(new Runnable() {
120 }, ModalityState
.NON_MODAL
, myProject
.getDisposed());
125 public void projectClosed() {
127 myRepaintAlarm
.cancelAllRequests();
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
,
161 ? InputEvent
.META_DOWN_MASK
162 : InputEvent
.CTRL_DOWN_MASK
)),
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
));
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() {
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() {
213 }, 100, ModalityState
.NON_MODAL
);
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
);
247 TreeUtil
.selectNode(myView
, node
);
251 public void refreshChangesViewNodeAsync(final VirtualFile file
) {
252 ApplicationManager
.getApplication().invokeLater(new Runnable() {
254 if (myProject
.isDisposed()) return;
255 refreshChangesViewNode(file
);
260 private void refreshChangesViewNode(final VirtualFile file
) {
261 DefaultMutableTreeNode root
= (DefaultMutableTreeNode
)myView
.getModel().getRoot();
263 final ChangeListManager changeListManager
= ChangeListManager
.getInstance(myProject
);
264 if (changeListManager
.isUnversioned(file
)) {
268 userObject
= changeListManager
.getChange(file
);
270 if (userObject
!= null) {
271 final DefaultMutableTreeNode node
= TreeUtil
.findNodeWithObject(root
, userObject
);
273 myView
.getModel().nodeChanged(node
);
278 private class MyChangeListListener
extends ChangeListAdapter
{
280 public void changeListAdded(ChangeList list
) {
284 public void changeListRemoved(ChangeList list
) {
288 public void changeListRenamed(ChangeList list
, String oldName
) {
292 public void changesMoved(Collection
<Change
> changes
, ChangeList fromList
, ChangeList toList
) {
296 public void defaultListChanged(final ChangeList oldDefaultList
, ChangeList newDefaultList
) {
300 public void changeListUpdateDone() {
302 ChangeListManagerImpl changeListManager
= ChangeListManagerImpl
.getInstanceImpl(myProject
);
303 VcsException updateException
= changeListManager
.getUpdateException();
304 if (updateException
== null) {
305 updateProgressText("", false);
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() {
322 public void collapseAll() {
323 TreeUtil
.collapseAll(myView
, 2);
324 TreeUtil
.expand(myView
, 1);
327 public boolean canCollapse() {
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
);
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
;