blob: 07e45d4c7c3bca15ab2fab82ee396c37d7ff97fa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Christian Pontesegger and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.ui.view;
import java.util.concurrent.ExecutionException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.ease.IExecutionListener;
import org.eclipse.ease.IReplEngine;
import org.eclipse.ease.IScriptEngine;
import org.eclipse.ease.Script;
import org.eclipse.ease.ScriptObjectType;
import org.eclipse.ease.ScriptResult;
import org.eclipse.ease.ui.Activator;
import org.eclipse.jface.resource.ColorDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GlyphMetrics;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.progress.UIJob;
public class ScriptHistoryText extends StyledText implements IExecutionListener {
public static final int STYLE_ERROR = 1;
public static final int STYLE_RESULT = 3;
public static final int STYLE_COMMAND = 4;
private static Image getImage(ScriptObjectType type) {
switch (type) {
case JAVA_OBJECT:
return Activator.getImage(Activator.PLUGIN_ID, "/icons/eobj16/debug_java_class.png", true);
case NATIVE_ARRAY:
return Activator.getImage(Activator.PLUGIN_ID, "/icons/eobj16/debug_local_array.png", true);
case NATIVE_OBJECT:
return Activator.getImage(Activator.PLUGIN_ID, "/icons/eobj16/debug_local_object.png", true);
case VOID:
return Activator.getImage(Activator.PLUGIN_ID, "/icons/eobj16/void_type.png", true);
default:
return Activator.getImage(Activator.PLUGIN_ID, "/icons/eobj16/debug_local_variable.png", true);
}
}
private class BlendBackgroundJob extends UIJob {
private boolean fRun;
private volatile boolean fStarted;
public BlendBackgroundJob() {
super("Darken Script Shell background");
setSystem(true);
}
public void arm() {
fRun = true;
fStarted = false;
schedule(300);
}
public void disarm() {
fRun = false;
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
fStarted = true;
if (fRun)
setBackground(fResourceManager.createColor(fColorShadedBackground));
return Status.OK_STATUS;
}
}
private BlendBackgroundJob fBlendBackgroundJob = null;
private final LocalResourceManager fResourceManager = new LocalResourceManager(JFaceResources.getResources(), getParent());
private final ColorDescriptor fColorDescriptorResult = ColorDescriptor.createFrom(getShell().getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
private final ColorDescriptor fColorDescriptorCommand = ColorDescriptor.createFrom(getShell().getDisplay().getSystemColor(SWT.COLOR_BLUE));
private final ColorDescriptor fColorDescriptorError = ColorDescriptor.createFrom(getShell().getDisplay().getSystemColor(SWT.COLOR_RED));
private ColorDescriptor fColorDefaultBackground = null;
private ColorDescriptor fColorShadedBackground = null;
private IReplEngine fCurrentEngine;
public ScriptHistoryText(final Composite parent, final int style) {
super(parent, style);
initialize();
}
public void addScriptEngine(final IReplEngine engine) {
if (engine != null) {
engine.addExecutionListener(this);
fCurrentEngine = engine;
}
}
public void removeScriptEngine(final IReplEngine engine) {
if (engine != null) {
engine.removeExecutionListener(this);
Display.getDefault().asyncExec(() -> setBackground(fResourceManager.createColor(fColorDefaultBackground)));
}
}
private ColorDescriptor createShadedBackground(Color defaultColor) {
final int defaultRgb = defaultColor.getRed() + defaultColor.getGreen() + defaultColor.getBlue();
if (defaultRgb >= 0x20)
return ColorDescriptor.createFrom(new RGB(Math.max(defaultColor.getRed() - 0x10, 0), Math.max(defaultColor.getGreen() - 0x10, 0),
Math.max(defaultColor.getBlue() - 0x10, 0)));
// original color is too dark, use a lighter color for shading
return ColorDescriptor.createFrom(new RGB(Math.min(defaultColor.getRed() + 0x10, 0xFF), Math.min(defaultColor.getGreen() + 0x10, 0xFF),
Math.min(defaultColor.getBlue() + 0x10, 0xFF)));
}
private void initialize() {
// set monospaced font
setFont(JFaceResources.getFont(IDebugUIConstants.PREF_CONSOLE_FONT));
fColorDefaultBackground = ColorDescriptor.createFrom(DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_BAKGROUND_COLOR));
fColorShadedBackground = createShadedBackground(DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_BAKGROUND_COLOR));
JFaceResources.getFontRegistry().addListener(event -> {
if (IDebugUIConstants.PREF_CONSOLE_FONT.equals(event.getProperty()))
setFont(JFaceResources.getFont(IDebugUIConstants.PREF_CONSOLE_FONT));
});
setEditable(false);
fBlendBackgroundJob = new BlendBackgroundJob();
// add support for response type images
addPaintObjectListener(event -> {
final StyleRange style = event.style;
if (style.data instanceof Image) {
final int lineHeight = event.ascent + event.descent;
final int yOffset = Math.max(0, (lineHeight - ((Image) style.data).getBounds().height) / 2);
final int x = event.x - 5;
final int y = event.y + yOffset + 2; // not sure why we need +2 here. Icon is not centered on linux without
event.gc.drawImage((Image) style.data, x, y);
}
});
}
@Override
public void dispose() {
fResourceManager.dispose();
if (fCurrentEngine != null) {
fCurrentEngine.removeExecutionListener(this);
fCurrentEngine = null;
}
super.dispose();
}
public void clear() {
setText("");
setStyleRanges(new StyleRange[0]);
}
@Override
public void notify(final IScriptEngine engine, final Script script, final int status) {
try {
switch (status) {
case SCRIPT_START:
fBlendBackgroundJob.arm();
printCommand(script.getCode());
break;
case SCRIPT_END:
fBlendBackgroundJob.disarm();
if (fBlendBackgroundJob.fStarted) {
// we need to reset the background color
Display.getDefault().asyncExec(() -> setBackground(fResourceManager.createColor(fColorDefaultBackground)));
}
printResult(script.getResult());
break;
default:
// do nothing
break;
}
} catch (final Exception e) {
// script.getCode() failed, ignore
}
}
/**
* Print the script command.
*
* @param message
* message to print
*/
public void printCommand(final String message) {
if (message != null) {
// print to output pane
Display.getDefault().asyncExec(() -> {
if (!isDisposed()) {
if (!getText().isEmpty())
append("\n");
// create new style range
final StyleRange styleRange = getStyle(STYLE_COMMAND, getText().length(), message.trim().length());
append(message.trim());
setStyleRange(styleRange);
// scroll to end of window
scrollToEnd();
}
});
}
}
/**
* Print the script result.
*
* @param result
* script execution result
*/
public void printResult(ScriptResult result) {
final String message = getResultMessage(result);
final Image image = getResultImage(result);
// // print to output pane
Display.getDefault().asyncExec(() -> {
// indent message
final String out = message.replaceAll("\\r?\\n", "\n\t");
if (!isDisposed()) {
// append image
if (image != null) {
append("\n "); // dummy character to be replaced by image
final StyleRange styleRange = new StyleRange();
styleRange.start = getText().length() - 1;
styleRange.length = 1;
styleRange.data = image;
final Rectangle rect = image.getBounds();
styleRange.metrics = new GlyphMetrics(rect.height, 0, rect.width);
setStyleRange(styleRange);
} else
append("\n\t");
// append message
append(out);
try {
result.get();
setStyleRange(getStyle(STYLE_RESULT, getText().length() - out.length(), out.length()));
} catch (final ExecutionException e) {
setStyleRange(getStyle(STYLE_ERROR, getText().length() - out.length(), out.length()));
}
// scroll to end of window
scrollToEnd();
}
});
}
/**
* Vertically scroll to the end of the history text.
*/
private void scrollToEnd() {
setHorizontalPixel(0);
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=538973
// seems the StyledText widget has a strange bug when trying to scroll correctly. We therefore add a factor to the calculated pixel offset to make sure
// we correctly scroll to the end.
setTopPixel((int) (getLineHeight() * getLineCount() * 1.1));
}
/**
* Get a text representation for the script execution result.
*
* @param result
* script result
* @return text string
*/
private String getResultMessage(ScriptResult result) {
try {
final Object executionResult = result.get();
if (fCurrentEngine != null)
return fCurrentEngine.toString(executionResult);
else
return (executionResult != null) ? executionResult.toString() : "null";
} catch (final ExecutionException e) {
final String message = e.getLocalizedMessage();
return (message != null) ? message : e.getClass().getName();
}
}
/**
* Get an image representation for the script execution result.
*
* @param result
* script result
* @return image or <code>null</code>
*/
private Image getResultImage(ScriptResult result) {
try {
result.get(); // fetch result to provoke exception check
if (fCurrentEngine != null) {
final ScriptObjectType type = fCurrentEngine.getType(result.getResult());
return getImage(type);
}
} catch (final ExecutionException e) {
return Activator.getImage(Activator.PLUGIN_ID, "/icons/eobj16/script_exception.png", true);
}
return null;
}
/**
* Create a new style range for a given offset/length.
*
* @param style
* style to use (see JavaScriptShell.STYLE_* constants)
* @param start
* start of text to be styled
* @param length
* length of text to be styled
* @return StyleRange for text
*/
private StyleRange getStyle(final int style, final int start, final int length) {
final StyleRange styleRange = new StyleRange();
styleRange.start = start;
styleRange.length = length;
switch (style) {
case STYLE_RESULT:
styleRange.foreground = fResourceManager.createColor(fColorDescriptorResult);
break;
case STYLE_COMMAND:
styleRange.foreground = fResourceManager.createColor(fColorDescriptorCommand);
styleRange.fontStyle = SWT.BOLD;
break;
case STYLE_ERROR:
styleRange.foreground = fResourceManager.createColor(fColorDescriptorError);
styleRange.fontStyle = SWT.ITALIC;
break;
default:
break;
}
return styleRange;
}
}