/******************************************************************************* * Copyright (c) 2004 Eric Merritt and others. All rights reserved. This program and the * accompanying materials are made available under the terms of the Eclipse Public License * v1.0 which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: Eric Merritt Vlad Dumitrescu *******************************************************************************/ package org.erlide.ui.internal; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.concurrent.TimeUnit; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.text.templates.ContextTypeRegistry; import org.eclipse.jface.text.templates.persistence.TemplateStore; import org.eclipse.jface.window.Window; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.editors.text.templates.ContributionContextTypeRegistry; import org.eclipse.ui.editors.text.templates.ContributionTemplateStore; import org.eclipse.ui.forms.FormColors; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.wb.swt.SWTResourceManager; import org.erlide.backend.BackendCore; import org.erlide.core.ErlangStatus; import org.erlide.debug.ui.model.ErlangDebuggerBackendListener; import org.erlide.engine.ErlangEngine; import org.erlide.ui.ErlideImage; import org.erlide.ui.ErlideUIConstants; import org.erlide.ui.UIMessageReporter; import org.erlide.ui.console.ErlConsoleManager; import org.erlide.ui.editors.erl.actions.ClearAllCachesAction; import org.erlide.ui.internal.folding.ErlangFoldingStructureProviderRegistry; import org.erlide.ui.perspectives.ErlangPerspective; import org.erlide.ui.templates.ErlangSourceContextTypeModule; import org.erlide.ui.templates.ErlangSourceContextTypeModuleElement; import org.erlide.ui.templates.ErlangTemplateContextType; import org.erlide.ui.templates.ErlideContributionTemplateStore; import org.erlide.ui.util.BackendManagerPopup; import org.erlide.ui.util.IContextMenuConstants; import org.erlide.ui.util.ImageDescriptorRegistry; import org.erlide.ui.util.NoRuntimeHandler; import org.erlide.ui.util.ProblemMarkerManager; import org.erlide.util.ErlLogger; import org.erlide.util.ErlideEventBus; import org.erlide.util.HostnameChecker; import org.erlide.util.SystemConfiguration; import org.osgi.framework.BundleContext; import org.osgi.service.prefs.BackingStoreException; /** * The main plugin class to be used in the desktop. * * * @author Eric Merritt [cyberlync at gmail dot com] */ public class ErlideUIPlugin extends AbstractUIPlugin { public static final String PLUGIN_ID = "org.erlide.ui"; private static volatile ErlideUIPlugin plugin; private ResourceBundle resourceBundle; private ImageDescriptorRegistry fImageDescriptorRegistry; private ErlangFoldingStructureProviderRegistry fFoldingStructureProviderRegistry; private ProblemMarkerManager fProblemMarkerManager = null; private ErlConsoleManager erlConsoleManager; private static final String CUSTOM_TEMPLATES_KEY = "org.erlide.ui.editor.customtemplates"; //$NON-NLS-1$ public ErlideUIPlugin() { super(); plugin = this; try { resourceBundle = ResourceBundle .getBundle("org.erlide.ui.ErlideUIPluginResources"); } catch (final MissingResourceException x) { x.printStackTrace(); resourceBundle = null; } } @Override public void start(final BundleContext context) throws Exception { ErlLogger.info("Starting UI " + Thread.currentThread()); super.start(context); final String workspace = ResourcesPlugin.getWorkspace().getRoot().getLocation() .toPortableString(); if (!ErlangEngine.getInstance().isAvailable()) { notifyNoRuntimeAndRestart(workspace); } else if (HostnameChecker.getInstance().getErlangHostName(true) == null && HostnameChecker.getInstance().getErlangHostName(false) == null) { notifyBadHostname(workspace); } ErlideEventBus.register(new NoRuntimeHandler()); ErlideEventBus.register(new UIMessageReporter()); if (SystemConfiguration.getInstance().isDeveloper()) { BackendManagerPopup.init(); } ErlLogger.info("Started UI"); erlConsoleManager = new ErlConsoleManager(); ConsolePlugin.getDefault().getConsoleManager() .addConsoleListener(erlConsoleManager); erlangDebuggerBackendListener = new ErlangDebuggerBackendListener(); BackendCore.getBackendManager().addBackendListener(erlangDebuggerBackendListener); startPeriodicCacheCleaner(); } @Override public void stop(final BundleContext context) throws Exception { erlConsoleManager.dispose(); super.stop(context); BackendCore.getBackendManager() .removeBackendListener(erlangDebuggerBackendListener); BackendCore.getBackendManager().dispose(); ErlideImage.dispose(); SWTResourceManager.dispose(); plugin = null; } private void notifyBadHostname(final String workspace) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { final Shell activeShell = PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getShell(); final String message = "We are sorry, but your machine's host name is not configured properly " + "and erlide can't work. You need to fix your .hosts file and restart.\n\n"; final String description = "Java and Erlang can't agree on hostnames. Please check the log in " + workspace + "/erlide.log for details on which names were tried.\n\n" + "Hostnames with dots in them can't be used as short names.\n" + "Hostnames with dashes in them might not always work.\n\n" + "Try to conect two Erlang nodes manually first. Add the working hostname to .hosts."; ErrorDialog.openError(activeShell, "Erlide can't work properly", message, new Status(IStatus.ERROR, PLUGIN_ID, description)); } }); } private void notifyNoRuntimeAndRestart(final String workspace) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { final Shell activeShell = PlatformUI.getWorkbench() .getActiveWorkbenchWindow().getShell(); final String message = "We are sorry, but the configured Erlang runtime could not be started " + "and erlide can't work. Please check and fix the configuration."; final String description = "The log in " + workspace + "/erlide.log may contain more information."; ErrorDialog.openError(activeShell, "Erlide can't work properly", message, new Status(IStatus.ERROR, PLUGIN_ID, description)); final PreferenceDialog pref = PreferencesUtil.createPreferenceDialogOn( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "org.erlide.ui.preferences.runtimes", null, null); if (pref != null) { if (pref.open() == Window.OK) { ErlLogger.info( "Restarting workbench after initial runtime configuration..."); PlatformUI.getWorkbench().restart(); } } } }); } public ErlConsoleManager getErlConsoleManager() { return erlConsoleManager; } private void startPeriodicCacheCleaner() { final Job cacheCleanerJob = new Job("erlide periodic cache cleaner") { @Override protected IStatus run(final IProgressMonitor monitor) { ErlLogger.info("*** Automatically cleaning caches ***"); try { ClearAllCachesAction.clearAllCaches(); } finally { schedule(TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS)); } return Status.OK_STATUS; } }; cacheCleanerJob.setPriority(Job.DECORATE); cacheCleanerJob.setSystem(true); cacheCleanerJob.schedule(getTimeToMidnight()); } private long getTimeToMidnight() { final Calendar date = new GregorianCalendar(); date.set(Calendar.HOUR_OF_DAY, 0); date.set(Calendar.MINUTE, 0); date.set(Calendar.SECOND, 0); date.set(Calendar.MILLISECOND, 0); date.add(Calendar.DAY_OF_MONTH, 1); final long restOfDayInMilliseconds = date.getTimeInMillis() - System.currentTimeMillis(); return restOfDayInMilliseconds; } public static ErlideUIPlugin getDefault() { if (plugin == null) { plugin = new ErlideUIPlugin(); } return plugin; } /** * Returns the string from the plugin's resource bundle, or 'key' if not found. * * @param key * The resource * @return The identified string */ public static String getResourceString(final String key) { final ResourceBundle bundle = ErlideUIPlugin.getDefault().getResourceBundle(); try { final String returnString = bundle != null ? bundle.getString(key) : key; return returnString; } catch (final MissingResourceException e) { return key; } } public ResourceBundle getResourceBundle() { return resourceBundle; } /** * Returns the standard display to be used. The method first checks, if the thread * calling this method has an associated display. If so, this display is returned. * Otherwise the method returns the default display. * * @return the standard display */ public static Display getStandardDisplay() { Display display = Display.getCurrent(); if (display == null) { display = Display.getDefault(); } return display; } /** * Creates an image and places it in the image registry. * * @param id * The image id * @param baseURL * The descriptor url */ protected void createImageDescriptor(final String id, final URL baseURL) { URL url = null; try { url = new URL(baseURL, ErlideUIConstants.ICON_PATH + id); } catch (final MalformedURLException e) { // ignore exception } getImageRegistry().put(id, ImageDescriptor.createFromURL(url)); } /** * Returns the image descriptor for the given image PLUGIN_ID. Returns null if there * is no such image. * * @param id * The image id * * @return The image descriptor */ public ImageDescriptor getImageDescriptor(final String id) { final ImageDescriptor returnImageDescriptor = getImageRegistry() .getDescriptor(id); return returnImageDescriptor; } /** * Returns the image for the given image PLUGIN_ID. Returns null if there is no such * image. * * @param id * The image id * * @return The image */ public Image getImage(final String id) { final Image returnImage = getImageRegistry().get(id); return returnImage; } /** * @see org.eclipse.ui.plugin.AbstractUIPlugin#initializeImageRegistry(org.eclipse.jface.resource.ImageRegistry) */ @Override protected void initializeImageRegistry(final ImageRegistry reg) { super.initializeImageRegistry(reg); final URL baseURL = getBundle().getEntry("/"); createImageDescriptor(ErlideUIConstants.IMG_CONSOLE, baseURL); createImageDescriptor(ErlideUIConstants.IMG_NEW_PROJECT_WIZARD, baseURL); createImageDescriptor(ErlideUIConstants.IMG_PROJECT_LABEL, baseURL); createImageDescriptor(ErlideUIConstants.IMG_PACKAGE_FOLDER_LABEL, baseURL); createImageDescriptor(ErlideUIConstants.IMG_PACKAGE_LABEL, baseURL); createImageDescriptor(ErlideUIConstants.IMG_FILE_LABEL, baseURL); createImageDescriptor(ErlideUIConstants.IMG_FOLDER_LABEL, baseURL); createImageDescriptor(ErlideUIConstants.IMG_DISABLED_REFRESH, baseURL); createImageDescriptor(ErlideUIConstants.IMG_REFRESH, baseURL); createImageDescriptor(ErlideUIConstants.IMG_DISABLED_IMPORT, baseURL); createImageDescriptor(ErlideUIConstants.IMG_IMPORT, baseURL); createImageDescriptor(ErlideUIConstants.IMG_DISABLED_EXPORT, baseURL); createImageDescriptor(ErlideUIConstants.IMG_EXPORT, baseURL); createImageDescriptor(ErlideUIConstants.IMG_COLLAPSEALL, baseURL); createImageDescriptor(ErlideUIConstants.IMG_PROJECT_CLOSED_LABEL, baseURL); createImageDescriptor(ErlideUIConstants.IMG_ERLANG_LOGO, baseURL); } public static IWorkbenchPage getActivePage() { final IWorkbenchWindow w = getActiveWorkbenchWindow(); if (w != null) { return w.getActivePage(); } return null; } public static IWorkbenchWindow getActiveWorkbenchWindow() { return getDefault().getWorkbench().getActiveWorkbenchWindow(); } public static Shell getActiveWorkbenchShell() { final IWorkbenchWindow window = getActiveWorkbenchWindow(); if (window != null) { return window.getShell(); } return null; } public static void log(final Exception e) { log(new Status(IStatus.ERROR, PLUGIN_ID, ErlangStatus.INTERNAL_ERROR.getValue(), e.getMessage(), null)); } public static void log(final IStatus status) { getDefault().getLog().log(status); } public static void logErrorMessage(final String message) { log(new Status(IStatus.ERROR, PLUGIN_ID, ErlangStatus.INTERNAL_ERROR.getValue(), message, null)); } public static void logErrorStatus(final String message, final IStatus status) { if (status == null) { logErrorMessage(message); return; } final MultiStatus multi = new MultiStatus(PLUGIN_ID, ErlangStatus.INTERNAL_ERROR.getValue(), message, null); multi.add(status); log(multi); } public static void log(final Throwable e) { log(new Status(IStatus.ERROR, PLUGIN_ID, ErlangStatus.INTERNAL_ERROR.getValue(), "Erlide internal error", e)); } public static ImageDescriptorRegistry getImageDescriptorRegistry() { return getDefault().internalGetImageDescriptorRegistry(); } private synchronized ImageDescriptorRegistry internalGetImageDescriptorRegistry() { if (fImageDescriptorRegistry == null) { fImageDescriptorRegistry = new ImageDescriptorRegistry(); } return fImageDescriptorRegistry; } public synchronized ErlangFoldingStructureProviderRegistry getFoldingStructureProviderRegistry() { if (fFoldingStructureProviderRegistry == null) { fFoldingStructureProviderRegistry = new ErlangFoldingStructureProviderRegistry(); } return fFoldingStructureProviderRegistry; } public static void createStandardGroups(final IMenuManager menu) { if (!menu.isEmpty()) { return; } menu.add(new Separator(IContextMenuConstants.GROUP_OPEN)); menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); menu.add(new Separator(IContextMenuConstants.GROUP_SEARCH)); menu.add(new Separator(IContextMenuConstants.GROUP_ADDITIONS)); menu.add(new Separator(IContextMenuConstants.GROUP_PROPERTIES)); } /** * Returns a section in the Erlang plugin's dialog settings. If the section doesn't * exist yet, it is created. * * @param name * the name of the section * @return the section of the given name * @since 3.2 */ public IDialogSettings getDialogSettingsSection(final String name) { final IDialogSettings dialogSettings = getDialogSettings(); IDialogSettings section = dialogSettings.getSection(name); if (section == null) { section = dialogSettings.addNewSection(name); } return section; } public ProblemMarkerManager getProblemMarkerManager() { if (fProblemMarkerManager == null) { fProblemMarkerManager = new ProblemMarkerManager(); } return fProblemMarkerManager; } public static IEclipsePreferences getPrefsNode() { final String qualifier = ErlideUIPlugin.PLUGIN_ID; final IScopeContext context = InstanceScope.INSTANCE; final IEclipsePreferences eclipsePreferences = context.getNode(qualifier); return eclipsePreferences; } private ContributionContextTypeRegistry fContextTypeRegistry; private ContributionTemplateStore fStore; private ErlangDebuggerBackendListener erlangDebuggerBackendListener; public TemplateStore getTemplateStore() { // this is to avoid recursive call when fContextTypeRegistry is null getContextTypeRegistry(); if (fStore == null) { fStore = new ErlideContributionTemplateStore(getContextTypeRegistry(), getPreferenceStore(), CUSTOM_TEMPLATES_KEY); try { fStore.load(); } catch (final IOException e) { getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, "", e)); //$NON-NLS-1$ } ErlangSourceContextTypeModule.getDefault().addElementResolvers(); } return fStore; } public ContextTypeRegistry getContextTypeRegistry() { if (fContextTypeRegistry == null) { // create an configure the contexts available in the template editor fContextTypeRegistry = new ContributionContextTypeRegistry(); fContextTypeRegistry .addContextType(ErlangTemplateContextType.ERLANG_CONTEXT_TYPE_ID); fContextTypeRegistry.addContextType( ErlangSourceContextTypeModule.ERLANG_SOURCE_CONTEXT_TYPE_MODULE_ID); fContextTypeRegistry.addContextType( ErlangSourceContextTypeModuleElement.ERLANG_SOURCE_CONTEXT_TYPE_MODULE_ELEMENT_ID); } return fContextTypeRegistry; } public static void errorDialog(final Shell shell, final String title, final String message0, final Throwable t) { IStatus status; String message = message0; if (t instanceof CoreException) { status = ((CoreException) t).getStatus(); // if the 'message' resource string and the IStatus' message are the // same, // don't show both in the dialog if (status != null && message.equals(status.getMessage())) { message = null; } } else { status = new Status(IStatus.ERROR, PLUGIN_ID, IDebugUIConstants.INTERNAL_ERROR, "Error within Debug UI: ", t); //$NON-NLS-1$ log(status); } ErrorDialog.openError(shell, title, message, status); } public void showErlangPerspective() { try { getWorkbench().showPerspective(ErlangPerspective.ID, getWorkbench().getActiveWorkbenchWindow()); } catch (final WorkbenchException we) { // ignore } } private FormToolkit fDialogsFormToolkit; public FormToolkit getDialogsFormToolkit() { if (fDialogsFormToolkit == null) { final FormColors colors = new FormColors(Display.getCurrent()); colors.setBackground(null); colors.setForeground(null); fDialogsFormToolkit = new FormToolkit(colors); } return fDialogsFormToolkit; } public static void flushInstanceScope() { try { InstanceScope.INSTANCE.getNode(PLUGIN_ID).flush(); } catch (final BackingStoreException e) { ErlLogger.warn(e); } } }