Initial EGit contribution to eclipse.org
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / Activator.java
blob7d3c652cebf0101d22875b9d8645b9b6fd9aadc2
1 /*******************************************************************************
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * which accompanies this distribution, and is available at
8 * http://www.eclipse.org/legal/epl-v10.html
9 *******************************************************************************/
10 package org.eclipse.egit.ui;
12 import java.net.Authenticator;
13 import java.net.ProxySelector;
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.LinkedHashSet;
18 import java.util.List;
19 import java.util.Set;
21 import org.eclipse.core.net.proxy.IProxyService;
22 import org.eclipse.core.resources.IProject;
23 import org.eclipse.core.resources.IResource;
24 import org.eclipse.core.resources.ResourcesPlugin;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Platform;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.core.runtime.SubProgressMonitor;
31 import org.eclipse.core.runtime.jobs.ISchedulingRule;
32 import org.eclipse.core.runtime.jobs.Job;
33 import org.eclipse.egit.core.project.RepositoryMapping;
34 import org.eclipse.jface.util.IPropertyChangeListener;
35 import org.eclipse.jface.util.PropertyChangeEvent;
36 import org.eclipse.jsch.core.IJSchService;
37 import org.eclipse.swt.graphics.Font;
38 import org.eclipse.swt.widgets.Display;
39 import org.eclipse.ui.plugin.AbstractUIPlugin;
40 import org.eclipse.ui.themes.ITheme;
41 import org.osgi.framework.BundleContext;
42 import org.osgi.framework.ServiceReference;
43 import org.eclipse.jgit.lib.IndexChangedEvent;
44 import org.eclipse.jgit.lib.RefsChangedEvent;
45 import org.eclipse.jgit.lib.Repository;
46 import org.eclipse.jgit.lib.RepositoryListener;
47 import org.eclipse.jgit.transport.SshSessionFactory;
49 /**
50 * This is a plugin singleton mostly controlling logging.
52 public class Activator extends AbstractUIPlugin {
54 /**
55 * The one and only instance
57 private static Activator plugin;
59 /**
60 * Property listeners for plugin specific events
62 private static List<IPropertyChangeListener> propertyChangeListeners =
63 new ArrayList<IPropertyChangeListener>(5);
65 /**
66 * Property constant indicating the decorator configuration has changed.
68 public static final String DECORATORS_CHANGED = "org.eclipse.egit.ui.DECORATORS_CHANGED"; //$NON-NLS-1$
70 /**
71 * @return the {@link Activator} singleton.
73 public static Activator getDefault() {
74 return plugin;
77 /**
78 * @return the id of the egit ui plugin
80 public static String getPluginId() {
81 return getDefault().getBundle().getSymbolicName();
84 /**
85 * Returns the standard display to be used. The method first checks, if the
86 * thread calling this method has an associated display. If so, this display
87 * is returned. Otherwise the method returns the default display.
89 * @return the display to use
91 public static Display getStandardDisplay() {
92 Display display = Display.getCurrent();
93 if (display == null) {
94 display = Display.getDefault();
96 return display;
99 /**
100 * Instantiate an error exception.
102 * @param message
103 * description of the error
104 * @param thr
105 * cause of the error or null
106 * @return an initialized {@link CoreException}
108 public static CoreException error(final String message, final Throwable thr) {
109 return new CoreException(new Status(IStatus.ERROR, getPluginId(), 0,
110 message, thr));
114 * Log an error via the Eclipse logging routines.
116 * @param message
117 * @param thr
118 * cause of error
120 public static void logError(final String message, final Throwable thr) {
121 getDefault().getLog().log(
122 new Status(IStatus.ERROR, getPluginId(), 0, message, thr));
126 * @param optionId
127 * name of debug option
128 * @return whether a named debug option is set
130 private static boolean isOptionSet(final String optionId) {
131 final String option = getPluginId() + optionId;
132 final String value = Platform.getDebugOption(option);
133 return value != null && value.equals("true");
137 * Log a debug message
139 * @param what
140 * message to log
142 public static void trace(final String what) {
143 if (getDefault().traceVerbose) {
144 System.out.println("[" + getPluginId() + "] " + what);
149 * Get the theme used by this plugin.
151 * @return our theme.
153 public static ITheme getTheme() {
154 return plugin.getWorkbench().getThemeManager().getCurrentTheme();
158 * Get a font known to this plugin.
160 * @param id
161 * one of our THEME_* font preference ids (see
162 * {@link UIPreferences});
163 * @return the configured font, borrowed from the registry.
165 public static Font getFont(final String id) {
166 return getTheme().getFontRegistry().get(id);
170 * Get a font known to this plugin, but with bold style applied over top.
172 * @param id
173 * one of our THEME_* font preference ids (see
174 * {@link UIPreferences});
175 * @return the configured font, borrowed from the registry.
177 public static Font getBoldFont(final String id) {
178 return getTheme().getFontRegistry().getBold(id);
181 private boolean traceVerbose;
182 private RCS rcs;
183 private RIRefresh refreshJob;
186 * Constructor for the egit ui plugin singleton
188 public Activator() {
189 plugin = this;
192 public void start(final BundleContext context) throws Exception {
193 super.start(context);
194 traceVerbose = isOptionSet("/trace/verbose");
195 setupSSH(context);
196 setupProxy(context);
197 setupRepoChangeScanner();
198 setupRepoIndexRefresh();
201 private void setupRepoIndexRefresh() {
202 refreshJob = new RIRefresh();
203 Repository.addAnyRepositoryChangedListener(refreshJob);
207 * Register for changes made to Team properties.
209 * @param listener
210 * The listener to register
212 public static synchronized void addPropertyChangeListener(
213 IPropertyChangeListener listener) {
214 propertyChangeListeners.add(listener);
218 * Remove a Team property changes.
220 * @param listener
221 * The listener to remove
223 public static synchronized void removePropertyChangeListener(
224 IPropertyChangeListener listener) {
225 propertyChangeListeners.remove(listener);
229 * Broadcast a Team property change.
231 * @param event
232 * The event to broadcast
234 public static synchronized void broadcastPropertyChange(PropertyChangeEvent event) {
235 for (IPropertyChangeListener listener : propertyChangeListeners)
236 listener.propertyChange(event);
239 static class RIRefresh extends Job implements RepositoryListener {
241 RIRefresh() {
242 super("Git index refresh Job");
245 private Set<IProject> projectsToScan = new LinkedHashSet<IProject>();
247 @Override
248 protected IStatus run(IProgressMonitor monitor) {
249 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
250 monitor.beginTask("Refreshing git managed projects", projects.length);
252 while (projectsToScan.size() > 0) {
253 IProject p;
254 synchronized (projectsToScan) {
255 if (projectsToScan.size() == 0)
256 break;
257 Iterator<IProject> i = projectsToScan.iterator();
258 p = i.next();
259 i.remove();
261 ISchedulingRule rule = p.getWorkspace().getRuleFactory().refreshRule(p);
262 try {
263 getJobManager().beginRule(rule, monitor);
264 p.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 1));
265 } catch (CoreException e) {
266 logError("Failed to refresh projects from index changes", e);
267 return new Status(IStatus.ERROR, getPluginId(), e.getMessage());
268 } finally {
269 getJobManager().endRule(rule);
272 monitor.done();
273 return Status.OK_STATUS;
276 public void indexChanged(IndexChangedEvent e) {
277 // Check the workspace setting "refresh automatically" setting first
278 if (!ResourcesPlugin.getPlugin().getPluginPreferences().getBoolean(
279 ResourcesPlugin.PREF_AUTO_REFRESH))
280 return;
282 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
283 Set<IProject> toRefresh= new HashSet<IProject>();
284 for (IProject p : projects) {
285 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
286 if (mapping != null && mapping.getRepository() == e.getRepository()) {
287 toRefresh.add(p);
290 synchronized (projectsToScan) {
291 projectsToScan.addAll(toRefresh);
293 if (projectsToScan.size() > 0)
294 schedule();
297 public void refsChanged(RefsChangedEvent e) {
298 // Do not react here
303 static class RCS extends Job {
304 RCS() {
305 super("Repository Change Scanner");
308 // FIXME, need to be more intelligent about this to avoid too much work
309 private static final long REPO_SCAN_INTERVAL = 10000L;
311 @Override
312 protected IStatus run(IProgressMonitor monitor) {
313 try {
314 // A repository can contain many projects, only scan once
315 // (a project could in theory be distributed among many
316 // repositories. We discard that as being ugly and stupid for
317 // the moment.
318 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
319 monitor.beginTask("Scanning Git repositories for changes", projects.length);
320 Set<Repository> scanned = new HashSet<Repository>();
321 for (IProject p : projects) {
322 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
323 if (mapping != null) {
324 Repository r = mapping.getRepository();
325 if (!scanned.contains(r)) {
326 if (monitor.isCanceled())
327 break;
328 trace("Scanning " + r + " for changes");
329 scanned.add(r);
330 ISchedulingRule rule = p.getWorkspace().getRuleFactory().modifyRule(p);
331 getJobManager().beginRule(rule, monitor);
332 try {
333 r.scanForRepoChanges();
334 } finally {
335 getJobManager().endRule(rule);
339 monitor.worked(1);
341 monitor.done();
342 trace("Rescheduling " + getName() + " job");
343 schedule(REPO_SCAN_INTERVAL);
344 } catch (Exception e) {
345 trace("Stopped rescheduling " + getName() + "job");
346 return new Status(
347 IStatus.ERROR,
348 getPluginId(),
350 "An error occurred while scanning for changes. Scanning aborted",
353 return Status.OK_STATUS;
357 private void setupRepoChangeScanner() {
358 rcs = new RCS();
359 rcs.schedule(RCS.REPO_SCAN_INTERVAL);
362 private void setupSSH(final BundleContext context) {
363 final ServiceReference ssh;
365 ssh = context.getServiceReference(IJSchService.class.getName());
366 if (ssh != null) {
367 SshSessionFactory.setInstance(new EclipseSshSessionFactory(
368 (IJSchService) context.getService(ssh)));
372 private void setupProxy(final BundleContext context) {
373 final ServiceReference proxy;
375 proxy = context.getServiceReference(IProxyService.class.getName());
376 if (proxy != null) {
377 ProxySelector.setDefault(new EclipseProxySelector(
378 (IProxyService) context.getService(proxy)));
379 Authenticator.setDefault(new EclipseAuthenticator(
380 (IProxyService) context.getService(proxy)));
384 public void stop(final BundleContext context) throws Exception {
385 trace("Trying to cancel " + rcs.getName() + " job");
386 rcs.cancel();
387 trace("Trying to cancel " + refreshJob.getName() + " job");
388 refreshJob.cancel();
390 rcs.join();
391 refreshJob.join();
393 trace("Jobs terminated");
394 super.stop(context);
395 plugin = null;