EGit operation / action refactoring
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / Activator.java
blob19d4dcf641106a3a1756464cf8948cd8d7e6b275
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.Status;
29 import org.eclipse.core.runtime.SubProgressMonitor;
30 import org.eclipse.core.runtime.jobs.ISchedulingRule;
31 import org.eclipse.core.runtime.jobs.Job;
32 import org.eclipse.egit.core.project.RepositoryMapping;
33 import org.eclipse.egit.ui.internal.trace.GitTraceLocation;
34 import org.eclipse.jface.util.IPropertyChangeListener;
35 import org.eclipse.jface.util.PropertyChangeEvent;
36 import org.eclipse.jgit.lib.IndexChangedEvent;
37 import org.eclipse.jgit.lib.RefsChangedEvent;
38 import org.eclipse.jgit.lib.Repository;
39 import org.eclipse.jgit.lib.RepositoryListener;
40 import org.eclipse.jgit.transport.SshSessionFactory;
41 import org.eclipse.jsch.core.IJSchService;
42 import org.eclipse.osgi.service.debug.DebugOptions;
43 import org.eclipse.swt.graphics.Font;
44 import org.eclipse.ui.plugin.AbstractUIPlugin;
45 import org.eclipse.ui.statushandlers.StatusManager;
46 import org.eclipse.ui.themes.ITheme;
47 import org.osgi.framework.BundleContext;
48 import org.osgi.framework.ServiceReference;
49 import org.osgi.util.tracker.ServiceTracker;
51 /**
52 * This is a plugin singleton mostly controlling logging.
54 public class Activator extends AbstractUIPlugin {
56 /**
57 * The one and only instance
59 private static Activator plugin;
61 /**
62 * Property listeners for plugin specific events
64 private static List<IPropertyChangeListener> propertyChangeListeners =
65 new ArrayList<IPropertyChangeListener>(5);
67 /**
68 * Property constant indicating the decorator configuration has changed.
70 public static final String DECORATORS_CHANGED = "org.eclipse.egit.ui.DECORATORS_CHANGED"; //$NON-NLS-1$
72 /**
73 * @return the {@link Activator} singleton.
75 public static Activator getDefault() {
76 return plugin;
79 /**
80 * @return the id of the egit ui plugin
82 public static String getPluginId() {
83 return getDefault().getBundle().getSymbolicName();
86 /**
87 * Handle an error. The error is logged. If <code>show</code> is
88 * <code>true</code> the error is shown to the user.
90 * @param message a localized message
91 * @param throwable
92 * @param show
94 public static void handleError(String message, Throwable throwable,
95 boolean show) {
96 IStatus status = new Status(IStatus.ERROR, getPluginId(), message,
97 throwable);
98 int style = StatusManager.LOG;
99 if (show)
100 style |= StatusManager.SHOW;
101 StatusManager.getManager().handle(status, style);
105 * Shows an error. The error is NOT logged.
107 * @param message
108 * a localized message
109 * @param throwable
111 public static void showError(String message, Throwable throwable) {
112 IStatus status = new Status(IStatus.ERROR, getPluginId(), message,
113 throwable);
114 StatusManager.getManager().handle(status, StatusManager.SHOW);
118 * Get the theme used by this plugin.
120 * @return our theme.
122 public static ITheme getTheme() {
123 return plugin.getWorkbench().getThemeManager().getCurrentTheme();
127 * Get a font known to this plugin.
129 * @param id
130 * one of our THEME_* font preference ids (see
131 * {@link UIPreferences});
132 * @return the configured font, borrowed from the registry.
134 public static Font getFont(final String id) {
135 return getTheme().getFontRegistry().get(id);
139 * Get a font known to this plugin, but with bold style applied over top.
141 * @param id
142 * one of our THEME_* font preference ids (see
143 * {@link UIPreferences});
144 * @return the configured font, borrowed from the registry.
146 public static Font getBoldFont(final String id) {
147 return getTheme().getFontRegistry().getBold(id);
150 private RCS rcs;
151 private RIRefresh refreshJob;
154 * Constructor for the egit ui plugin singleton
156 public Activator() {
157 plugin = this;
160 public void start(final BundleContext context) throws Exception {
161 super.start(context);
163 if (isDebugging()) {
164 ServiceTracker debugTracker = new ServiceTracker(context,
165 DebugOptions.class.getName(), null);
166 debugTracker.open();
168 DebugOptions opts = (DebugOptions) debugTracker.getService();
169 GitTraceLocation.initializeFromOptions(opts, true);
172 setupSSH(context);
173 setupProxy(context);
174 setupRepoChangeScanner();
175 setupRepoIndexRefresh();
178 private void setupRepoIndexRefresh() {
179 refreshJob = new RIRefresh();
180 Repository.addAnyRepositoryChangedListener(refreshJob);
184 * Register for changes made to Team properties.
186 * @param listener
187 * The listener to register
189 public static synchronized void addPropertyChangeListener(
190 IPropertyChangeListener listener) {
191 propertyChangeListeners.add(listener);
195 * Remove a Team property changes.
197 * @param listener
198 * The listener to remove
200 public static synchronized void removePropertyChangeListener(
201 IPropertyChangeListener listener) {
202 propertyChangeListeners.remove(listener);
206 * Broadcast a Team property change.
208 * @param event
209 * The event to broadcast
211 public static synchronized void broadcastPropertyChange(PropertyChangeEvent event) {
212 for (IPropertyChangeListener listener : propertyChangeListeners)
213 listener.propertyChange(event);
216 static class RIRefresh extends Job implements RepositoryListener {
218 RIRefresh() {
219 super(UIText.Activator_refreshJobName);
222 private Set<IProject> projectsToScan = new LinkedHashSet<IProject>();
224 @Override
225 protected IStatus run(IProgressMonitor monitor) {
226 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
227 monitor.beginTask(UIText.Activator_refreshingProjects, projects.length);
229 while (projectsToScan.size() > 0) {
230 IProject p;
231 synchronized (projectsToScan) {
232 if (projectsToScan.size() == 0)
233 break;
234 Iterator<IProject> i = projectsToScan.iterator();
235 p = i.next();
236 i.remove();
238 ISchedulingRule rule = p.getWorkspace().getRuleFactory().refreshRule(p);
239 try {
240 getJobManager().beginRule(rule, monitor);
241 p.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 1));
242 } catch (CoreException e) {
243 handleError(UIText.Activator_refreshFailed, e, false);
244 return new Status(IStatus.ERROR, getPluginId(), e.getMessage());
245 } finally {
246 getJobManager().endRule(rule);
249 monitor.done();
250 return Status.OK_STATUS;
253 public void indexChanged(IndexChangedEvent e) {
254 // Check the workspace setting "refresh automatically" setting first
255 if (!ResourcesPlugin.getPlugin().getPluginPreferences().getBoolean(
256 ResourcesPlugin.PREF_AUTO_REFRESH))
257 return;
259 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
260 Set<IProject> toRefresh= new HashSet<IProject>();
261 for (IProject p : projects) {
262 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
263 if (mapping != null && mapping.getRepository() == e.getRepository()) {
264 toRefresh.add(p);
267 synchronized (projectsToScan) {
268 projectsToScan.addAll(toRefresh);
270 if (projectsToScan.size() > 0)
271 schedule();
274 public void refsChanged(RefsChangedEvent e) {
275 // Do not react here
280 static class RCS extends Job {
281 RCS() {
282 super(UIText.Activator_repoScanJobName);
285 // FIXME, need to be more intelligent about this to avoid too much work
286 private static final long REPO_SCAN_INTERVAL = 10000L;
287 // volatile in order to ensure thread synchronization
288 private volatile boolean doReschedule = true;
290 void setReschedule(boolean reschedule){
291 doReschedule = reschedule;
294 @Override
295 protected IStatus run(IProgressMonitor monitor) {
296 try {
297 // A repository can contain many projects, only scan once
298 // (a project could in theory be distributed among many
299 // repositories. We discard that as being ugly and stupid for
300 // the moment.
301 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
302 monitor.beginTask(UIText.Activator_scanningRepositories, projects.length);
303 Set<Repository> scanned = new HashSet<Repository>();
304 for (IProject p : projects) {
305 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
306 if (mapping != null) {
307 Repository r = mapping.getRepository();
308 if (!scanned.contains(r)) {
309 if (monitor.isCanceled())
310 break;
311 // TODO is this the right location?
312 if (GitTraceLocation.UI.isActive())
313 GitTraceLocation.getTrace().trace(
314 GitTraceLocation.UI.getLocation(),
315 "Scanning " + r + " for changes"); //$NON-NLS-1$ //$NON-NLS-2$
316 scanned.add(r);
317 ISchedulingRule rule = p.getWorkspace().getRuleFactory().modifyRule(p);
318 getJobManager().beginRule(rule, monitor);
319 try {
320 r.scanForRepoChanges();
321 } finally {
322 getJobManager().endRule(rule);
326 monitor.worked(1);
328 monitor.done();
329 // TODO is this the right location?
330 if (GitTraceLocation.UI.isActive())
331 GitTraceLocation.getTrace().trace(
332 GitTraceLocation.UI.getLocation(),
333 "Rescheduling " + getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
334 if (doReschedule)
335 schedule(REPO_SCAN_INTERVAL);
336 } catch (Exception e) {
337 // TODO is this the right location?
338 if (GitTraceLocation.UI.isActive())
339 GitTraceLocation.getTrace().trace(
340 GitTraceLocation.UI.getLocation(),
341 "Stopped rescheduling " + getName() + "job"); //$NON-NLS-1$ //$NON-NLS-2$
342 return new Status(
343 IStatus.ERROR,
344 getPluginId(),
346 UIText.Activator_scanError,
349 return Status.OK_STATUS;
353 private void setupRepoChangeScanner() {
354 rcs = new RCS();
355 rcs.setSystem(true);
356 rcs.schedule(RCS.REPO_SCAN_INTERVAL);
359 private void setupSSH(final BundleContext context) {
360 final ServiceReference ssh;
362 ssh = context.getServiceReference(IJSchService.class.getName());
363 if (ssh != null) {
364 SshSessionFactory.setInstance(new EclipseSshSessionFactory(
365 (IJSchService) context.getService(ssh)));
369 private void setupProxy(final BundleContext context) {
370 final ServiceReference proxy;
372 proxy = context.getServiceReference(IProxyService.class.getName());
373 if (proxy != null) {
374 ProxySelector.setDefault(new EclipseProxySelector(
375 (IProxyService) context.getService(proxy)));
376 Authenticator.setDefault(new EclipseAuthenticator(
377 (IProxyService) context.getService(proxy)));
381 public void stop(final BundleContext context) throws Exception {
383 if (GitTraceLocation.UI.isActive())
384 GitTraceLocation.getTrace().trace(
385 GitTraceLocation.UI.getLocation(),
386 "Trying to cancel " + rcs.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
388 rcs.setReschedule(false);
390 rcs.cancel();
391 if (GitTraceLocation.UI.isActive())
392 GitTraceLocation.getTrace().trace(
393 GitTraceLocation.UI.getLocation(),
394 "Trying to cancel " + refreshJob.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
395 refreshJob.cancel();
397 rcs.join();
398 refreshJob.join();
400 if (GitTraceLocation.UI.isActive())
401 GitTraceLocation.getTrace().trace(
402 GitTraceLocation.UI.getLocation(), "Jobs terminated"); //$NON-NLS-1$
404 super.stop(context);
405 plugin = null;
409 * @param message
410 * @param e
412 public static void logError(String message, Throwable e) {
413 handleError(message, e, false);
417 * @param message
418 * @param e
420 public static void error(String message, Throwable e) {
421 handleError(message, e, false);
425 * Creates an error status
427 * @param message
428 * a localized message
429 * @param throwable
430 * @return a new Status object
432 public static IStatus createErrorStatus(String message, Throwable throwable) {
433 return new Status(IStatus.ERROR, getPluginId(), message, throwable);