/*
* Copyright (c) 2010-2016, Sikuli.org, sikulix.com
* Released under the MIT License.
*
*/
package org.sikuli.ide;
//
// A simple Java Console for your application (Swing version)
// Requires Java 1.1.5 or higher
//
// Disclaimer the use of this source is at your own risk.
//
// Permision to use and distribute into your own applications
//
// RJHM van den Bergh , rvdb@comweb.nl
import org.sikuli.basics.PreferencesUser;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.io.*;
import java.util.Arrays;
import java.util.regex.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.JMenuItem;
import org.sikuli.basics.Debug;
import org.sikuli.scriptrunner.IScriptRunner;
import org.sikuli.basics.Settings;
import org.sikuli.scriptrunner.ScriptingSupport;
public class EditorConsolePane extends JPanel implements Runnable {
private static final String me = "EditorConsolePane: ";
static boolean ENABLE_IO_REDIRECT = true;
static {
String flag = System.getProperty("sikuli.console");
if (flag != null && flag.equals("false")) {
ENABLE_IO_REDIRECT = false;
}
}
private int NUM_PIPES;
private JTextPane textArea;
private Thread[] reader;
private boolean quit;
private PipedInputStream[] pin;
private JPopupMenu popup;
Thread errorThrower; // just for testing (Throws an Exception at this Console)
class PopupListener extends MouseAdapter {
JPopupMenu popup;
PopupListener(JPopupMenu popupMenu) {
popup = popupMenu;
}
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
}
public EditorConsolePane() {
super();
textArea = new JTextPane();
textArea.setEditorKit(new HTMLEditorKit());
textArea.setTransferHandler(new JTextPaneHTMLTransferHandler());
String css = PreferencesUser.getInstance().getConsoleCSS();
((HTMLEditorKit) textArea.getEditorKit()).getStyleSheet().addRule(css);
textArea.setEditable(false);
setLayout(new BorderLayout());
add(new JScrollPane(textArea), BorderLayout.CENTER);
if (ENABLE_IO_REDIRECT) {
Debug.log(3, "EditorConsolePane: starting redirection to message area");
int npipes = 2;
NUM_PIPES = npipes * ScriptingSupport.scriptRunner.size();
pin = new PipedInputStream[NUM_PIPES];
reader = new Thread[NUM_PIPES];
for (int i = 0; i < NUM_PIPES; i++) {
pin[i] = new PipedInputStream();
}
int irunner = 0;
for (IScriptRunner srunner : ScriptingSupport.scriptRunner.values()) {
Debug.log(3, "EditorConsolePane: redirection for %s", srunner.getName());
if (srunner.doSomethingSpecial("redirect", Arrays.copyOfRange(pin, irunner*npipes, irunner*npipes+2))) {
Debug.log(3, "EditorConsolePane: redirection success for %s", srunner.getName());
quit = false; // signals the Threads that they should exit
//TODO Hack to avoid repeated redirect of stdout/err
ScriptingSupport.systemRedirected = true;
// Starting two seperate threads to read from the PipedInputStreams
for (int i = irunner * npipes; i < irunner * npipes + npipes; i++) {
reader[i] = new Thread(this);
reader[i].setDaemon(true);
reader[i].start();
}
irunner++;
}
}
}
//Create the popup menu.
popup = new JPopupMenu();
JMenuItem menuItem = new JMenuItem("Clear messages");
// Add ActionListener that clears the textArea
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.setText("");
}
});
popup.add(menuItem);
//Add listener to components that can bring up popup menus.
MouseListener popupListener = new PopupListener(popup);
textArea.addMouseListener(popupListener);
}
private void appendMsg(String msg) {
HTMLDocument doc = (HTMLDocument) textArea.getDocument();
HTMLEditorKit kit = (HTMLEditorKit) textArea.getEditorKit();
try {
kit.insertHTML(doc, doc.getLength(), msg, 0, 0, null);
} catch (Exception e) {
Debug.error(me + "Problem appending text to message area!\n%s", e.getMessage());
}
}
/*
public synchronized void windowClosed(WindowEvent evt)
{
quit=true;
this.notifyAll(); // stop all threads
try { reader.join(1000);pin.close(); } catch (Exception e){}
try { reader2.join(1000);pin2.close(); } catch (Exception e){}
System.exit(0);
}
public synchronized void windowClosing(WindowEvent evt)
{
frame.setVisible(false); // default behaviour of JFrame
frame.dispose();
}
*/
static final String lineSep = System.getProperty("line.separator");
private String htmlize(String msg) {
StringBuilder sb = new StringBuilder();
Pattern patMsgCat = Pattern.compile("\\[(.+?)\\].*");
msg = msg.replace("&", "&").replace("<", "<").replace(">",">");
for (String line : msg.split(lineSep)) {
Matcher m = patMsgCat.matcher(line);
String cls = "normal";
if (m.matches()) {
cls = m.group(1);
}
line = "<span class='" + cls + "'>" + line + "</span>";
sb.append(line).append("<br>");
}
return sb.toString();
}
@Override
public synchronized void run() {
try {
for (int i = 0; i < NUM_PIPES; i++) {
while (Thread.currentThread() == reader[i]) {
try {
this.wait(100);
} catch (InterruptedException ie) {
}
if (pin[i].available() != 0) {
String input = this.readLine(pin[i]);
appendMsg(htmlize(input));
if (textArea.getDocument().getLength() > 0) {
textArea.setCaretPosition(textArea.getDocument().getLength() - 1);
}
}
if (quit) {
return;
}
}
}
} catch (Exception e) {
Debug.error(me + "Console reports an internal error:\n%s", e.getMessage());
}
}
public synchronized String readLine(PipedInputStream in) throws IOException {
String input = "";
do {
int available = in.available();
if (available == 0) {
break;
}
byte b[] = new byte[available];
in.read(b);
input = input + new String(b, 0, b.length);
} while (!input.endsWith("\n") && !input.endsWith("\r\n") && !quit);
return input;
}
public void clear() {
textArea.setText("");
}
}
class JTextPaneHTMLTransferHandler extends TransferHandler {
private static final String me = "EditorConsolePane: ";
public JTextPaneHTMLTransferHandler() {
}
@Override
public void exportToClipboard(JComponent comp, Clipboard clip, int action) {
super.exportToClipboard(comp, clip, action);
}
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
@Override
protected Transferable createTransferable(JComponent c) {
JTextPane aTextPane = (JTextPane) c;
HTMLEditorKit kit = ((HTMLEditorKit) aTextPane.getEditorKit());
StyledDocument sdoc = aTextPane.getStyledDocument();
int sel_start = aTextPane.getSelectionStart();
int sel_end = aTextPane.getSelectionEnd();
int i = sel_start;
StringBuilder output = new StringBuilder();
while (i < sel_end) {
Element e = sdoc.getCharacterElement(i);
Object nameAttr = e.getAttributes().getAttribute(StyleConstants.NameAttribute);
int start = e.getStartOffset(), end = e.getEndOffset();
if (nameAttr == HTML.Tag.BR) {
output.append("\n");
} else if (nameAttr == HTML.Tag.CONTENT) {
if (start < sel_start) {
start = sel_start;
}
if (end > sel_end) {
end = sel_end;
}
try {
String str = sdoc.getText(start, end - start);
output.append(str);
} catch (BadLocationException ble) {
Debug.error(me + "Copy-paste problem!\n%s", ble.getMessage());
}
}
i = end;
}
return new StringSelection(output.toString());
}
}