fix SafeDeleteTest
[idea/community.git] / plugins / hg4idea / src / org / zmlx / hg4idea / HgVcs.java
1 // Copyright 2008-2010 Victor Iacoban
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software distributed under
10 // the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 // either express or implied. See the License for the specific language governing permissions and
12 // limitations under the License.
13 package org.zmlx.hg4idea;
14
15 import com.intellij.concurrency.JobScheduler;
16 import com.intellij.openapi.application.ApplicationManager;
17 import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
18 import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
19 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
20 import com.intellij.openapi.fileTypes.FileTypeManager;
21 import com.intellij.openapi.options.Configurable;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.Disposer;
24 import com.intellij.openapi.util.IconLoader;
25 import com.intellij.openapi.util.SystemInfo;
26 import com.intellij.openapi.vcs.AbstractVcs;
27 import com.intellij.openapi.vcs.CommittedChangesProvider;
28 import com.intellij.openapi.vcs.ProjectLevelVcsManager;
29 import com.intellij.openapi.vcs.VcsException;
30 import com.intellij.openapi.vcs.annotate.AnnotationProvider;
31 import com.intellij.openapi.vcs.changes.ChangeProvider;
32 import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
33 import com.intellij.openapi.vcs.diff.DiffProvider;
34 import com.intellij.openapi.vcs.history.VcsHistoryProvider;
35 import com.intellij.openapi.vcs.rollback.RollbackEnvironment;
36 import com.intellij.openapi.vcs.update.UpdateEnvironment;
37 import com.intellij.openapi.vfs.VirtualFile;
38 import com.intellij.openapi.vfs.VirtualFileListener;
39 import com.intellij.openapi.vfs.VirtualFileManager;
40 import com.intellij.openapi.wm.StatusBar;
41 import com.intellij.openapi.wm.WindowManager;
42 import com.intellij.util.messages.MessageBusConnection;
43 import com.intellij.util.messages.Topic;
44 import org.zmlx.hg4idea.provider.*;
45 import org.zmlx.hg4idea.provider.annotate.HgAnnotationProvider;
46 import org.zmlx.hg4idea.provider.commit.HgCheckinEnvironment;
47 import org.zmlx.hg4idea.provider.update.HgIntegrateEnvironment;
48 import org.zmlx.hg4idea.provider.update.HgUpdateEnvironment;
49 import org.zmlx.hg4idea.ui.HgChangesetStatus;
50 import org.zmlx.hg4idea.ui.HgCurrentBranchStatus;
51
52 import javax.swing.*;
53 import java.io.File;
54 import java.util.concurrent.ScheduledFuture;
55 import java.util.concurrent.TimeUnit;
56
57 public class HgVcs extends AbstractVcs {
58
59   public static final Topic<HgUpdater> BRANCH_TOPIC =
60     new Topic<HgUpdater>("hg4idea.branch", HgUpdater.class);
61
62   public static final Topic<HgUpdater> INCOMING_TOPIC =
63     new Topic<HgUpdater>("hg4idea.incoming", HgUpdater.class);
64
65   public static final Topic<HgUpdater> OUTGOING_TOPIC =
66     new Topic<HgUpdater>("hg4idea.outgoing", HgUpdater.class);
67
68   public static final Icon MERCURIAL_ICON = IconLoader.getIcon("/images/mercurial.png");
69
70   private static final Icon INCOMING_ICON = IconLoader.getIcon("/actions/moveDown.png");
71   private static final Icon OUTGOING_ICON = IconLoader.getIcon("/actions/moveUp.png");
72
73   public static final String VCS_NAME = "hg4idea";
74   public static final String DIRSTATE_FILE_PATH = ".hg/dirstate";
75   public static final String NOTIFICATION_GROUP_ID = "Mercurial";
76   public static final String HG_EXECUTABLE_FILE_NAME = (SystemInfo.isWindows ? "hg.exe" : "hg");
77
78   private static final String ORIG_FILE_PATTERN = "*.orig";
79
80   private final HgChangeProvider changeProvider;
81   private final HgProjectConfigurable configurable;
82   private final HgRollbackEnvironment rollbackEnvironment;
83   private final HgDiffProvider diffProvider;
84   private final HgHistoryProvider historyProvider;
85   private final HgCheckinEnvironment checkinEnvironment;
86   private final HgAnnotationProvider annotationProvider;
87   private final HgUpdateEnvironment updateEnvironment;
88   private final HgIntegrateEnvironment integrateEnvironment;
89   private final HgCachingCommitedChangesProvider commitedChangesProvider;
90   private final HgCurrentBranchStatus hgCurrentBranchStatus = new HgCurrentBranchStatus();
91   private final HgChangesetStatus incomingChangesStatus = new HgChangesetStatus(INCOMING_ICON);
92   private final HgChangesetStatus outgoingChangesStatus = new HgChangesetStatus(OUTGOING_ICON);
93   private MessageBusConnection messageBusConnection;
94   private ScheduledFuture<?> changesUpdaterScheduledFuture;
95   private final HgGlobalSettings globalSettings;
96   private final HgProjectSettings projectSettings;
97
98   private boolean started = false;
99   private HgVFSListener myVFSListener;
100   private VirtualFileListener myDirStateChangeListener;
101
102   public HgVcs(Project project,
103     HgGlobalSettings globalSettings, HgProjectSettings projectSettings) {
104     super(project, VCS_NAME);
105     this.globalSettings = globalSettings;
106     this.projectSettings = projectSettings;
107     configurable = new HgProjectConfigurable(projectSettings);
108     changeProvider = new HgChangeProvider(project, getKeyInstanceMethod());
109     rollbackEnvironment = new HgRollbackEnvironment(project);
110     diffProvider = new HgDiffProvider(project);
111     historyProvider = new HgHistoryProvider(project);
112     checkinEnvironment = new HgCheckinEnvironment(project);
113     annotationProvider = new HgAnnotationProvider(project);
114     updateEnvironment = new HgUpdateEnvironment(project);
115     integrateEnvironment = new HgIntegrateEnvironment(project);
116     commitedChangesProvider = new HgCachingCommitedChangesProvider(project);
117     myDirStateChangeListener = new HgDirStateChangeListener(myProject);
118   }
119
120   public String getDisplayName() {
121     return configurable.getDisplayName();
122   }
123
124   public Configurable getConfigurable() {
125     return configurable;
126   }
127
128   @Override
129   public ChangeProvider getChangeProvider() {
130     if (!started) {
131       return null;
132     }
133
134     return changeProvider;
135   }
136
137   @Override
138   public RollbackEnvironment getRollbackEnvironment() {
139     if (!started) {
140       return null;
141     }
142
143     return rollbackEnvironment;
144   }
145
146   @Override
147   public DiffProvider getDiffProvider() {
148     if (!started) {
149       return null;
150     }
151
152     return diffProvider;
153   }
154
155   @Override
156   public VcsHistoryProvider getVcsHistoryProvider() {
157     if (!started) {
158       return null;
159     }
160
161     return historyProvider;
162   }
163
164   @Override
165   public VcsHistoryProvider getVcsBlockHistoryProvider() {
166     return getVcsHistoryProvider();
167   }
168
169   @Override
170   public CheckinEnvironment getCheckinEnvironment() {
171     if (!started) {
172       return null;
173     }
174
175     return checkinEnvironment;
176   }
177
178   @Override
179   public AnnotationProvider getAnnotationProvider() {
180     if (!started) {
181       return null;
182     }
183
184     return annotationProvider;
185   }
186
187   @Override
188   public UpdateEnvironment getUpdateEnvironment() {
189     if (!started) {
190       return null;
191     }
192
193     return updateEnvironment;
194   }
195
196   @Override
197   public UpdateEnvironment getIntegrateEnvironment() {
198     if (!started) {
199       return null;
200     }
201
202     return integrateEnvironment;
203   }
204
205   @Override
206   public CommittedChangesProvider getCommittedChangesProvider() {
207     if (!started) {
208       return null;
209     }
210     return null;
211 //    return commitedChangesProvider;
212   }
213
214   @Override
215   public boolean isVersionedDirectory(VirtualFile dir) {
216     return HgUtil.getNearestHgRoot(dir) != null;
217   }
218
219   public boolean isStarted() {
220     return started;
221   }
222
223   @Override
224   protected void shutdown() throws VcsException {
225     started = false;
226   }
227
228   @Override
229   public void activate() {
230     // validate hg executable
231     if (ApplicationManager.getApplication().isUnitTestMode()) {
232       started = true;
233     } else {
234       HgExecutableValidator validator = new HgExecutableValidator(myProject);
235       started = validator.check(globalSettings);
236     }
237     if (!started) {
238       return;
239     }
240
241     // status bar
242     StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
243     if (statusBar != null) {
244       statusBar.addWidget(hgCurrentBranchStatus, myProject);
245       statusBar.addWidget(incomingChangesStatus, myProject);
246       statusBar.addWidget(outgoingChangesStatus, myProject);
247     }
248
249     // updaters and listeners
250     final HgIncomingStatusUpdater incomingUpdater = new HgIncomingStatusUpdater(incomingChangesStatus, projectSettings);
251     final HgOutgoingStatusUpdater outgoingUpdater = new HgOutgoingStatusUpdater(outgoingChangesStatus, projectSettings);
252     changesUpdaterScheduledFuture = JobScheduler.getScheduler().scheduleWithFixedDelay(
253       new Runnable() {
254         public void run() {
255           incomingUpdater.update(myProject);
256           outgoingUpdater.update(myProject);
257         }
258       }, 0, HgGlobalSettings.getIncomingCheckIntervalSeconds(), TimeUnit.SECONDS);
259
260     messageBusConnection = myProject.getMessageBus().connect();
261     messageBusConnection.subscribe(INCOMING_TOPIC, incomingUpdater);
262     messageBusConnection.subscribe(OUTGOING_TOPIC, outgoingUpdater);
263     messageBusConnection.subscribe(BRANCH_TOPIC, new HgCurrentBranchStatusUpdater(hgCurrentBranchStatus));
264     messageBusConnection.subscribe(
265       FileEditorManagerListener.FILE_EDITOR_MANAGER,
266       new FileEditorManagerAdapter() {
267         @Override
268         public void selectionChanged(FileEditorManagerEvent event) {
269           Project project = event.getManager().getProject();
270           project.getMessageBus()
271             .asyncPublisher(BRANCH_TOPIC)
272             .update(project);
273         }
274       }
275     );
276
277     myVFSListener = new HgVFSListener(myProject, this);
278     VirtualFileManager.getInstance().addVirtualFileListener(myDirStateChangeListener);
279
280     // ignore temporary files
281     final String ignoredPattern = FileTypeManager.getInstance().getIgnoredFilesList();
282     if (!ignoredPattern.contains(ORIG_FILE_PATTERN)) {
283       final String newPattern = ignoredPattern + (ignoredPattern.endsWith(";") ? "" : ";") + ORIG_FILE_PATTERN;
284       HgUtil.runWriteActionLater(new Runnable() {
285         public void run() {
286           FileTypeManager.getInstance().setIgnoredFilesList(newPattern);
287         }
288       });
289     }
290   }
291
292   @Override
293   public void deactivate() {
294     if (!started) {
295       return;
296     }
297
298     StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
299     if (messageBusConnection != null) {
300       messageBusConnection.disconnect();
301     }
302     if (changesUpdaterScheduledFuture != null) {
303       changesUpdaterScheduledFuture.cancel(true);
304     }
305     if (statusBar != null) {
306       //statusBar.removeCustomIndicationComponent(incomingChangesStatus);
307       //statusBar.removeCustomIndicationComponent(outgoingChangesStatus);
308       //statusBar.removeCustomIndicationComponent(hgCurrentBranchStatus);
309     }
310
311     if (myVFSListener != null) {
312       Disposer.dispose(myVFSListener);
313       myVFSListener = null;
314     }
315
316     VirtualFileManager.getInstance().removeVirtualFileListener(myDirStateChangeListener);
317   }
318
319   public static HgVcs getInstance(Project project) {
320     return (HgVcs) ProjectLevelVcsManager.getInstance(project).findVcsByName(VCS_NAME);
321   }
322
323   private static String ourTestHgExecutablePath; // path to hg in test mode
324
325   /**
326    * Sets the path to hg executable used in the test mode.
327    */
328   public static void setTestHgExecutablePath(String path) {
329     ourTestHgExecutablePath = path;
330   }
331
332   /**
333    * Returns the hg executable file.
334    * If it is a test, returns the special value set in the test setup.
335    * If it is a normal app, returns the value from global settings.
336    */
337   public String getHgExecutable() {
338     if (ApplicationManager.getApplication().isUnitTestMode()) {
339       return (new File(ourTestHgExecutablePath, HG_EXECUTABLE_FILE_NAME)).getPath();
340     }
341     return globalSettings.getHgExecutable();
342   }
343
344   public HgGlobalSettings getGlobalSettings() {
345     return globalSettings;
346   }
347 }