/**
* Copyright (c) 2015 by Brainwy Software Ltda. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.debug.console;
import java.io.IOException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.internal.ui.views.console.ProcessConsole;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IOConsoleOutputStream;
import org.eclipse.ui.console.TextConsoleViewer;
import org.eclipse.ui.internal.console.IOConsolePage;
import org.eclipse.ui.internal.console.IOConsolePartitioner;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.model.PyDebugTarget;
import org.python.pydev.debug.newconsole.CurrentPyStackFrameForConsole;
import org.python.pydev.debug.newconsole.PydevConsoleConstants;
import org.python.pydev.debug.newconsole.PydevConsoleFactory;
import org.python.pydev.debug.newconsole.PydevDebugConsole;
import org.python.pydev.debug.newconsole.PydevDebugConsoleCommunication;
import org.python.pydev.debug.newconsole.prefs.ColorManager;
import org.python.pydev.shared_interactive_console.console.IScriptConsoleCommunication;
import org.python.pydev.shared_interactive_console.console.InterpreterResponse;
import org.python.pydev.shared_interactive_console.console.ScriptConsolePrompt;
import org.python.pydev.shared_interactive_console.console.ui.IConsoleStyleProvider;
import org.python.pydev.shared_interactive_console.console.ui.IScriptConsoleListener;
import org.python.pydev.shared_interactive_console.console.ui.internal.IScriptConsoleContentHandler;
import org.python.pydev.shared_interactive_console.console.ui.internal.ScriptConsoleViewer;
@SuppressWarnings("restriction")
public class PromptOverlay implements DisposeListener, Listener, IScriptConsoleContentHandler {
private static final String IS_PROMPT_OVERLAY_STYLED_TEXT = "IS_PROMPT_OVERLAY_STYLED_TEXT";
private StyledText interactiveConsoleTextWidget;
private StyledText styledText;
private Composite styledTextParent;
private CustomPageBookLayout customLayout;
private final CurrentPyStackFrameForConsole currentPyStackFrameForConsole;
private ScriptConsoleViewer viewer;
private PromptOverlayReplaceGlobalActionHandlers promptOverlayActionHandlers;
private boolean overlayVisible = true;
private double percSize = .3;
private PydevDebugConsole debugConsole;
private boolean bufferedOutput = false;
private PyDebugTarget debugTarget;
public PromptOverlay(IOConsolePage consolePage, final ProcessConsole processConsole,
CurrentPyStackFrameForConsole currentPyStackFrameForConsole) {
this.currentPyStackFrameForConsole = currentPyStackFrameForConsole;
SourceViewerConfiguration cfg;
try {
ILaunch launch = processConsole.getProcess().getLaunch();
debugTarget = (PyDebugTarget) launch.getDebugTarget();
if (debugTarget == null) {
throw new RuntimeException("debugTarget should be a non-null PyDebugTarget.");
}
debugConsole = new PydevConsoleFactory().createDebugConsole(launch, "", false, bufferedOutput,
currentPyStackFrameForConsole);
cfg = debugConsole.createSourceViewerConfiguration();
processConsole.setAttribute(PydevDebugConsole.SCRIPT_DEBUG_CONSOLE_IN_PROCESS_CONSOLE, debugConsole);
} catch (Exception e) {
// If we can't create the debug console, bail out and do nothing else.
Log.log(e);
return;
}
TextConsoleViewer consoleViewer = consolePage.getViewer();
final StyledText styledText = (StyledText) consoleViewer.getControl();
this.styledText = styledText;
styledTextParent = styledText.getParent();
final IConsoleStyleProvider styleProvider = debugConsole.createStyleProvider();
viewer = new ScriptConsoleViewer(styledTextParent, debugConsole, this, styleProvider,
debugConsole.getInitialCommands(), debugConsole.getFocusOnStart(), debugConsole.getBackspaceAction(),
debugConsole.getAutoEditStrategy(), debugConsole.getTabCompletionEnabled(), false);
viewer.configure(cfg);
Layout currentLayout = styledTextParent.getLayout();
this.customLayout = new CustomPageBookLayout(currentLayout);
this.interactiveConsoleTextWidget = viewer.getTextWidget();
this.interactiveConsoleTextWidget.setData(IS_PROMPT_OVERLAY_STYLED_TEXT, Boolean.TRUE);
final IOConsoleOutputStream streamPrompt = processConsole.newOutputStream();
final IOConsoleOutputStream stream = processConsole.newOutputStream();
this.promptOverlayActionHandlers = new PromptOverlayReplaceGlobalActionHandlers(consolePage, viewer);
IActionBars bars = consolePage.getSite().getActionBars();
IToolBarManager toolbarManager = bars.getToolBarManager();
ShowPromptOverlayAction showPromptOverlayAction = new ShowPromptOverlayAction(this);
toolbarManager.prependToGroup(IConsoleConstants.LAUNCH_GROUP, showPromptOverlayAction);
bars.updateActionBars();
debugConsole.addListener(new IScriptConsoleListener() {
@Override
public void userRequest(String text, ScriptConsolePrompt prompt) {
try {
if (!bufferedOutput) {
streamPrompt.setColor(ColorManager.getDefault().getPreferenceColor(
PydevConsoleConstants.CONSOLE_PROMPT_COLOR));
stream.setColor(ColorManager.getDefault().getPreferenceColor(
PydevConsoleConstants.CONSOLE_INPUT_COLOR));
IDocument document = processConsole.getDocument();
IDocumentPartitioner partitioner = document.getDocumentPartitioner();
IOConsolePartitioner ioConsolePartitioner = (IOConsolePartitioner) partitioner;
ioConsolePartitioner.streamAppended(streamPrompt, prompt.toString());
ioConsolePartitioner.streamAppended(stream, text + "\n");
}
} catch (IOException e) {
Log.log(e);
}
}
@Override
public void interpreterResponse(InterpreterResponse response, ScriptConsolePrompt prompt) {
}
@Override
public boolean isOnStateWhereCommandHandlingShouldStop(String commandLine) {
if (debugTarget.isWaitingForInput()) {
if (!commandLine.endsWith("\n")) {
commandLine += "\n";
}
// This should add the command to the input stream.
styledText.append(commandLine);
return true;
}
return false;
}
});
styledText.addDisposeListener(this);
styledText.addListener(SWT.Hide, this);
styledText.addListener(SWT.Show, this);
styledText.addListener(SWT.Paint, this);
styledText.addListener(SWT.Resize, this);
styledText.addListener(SWT.Selection, this);
adjust();
}
@Override
public void contentAssistRequired() {
if (this.currentPyStackFrameForConsole.getLastSelectedFrame() == null) {
return;
}
viewer.getContentAssist().showPossibleCompletions();
}
@Override
public void quickAssistRequired() {
viewer.getQuickAssistAssistant().showPossibleQuickAssists();
}
@Override
public void widgetDisposed(DisposeEvent e) {
dispose();
}
@Override
public void handleEvent(Event event) {
adjust();
}
private void adjust() {
if (styledTextParent == null || styledTextParent.isDisposed()) {
return;
}
if (overlayVisible && styledText != null && !styledText.isDisposed() && styledText.isVisible()) {
if (styledTextParent.getLayout() != customLayout) {
styledTextParent.setLayout(customLayout);
styledTextParent.layout(true);
}
if (!interactiveConsoleTextWidget.isVisible()) {
interactiveConsoleTextWidget.setVisible(true);
}
if (!interactiveConsoleTextWidget.getBackground().equals(styledText.getBackground())) {
interactiveConsoleTextWidget.setBackground(styledText.getBackground());
}
if (!interactiveConsoleTextWidget.getForeground().equals(styledText.getForeground())) {
interactiveConsoleTextWidget.setForeground(styledText.getForeground());
}
if (!interactiveConsoleTextWidget.getFont().equals(styledText.getFont())) {
interactiveConsoleTextWidget.setFont(styledText.getFont());
}
} else {
if (interactiveConsoleTextWidget.isVisible()) {
interactiveConsoleTextWidget.setVisible(false);
}
if (styledTextParent.getLayout() != this.customLayout.originalParentLayout) {
styledTextParent.setLayout(this.customLayout.originalParentLayout);
styledTextParent.layout(true);
}
}
}
private class CustomPageBookLayout extends Layout {
public final Layout originalParentLayout;
public CustomPageBookLayout(Layout originalParentLayout) {
if (originalParentLayout instanceof CustomPageBookLayout) {
//It's there by some other view of ours (switched directly between them).
CustomPageBookLayout customPageBookLayout = (CustomPageBookLayout) originalParentLayout;
this.originalParentLayout = customPageBookLayout.originalParentLayout;
} else {
this.originalParentLayout = originalParentLayout;
}
}
@Override
protected Point computeSize(Composite composite, int wHint, int hHint,
boolean flushCache) {
if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) {
return new Point(wHint, hHint);
}
Point result = null;
if (styledText != null) {
result = styledText.computeSize(wHint, hHint, flushCache);
} else {
result = new Point(0, 0);
}
if (wHint != SWT.DEFAULT) {
result.x = wHint;
}
if (hHint != SWT.DEFAULT) {
result.y = hHint;
}
return result;
}
@Override
protected void layout(Composite composite, boolean flushCache) {
if (styledText != null && !styledText.isDisposed()) {
Rectangle bounds = composite.getClientArea();
int height = bounds.height;
int perc = (int) (height * percSize); // 30% to the input
interactiveConsoleTextWidget.setBounds(bounds.x, bounds.y + height - perc, bounds.width,
perc);
styledText.setBounds(bounds.x, bounds.y, bounds.width, height - perc);
}
}
}
public void dispose() {
try {
styledText = null;
if (interactiveConsoleTextWidget != null) {
interactiveConsoleTextWidget.setVisible(false);
interactiveConsoleTextWidget.dispose();
interactiveConsoleTextWidget = null;
}
} catch (Exception e1) {
Log.log(e1);
}
try {
if (styledTextParent != null) {
if (!styledTextParent.isDisposed()) {
if (styledTextParent.getLayout() == customLayout) {
styledTextParent.setLayout(this.customLayout.originalParentLayout);
}
}
styledTextParent = null;
}
} catch (Exception e1) {
Log.log(e1);
}
try {
if (promptOverlayActionHandlers != null) {
promptOverlayActionHandlers.dispose();
}
promptOverlayActionHandlers = null;
} catch (Exception e1) {
Log.log(e1);
}
}
public void setOverlayVisible(boolean visible) {
if (this.overlayVisible != visible) {
this.overlayVisible = visible;
adjustAndLayout();
}
}
/**
* Returns a number from 0 - 100.
*/
public int getRelativeConsoleHeight() {
return (int) (this.percSize * 100);
}
public void setRelativeConsoleHeight(int relSize0To100) {
double newVal = relSize0To100 / 100.;
if (newVal != this.percSize) {
this.percSize = newVal;
adjustAndLayout();
}
}
private void adjustAndLayout() {
adjust();
if (styledTextParent != null && !styledTextParent.isDisposed()) {
styledTextParent.layout(true);
}
}
public void activated() {
//I.e.: Console view gets focus
}
public void deactivated() {
//I.e.: Console view looses focus
}
public void setBufferedOutput(boolean bufferedOutput) {
if (this.bufferedOutput != bufferedOutput) {
this.bufferedOutput = bufferedOutput;
IScriptConsoleCommunication consoleCommunication = debugConsole.getInterpreter().getConsoleCommunication();
if (consoleCommunication instanceof PydevDebugConsoleCommunication) {
PydevDebugConsoleCommunication pydevDebugConsoleCommunication = (PydevDebugConsoleCommunication) consoleCommunication;
pydevDebugConsoleCommunication.setBufferedOutput(bufferedOutput);
}
}
}
}