/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) 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:
* bstefanescu
*/
package org.eclipse.ecr.ide.shell.views;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.lang.reflect.Method;
import jline.ConsoleReader;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.LineStyleEvent;
import org.eclipse.swt.custom.LineStyleListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.nuxeo.shell.Shell;
import org.nuxeo.shell.cmds.ConsoleReaderFactory;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*
*/
public class Console extends StyledText implements ConsoleReaderFactory, KeyListener, VerifyKeyListener, VerifyListener, LineStyleListener {
protected Method complete;
protected ConsoleReader reader;
protected final In in;
protected final Writer out;
protected Font font;
protected CompletionBuffer completionBuf = null;
protected Character mask;
protected StringBuilder pwd;
public Console(Composite parent) throws Exception {
super (parent, SWT.H_SCROLL | SWT.V_SCROLL);
in = new In();
out = new Out();
reader = new ConsoleReader(in, out, null, new SWTTerminal(this));
//reader.setCompletionHandler(new SwingCompletionHandler(this));
complete = reader.getClass().getDeclaredMethod("complete");
complete.setAccessible(true);
setMargins(6, 6, 6, 6);
setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
setForeground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
font = new Font(getDisplay(), "Courier", 14, SWT.NONE);
setFont(font);
addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
if (font != null) {
font.dispose();
font = null;
exit();
}
}
});
addKeyListener(this);
addVerifyKeyListener(this);
addVerifyListener(this);
addLineStyleListener(this);
}
protected void resetTerminal() {
setText("");
try {
Shell.get().hello();
} catch (Exception e) {
e.printStackTrace();
}
in.put("\n");
}
/**
* @param mask the mask to set
*/
public void setMask(Character mask) {
this.mask = mask;
}
public String getPrompt() {
return reader.getDefaultPrompt();
}
@Override
public ConsoleReader getConsoleReader() {
return reader;
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void verifyKey(VerifyEvent e) {
// if not writing in the last line move to last line
int rows = getLineCount();
int offset = getCaretOffset();
if (getLineAtOffset(offset) != rows-1) {
if (e.character >= 32 && e.character < 256) {
setSelection(getCharCount());
}
} else if (e.keyCode == SWT.ARROW_LEFT || e.keyCode == SWT.BS) {
// avoid moving cursor in prompt area
String text = getLine(rows-1);
int lineStart = getCharCount() - text.length();
int cmdStart = lineStart + getPrompt().length();
if (offset <= cmdStart) {
e.doit = false;
}
} else if (e.keyCode == SWT.ARROW_UP) {
if (reader.getHistory().previous()) {
setCommandText(reader.getHistory().current());
} else {
beep();
}
e.doit = false;
} else if (e.keyCode == SWT.ARROW_DOWN) {
if (reader.getHistory().next()) {
setCommandText(reader.getHistory().current());
} else {
beep();
}
e.doit = false;
} else if (e.keyCode == SWT.TAB) {
complete();
e.doit = false;
} else if (e.keyCode == SWT.CR) {
if (mask != null) {
String text = pwd == null ? "" : pwd.toString();
pwd = null;
in.put(text+"\n");
return;
}
String text = getCommandText().trim();
if ("exit".equals(text)) {
resetTerminal();
} else {
resetBuf();
if (text.length() > 0 && reader.getUseHistory()) {
reader.getHistory().addToHistory(text);
reader.getHistory().moveToEnd();
}
in.put(text+"\n");
}
}
}
protected StyleRange sr = null;
@Override
public void verifyText(VerifyEvent e) {
sr = null;
//System.out.println("]]]]]] "+e.start+":"+e.end+": "+e.text);
if (mask != null) {
if (e.text.length() == 1) {
if (e.text.charAt(0) >= 32) {
if (pwd == null) {
pwd = new StringBuilder();
}
pwd.append(e.text.charAt(0));
e.text = ""+mask.charValue();
}
}
} else {
// remove format tags and store style range info to be used to apply styles
// int i = e.text.indexOf("CREDITS");
// sr = new StyleRange();
// sr.start = e.start+i;
// sr.length = "CREDITS".length();
// sr.fontStyle = SWT.BOLD;
// sr.foreground = getDisplay().getSystemColor(SWT.COLOR_WHITE);
//TODO
}
}
@Override
public void lineGetStyle(LineStyleEvent event) {
if (sr != null) {
int i = event.lineOffset+event.lineText.length();
if (sr.start < i && event.lineOffset <= sr.start) {
event.styles = new StyleRange[] {sr};
sr = null;
}
}
}
public void exit() {
in.put("exit 1\n");
}
public void complete() {
syncBuf();
completionBuf = new CompletionBuffer(getCommandTextBeforeCaret());
System.out.println("#start: "+completionBuf.toString());
try {
if (!((Boolean) complete.invoke(reader))) {
beep();
} else {
setCommandText(completionBuf.toString());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
completionBuf = null;
}
}
public void setCommandText(String text) {
setSelection(getCommandLineOffset()+getPrompt().length(), getCharCount());
insert(text);
setSelection(getCharCount());
}
public void beep() {
getDisplay().beep();
}
public void syncBuf() {
StringBuffer sb = reader.getCursorBuffer().getBuffer();
sb.setLength(0);
sb.append(getCommandText());
reader.getCursorBuffer().cursor = getCommandCursor();
}
public void resetBuf() {
StringBuffer sb = reader.getCursorBuffer().getBuffer();
sb.setLength(0);
reader.getCursorBuffer().cursor = 0;
}
public final String getCommandLine() {
return getLine(getLineCount()-1);
}
public final String getCommandText() {
return getCommandLine().substring(getPrompt().length());
}
public final String getCommandTextBeforeCaret() {
String line = getCommandLine();
return line.substring(getPrompt().length(), line.length() - (getCharCount() - getCaretOffset()));
}
public final int getCommandLineOffset() {
return getCharCount() - getCommandLine().length();
}
public final int getCommandCursor() {
return getCaretOffset() - getPrompt().length() - getCommandLineOffset();
}
class In extends InputStream {
protected StringBuilder buf = new StringBuilder();
public synchronized void put(int key) {
buf.append((char) key);
notify();
}
public synchronized void put(String text) {
buf.append(text);
notify();
}
@Override
public synchronized int read() throws IOException {
if (buf.length() > 0) {
char c = buf.charAt(0);
buf.deleteCharAt(0);
return c;
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (buf.length() == 0) {
throw new IllegalStateException(
"invalid state for console input stream");
}
char c = buf.charAt(0);
buf.deleteCharAt(0);
return c;
}
}
class Out extends Writer {
protected void _write(final String str) {
if (Display.getCurrent() != null) {
// we are in UI thread
print(str);
} else {
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
print(str);
};
});
}
}
protected void print(String str) {
Console.this.append(str);
setSelection(getCharCount());
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
if (len == 0) {
return;
}
// System.out.print(">in ");
// for (int i=0; i<len; i++) {
// System.out.print(((int)cbuf[off+i])+"["+cbuf[off+i]+"]");
// }
// System.out.println();
if (completionBuf != null) {
completionBuf.append(cbuf, off, len);
} else {
_write(new String(cbuf, off, len));
}
}
@Override
public void flush() throws IOException {
}
@Override
public void close() throws IOException {
flush();
}
}
}