blob: 5802ca1b07742145010f65e50fbcc8b6ba05fe3a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2017 QNX Software Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* QNX Software Systems - initial API and implementation
* Red Hat Inc. - multiple build console support
* Dmitry Kozlov (CodeSourcery) - Build error highlighting and navigation
* Alex Collins (Broadcom Corp.) - Global build console
*******************************************************************************/
package org.eclipse.cdt.internal.ui.buildconsole;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.resources.IConsole;
import org.eclipse.cdt.internal.core.LocalProjectScope;
import org.eclipse.cdt.internal.ui.preferences.BuildConsolePreferencePage;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.IBuildConsoleEvent;
import org.eclipse.cdt.ui.IBuildConsoleListener;
import org.eclipse.cdt.ui.IBuildConsoleManager;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsoleView;
import org.osgi.service.prefs.Preferences;
public class BuildConsoleManager implements IBuildConsoleManager, IResourceChangeListener, IPropertyChangeListener {
/*package*/ static final String PREF_QUALIFIER = CUIPlugin.PLUGIN_ID;
public static final String KEY_KEEP_LOG = "keepLog"; //$NON-NLS-1$
public static final String KEY_LOG_LOCATION = "logLocation"; //$NON-NLS-1$
public static final boolean CONSOLE_KEEP_LOG_DEFAULT = true;
private static final String BUILD_CONSOLE_NODE = "buildConsole"; //$NON-NLS-1$
private static final String PROJECT_LOG_EXT = ".build.log"; //$NON-NLS-1$
private ListenerList<IBuildConsoleListener> listeners = new ListenerList<>();
/** UI console object in which per-project consoles are shown */
private BuildConsole fConsole;
private Map<IProject, BuildConsolePartitioner> fConsoleMap = new HashMap<>();
private Color infoColor;
private Color outputColor;
private Color errorColor;
private Color backgroundColor;
private Color problemHighlightedColor;
private Color problemErrorBackgroundColor;
private Color problemInfoBackgroundColor;
private Color problemWarningBackgroundColor;
public Color getProblemHighlightedColor() {
return problemHighlightedColor;
}
/**
* This function returns background color for errors only now
*/
public Color getProblemBackgroundColor() {
return problemErrorBackgroundColor;
}
public Color getWarningBackgroundColor() {
return problemWarningBackgroundColor;
}
public Color getInfoBackgroundColor() {
return problemInfoBackgroundColor;
}
private BuildConsoleStreamDecorator infoStream;
private BuildConsoleStreamDecorator outputStream;
private BuildConsoleStreamDecorator errorStream;
private String fName;
private String fContextMenuId;
static public final int BUILD_STREAM_TYPE_INFO = 0;
static public final int BUILD_STREAM_TYPE_OUTPUT = 1;
static public final int BUILD_STREAM_TYPE_ERROR = 2;
static public final String DEFAULT_CONTEXT_MENU_ID = CUIPlugin.PLUGIN_ID + ".CDTBuildConsole"; //$NON-NLS-1$
private IProject fLastProject;
/**
* Default constructor.
*/
public BuildConsoleManager() {
}
/**
* Notifies the console manager that console activity has started on the
* project The manager will open the console if the preference is set to
* show the console, and notify listeners
*/
protected void startConsoleActivity(IProject project) {
if (!listeners.isEmpty()) {
for (IBuildConsoleListener listener : listeners) {
ConsoleEvent event = new ConsoleEvent(BuildConsoleManager.this, project,
IBuildConsoleEvent.CONSOLE_START);
listener.consoleChange(event);
}
}
showConsole(true);
}
/**
* Opens the console view. If the view is already open, it is brought to the
* front if requested and the respective user preference is set. The console
* that is shown is the console that was last on top.
*/
protected void showConsole(boolean bringToTop) {
IWorkbenchWindow window = CUIPlugin.getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage page = window.getActivePage();
if (page != null) {
IViewPart consoleView = page.findView(IConsoleConstants.ID_CONSOLE_VIEW);
if (consoleView == null && BuildConsolePreferencePage.isAutoOpenConsole()) {
IWorkbenchPart activePart = page.getActivePart();
try {
consoleView = page.showView(IConsoleConstants.ID_CONSOLE_VIEW);
} catch (PartInitException pie) {
CUIPlugin.log(pie);
}
//restore focus stolen by the creation of the
// console
page.activate(activePart);
}
boolean shouldBringToTop = shouldBringToTop(consoleView);
if (bringToTop && shouldBringToTop) {
page.bringToTop(consoleView);
}
if (consoleView instanceof IConsoleView) {
if (BuildConsole.getCurrentPage() == null)
((IConsoleView) consoleView).display(fConsole);
else if (shouldBringToTop)
((IConsoleView) consoleView).display(BuildConsole.getCurrentPage().getConsole());
}
}
}
}
boolean shouldBringToTop(IViewPart consoleView) {
boolean bringToTop = false;
if (consoleView instanceof IConsoleView) {
IConsoleView cView = (IConsoleView) consoleView;
return !cView.isPinned() && BuildConsolePreferencePage.isConsoleOnTop();
}
return bringToTop;
}
/**
* Traverses the delta looking for added/removed/changed launch
* configuration files.
*
* @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
*/
@Override
public void resourceChanged(IResourceChangeEvent event) {
IResource resource = event.getResource();
if (resource != null && resource.getType() == IResource.PROJECT) {
if (event.getType() == IResourceChangeEvent.PRE_DELETE
|| event.getType() == IResourceChangeEvent.PRE_CLOSE) {
IDocumentPartitioner partioner = fConsoleMap.remove(resource);
if (partioner != null) {
partioner.disconnect();
if (!listeners.isEmpty()) {
for (IBuildConsoleListener listener : listeners) {
ConsoleEvent consoleEvent = new ConsoleEvent(this, (IProject) resource,
IBuildConsoleEvent.CONSOLE_CLOSE);
listener.consoleChange(consoleEvent);
}
}
}
}
}
}
/**
* Release resources allocated on {@link #startup(String, String, URL)}.
*/
public void shutdown() {
if (infoColor != null) {
infoColor.dispose();
outputColor.dispose();
errorColor.dispose();
backgroundColor.dispose();
problemErrorBackgroundColor.dispose();
problemWarningBackgroundColor.dispose();
problemInfoBackgroundColor.dispose();
problemHighlightedColor.dispose();
}
ConsolePlugin.getDefault().getConsoleManager()
.removeConsoles(new org.eclipse.ui.console.IConsole[] { fConsole });
CUIPlugin.getWorkspace().removeResourceChangeListener(this);
CUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
}
private void runUI(Runnable run) {
Display display;
display = Display.getCurrent();
if (display == null) {
display = Display.getDefault();
display.asyncExec(run);
} else {
run.run();
}
}
/**
* Create new build console. Subclasses may override to create a specialized
* console.
*
* @param name - name of console to appear in the list of consoles in context menu
* in the Console view.
* @param contextId - context menu id in the Console view.
* @param iconUrl - a {@link URL} of the icon for the context menu of the Console
* view. The url is expected to point to an image in eclipse OSGi bundle.
* {@code iconUrl} can be <b>null</b>, in that case the default image is used.
* @return newly created build console.
*/
protected BuildConsole createBuildConsole(String name, String contextId, final URL iconUrl) {
return new BuildConsole(this, name, contextId, iconUrl);
}
/**
* Start console activities. This will create a new console in the Console view,
* create streams, color resources, register listeners etc.
* Most work is done in UI thread.
*
* Use {@link #shutdown()} after the console activity ends.
*
* @param name - name of the console to appear in the Console view.
* @param contextId - context menu id in the Console view.
* @param iconUrl - icon to show in the context menu.
*/
public void startup(String name, String contextId, final URL iconUrl) {
// Ensure global console is initialized before any other build console
if (!(this instanceof GlobalBuildConsoleManager))
GlobalBuildConsoleManager.startup();
infoStream = new BuildConsoleStreamDecorator();
outputStream = new BuildConsoleStreamDecorator();
errorStream = new BuildConsoleStreamDecorator();
fName = name;
fContextMenuId = contextId;
runUI(() -> {
// add console to the Console view
fConsole = createBuildConsole(fName, fContextMenuId, iconUrl);
ConsolePlugin.getDefault().getConsoleManager()
.addConsoles(new org.eclipse.ui.console.IConsole[] { fConsole });
infoStream.setConsole(fConsole);
infoColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_INFO_COLOR);
infoStream.setColor(infoColor);
outputStream.setConsole(fConsole);
outputColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_OUTPUT_COLOR);
outputStream.setColor(outputColor);
errorStream.setConsole(fConsole);
errorColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_ERROR_COLOR);
errorStream.setColor(errorColor);
backgroundColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_BACKGROUND_COLOR);
fConsole.setBackground(backgroundColor);
problemHighlightedColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_HIGHLIGHTED_COLOR);
problemErrorBackgroundColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_BACKGROUND_COLOR);
problemWarningBackgroundColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_WARNING_BACKGROUND_COLOR);
problemInfoBackgroundColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_INFO_BACKGROUND_COLOR);
});
CUIPlugin.getWorkspace().addResourceChangeListener(this);
CUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
}
@Override
public void propertyChange(PropertyChangeEvent event) {
String property = event.getProperty();
// colors
if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_INFO_COLOR)) {
Color newColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_INFO_COLOR);
infoStream.setColor(newColor);
infoColor.dispose();
infoColor = newColor;
} else if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_OUTPUT_COLOR)) {
Color newColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_OUTPUT_COLOR);
outputStream.setColor(newColor);
outputColor.dispose();
outputColor = newColor;
} else if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_ERROR_COLOR)) {
Color newColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_ERROR_COLOR);
errorStream.setColor(newColor);
errorColor.dispose();
errorColor = newColor;
} else if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_BACKGROUND_COLOR)) {
Color newColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_BACKGROUND_COLOR);
fConsole.setBackground(newColor);
backgroundColor.dispose();
backgroundColor = newColor;
} else if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_HIGHLIGHTED_COLOR)) {
Color newColor = createColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_HIGHLIGHTED_COLOR);
problemHighlightedColor.dispose();
problemHighlightedColor = newColor;
redrawTextViewer();
} else if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_BACKGROUND_COLOR)) {
Color newColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_BACKGROUND_COLOR);
problemErrorBackgroundColor.dispose();
problemErrorBackgroundColor = newColor;
redrawTextViewer();
} else if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_WARNING_BACKGROUND_COLOR)) {
Color newColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_WARNING_BACKGROUND_COLOR);
problemWarningBackgroundColor.dispose();
problemWarningBackgroundColor = newColor;
redrawTextViewer();
} else if (property.equals(BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_INFO_BACKGROUND_COLOR)) {
Color newColor = createBackgroundColor(CUIPlugin.getStandardDisplay(),
BuildConsolePreferencePage.PREF_BUILDCONSOLE_PROBLEM_INFO_BACKGROUND_COLOR);
problemInfoBackgroundColor.dispose();
problemInfoBackgroundColor = newColor;
redrawTextViewer();
}
}
private void redrawTextViewer() {
final BuildConsolePage p = BuildConsole.getCurrentPage();
if (p == null)
return;
final BuildConsoleViewer v = p.getViewer();
if (v == null)
return;
Display display = Display.getDefault();
display.asyncExec(() -> v.getTextWidget().redraw());
}
public IBuildConsoleStreamDecorator getStreamDecorator(int type) throws CoreException {
switch (type) {
case BUILD_STREAM_TYPE_ERROR:
return errorStream;
case BUILD_STREAM_TYPE_INFO:
return infoStream;
case BUILD_STREAM_TYPE_OUTPUT:
return outputStream;
}
throw new CoreException(new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, -1, "No Such Console", null)); //$NON-NLS-1$
}
/**
* Returns a color instance based on data from a preference field.
*/
private Color createColor(Display display, String preference) {
RGB rgb = PreferenceConverter.getColor(CUIPlugin.getDefault().getPreferenceStore(), preference);
return new Color(display, rgb);
}
/**
* Returns a background color instance based on data from a preference field.
* This is a workaround for black console bug 320723.
*/
private Color createBackgroundColor(Display display, String preference) {
IPreferenceStore preferenceStore = CUIPlugin.getDefault().getPreferenceStore();
RGB rgb;
if (preferenceStore.contains(preference)) {
rgb = PreferenceConverter.getColor(preferenceStore, preference);
} else {
rgb = new RGB(200, 200, 200); // gray background
}
return new Color(display, rgb);
}
/**
* {@inheritDoc}
*/
@Override
public IConsole getConsole(IProject project) {
return new MultiBuildConsoleAdapter(getProjectConsole(project), GlobalBuildConsoleManager.getGlobalConsole());
}
/**
* {@inheritDoc}
*
* @return the console for the specified project. Returns {@code null}
* if project is {@code null} or not accessible.
*/
@Override
public IConsole getProjectConsole(IProject project) {
if (project == null || !project.isAccessible())
return null;
fLastProject = project;
return getProjectConsolePartioner(project).getConsole();
}
@Override
public IProject getLastBuiltProject() {
return fLastProject;
}
/**
* @return the partitioner for the specified projects build console
*/
private BuildConsolePartitioner getProjectConsolePartioner(IProject project) {
BuildConsolePartitioner partitioner = fConsoleMap.get(project);
if (partitioner == null) {
partitioner = new BuildConsolePartitioner(project, this);
fConsoleMap.put(project, partitioner);
}
return partitioner;
}
/**
* @return the document backing the build console for the specified project
*/
@Override
public IDocument getConsoleDocument(IProject project) {
Assert.isNotNull(project);
return getProjectConsolePartioner(project).getDocument();
}
@Override
public void addConsoleListener(IBuildConsoleListener listener) {
listeners.add(listener);
}
@Override
public void removeConsoleListener(IBuildConsoleListener listener) {
listeners.remove(listener);
}
/**
* @return logging preferences for a given project.
* @param project to get logging preferences for, can't be {@code null}.
*/
public Preferences getBuildLogPreferences(IProject project) {
return new LocalProjectScope(project).getNode(PREF_QUALIFIER).node(BUILD_CONSOLE_NODE);
}
/**
* @return default location of logs for a project.
* @param project to get default log location for, can't be {@code null}.
*/
public String getDefaultConsoleLogLocation(IProject project) {
IPath defaultLogLocation = CUIPlugin.getDefault().getStateLocation()
.append(project.getName() + PROJECT_LOG_EXT);
return defaultLogLocation.toOSString();
}
/**
* Where the log for per-project console is kept.
*
* @param project - the project. Cannot be {@code null}.
* @return {@link URI} of build log or {@code null} if not available.
*/
public URI getLogURI(IProject project) {
Assert.isNotNull(project);
if (fContextMenuId != DEFAULT_CONTEXT_MENU_ID)
return null;
URI logURI = null;
Preferences prefs = getBuildLogPreferences(project);
boolean keepLog = prefs.getBoolean(KEY_KEEP_LOG, CONSOLE_KEEP_LOG_DEFAULT);
if (keepLog) {
String strLocation;
strLocation = prefs.get(KEY_LOG_LOCATION, getDefaultConsoleLogLocation(project));
if (strLocation.trim().length() > 0) {
logURI = URIUtil.toURI(strLocation);
}
if (logURI == null) {
IStatus status = new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID,
"Can't determine URI for location=[" + strLocation + "]"); //$NON-NLS-1$ //$NON-NLS-2$
CUIPlugin.log(status);
}
}
return logURI;
}
}