//-----------------------------------------------------------------------------
// $RCSfile: PythonPanel.java,v $
// $Revision: 1.6 $
// $Author: snoopdave $
// $Date: 2000/04/08 20:12:48 $
//-----------------------------------------------------------------------------
package org.relayirc.swingutil;
import org.python.util.PythonInterpreter;
import org.python.core.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
/**
* Interactive JPython panel that emulates the one in PythonWin IDE.
* @author David M Johnson
* @version $Revision: 1.6 $
*
The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL
* Original Code: Relay IRC Chat Engine
* Initial Developer: David M. Johnson
* Contributor(s): No contributors to this file (yet)
* Copyright (C) 1997-2000 by David M. Johnson
* All Rights Reserved.
*/
public class PythonPanel extends JPanel {
private PythonInterpreter _python = new PythonInterpreter();
private JTextArea _textArea = new JTextArea();
public static int INDENT = 3;
public static int PROMPT_LENGTH = 3;
public static String START_PROMPT = ">>>";
public static String CONTINUE_PROMPT = "...";
//----------------------------------------------------------------------
/** Construct PythonPanel and its embedded JPython interpreter. */
public PythonPanel() {
PythonInterpreter _python = new PythonInterpreter();
_python.exec("zzz=1"); // warm-up the interpreter...
_textArea.setFont(new Font("Courier",Font.PLAIN,12));
_textArea.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent de) {
String text = "";
int len = de.getLength();
try {
text = de.getDocument().getText(de.getOffset(),len);
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
if (text.equals("\n")) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
handleNewLine();
}
});
}
}
public void changedUpdate(DocumentEvent de) {}
public void removeUpdate(DocumentEvent de) {}
});
firstLine();
setLayout(new BorderLayout());
add(_textArea,BorderLayout.CENTER);
}
//----------------------------------------------------------------------
/** This is where the emulate-PythonWin logic lives. */
private void handleNewLine() {
int pos = _textArea.getCaretPosition();
Document doc = _textArea.getDocument();
try {
// Get line entered by user
int prevline = searchBack("\n",pos - 2);
String line = doc.getText( prevline + 1, (pos - prevline) );
line = line.trim();
boolean isCodeLine = line.startsWith(START_PROMPT)
|| line.startsWith(CONTINUE_PROMPT);
int prevPrompt = searchBack(START_PROMPT,doc.getLength());
int nextPrompt = searchForth(START_PROMPT,pos);
debug.println("["+line+"]");
debug.println(
"pos ="+pos+" prevline="+prevline+" iscode="+isCodeLine);
// On empty start prompt before prev prompt -> send caret to new prompt
if (line.equals( START_PROMPT ) && pos < prevPrompt) {
debug.println("A");
removeNewLine(pos);
newLine();
}
// On empty start prompt line at end -> send caret to new prompt
else if (line.equals( START_PROMPT ) && pos > prevPrompt) {
debug.println("B");
newPrompt();
}
// On empty continue prompt line -> execute script then new prompt
else if (line.equals( CONTINUE_PROMPT ) && pos > prevPrompt) {
debug.println("C");
executeScript();
newPrompt();
}
// On non-code line before last prompt -> send caret to new prompt
else if (!isCodeLine && pos < prevPrompt) {
debug.println("D");
removeNewLine(pos);
newLine();
}
// On code line before last start prompt -> grab script, put it at end
else if (isCodeLine && pos < prevPrompt) {
debug.println("E");
removeNewLine(pos);
grabCode(pos);
}
// Within code after last start prompt -> execute code
else if (isCodeLine && nextPrompt == -1 && pos < doc.getLength() - 1) {
debug.println("F");
removeNewLine(pos);
executeScript();
newPrompt();
}
// Within text in current script -> send caret to new prompt
else if (isCodeLine && pos < doc.getLength()-1) {
debug.println("G");
newPrompt();
}
// At end of code line ending with a colon -> increate indent
else if (isCodeLine && line.endsWith(":")) {
debug.println("H");
indentLine(_textArea.getCaretPosition(),figureIndent(line)+INDENT);
}
// At end of start prompt line with no colon -> execute line
else if (line.startsWith( START_PROMPT )) {
debug.println("I");
executeScript();
newPrompt();
}
// At end of ... prompt line with no colon -> indent
else if (line.startsWith( CONTINUE_PROMPT )) {
debug.println("J");
indentLine(_textArea.getCaretPosition(),figureIndent(line));
}
else {
debug.println("K");
newPrompt();
}
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
//----------------------------------------------------------------------
/** Remove newline at specified position. */
private void removeNewLine(int pos) {
try {
Document doc = _textArea.getDocument();
doc.remove(pos-1,1);
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
//----------------------------------------------------------------------
/** Grab the script at specified postion, copy it and paste it to the
* end of the text area.
*/
private void grabCode(int pos) {
int prevPrompt = 0;
try {
// Script begins at last start prompt before pos and ends
// at last continue prompt after pos.
Document doc = _textArea.getDocument();
int bos = searchBack(START_PROMPT,pos) + PROMPT_LENGTH + 1;
int nextNewLine = searchForth("\n",pos);
int eos = searchForth(START_PROMPT,pos) - 1;
int lastCont = searchBack(CONTINUE_PROMPT,eos) - 1;
if (lastCont < bos) {
eos = nextNewLine;
}
else {
eos = lastCont;
}
// Put script at end
String script = doc.getText(bos,eos-bos);
debug.println("\n"+script+"\n\n");
doc.insertString(doc.getLength()-1,script,null);
_textArea.setCaretPosition(doc.getLength()-1);
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
//----------------------------------------------------------------------
/** Execute the script at end of text area. */
private void executeScript() {
try {
// Script begins a last start prompt and ends at end of text area
Document doc = _textArea.getDocument();
int eos = doc.getLength() - 1;
int bos = searchBack(START_PROMPT,eos);
debug.println("bos="+bos+" eos="+eos);
// Grab the script, strip off start and continue prompts
String script = "";
String rawScript = doc.getText(bos,eos-bos);
StringTokenizer toker = new StringTokenizer(rawScript,"\n");
while (toker.hasMoreTokens()) {
try {
String token = toker.nextToken();
script += token.substring(PROMPT_LENGTH+1)+"\n";
}
catch (NoSuchElementException nsee) {
// Ignored by design
}
}
// Execute the script
debug.println("\n");
StringWriter outw = new StringWriter();
StringWriter errw = new StringWriter();
_python.setOut(outw);
_python.setErr(errw);
try {
_python.exec(script);
}
catch (Exception e) {
e.printStackTrace();
}
// Print out theo script results
if (errw.toString().length() > 0) {
doc.insertString(doc.getLength()-1,errw.toString(),null);
}
else {
doc.insertString(doc.getLength()-1,outw.toString(),null);
}
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
//----------------------------------------------------------------------
/** Creates new line at end of text area and send caret there. */
private void newLine() {
Document doc = _textArea.getDocument();
int pos = doc.getLength() > 0 ? doc.getLength() - 1 : 0;
try {
doc.insertString(pos,"\n",null);
_textArea.setCaretPosition(doc.getLength()-1);
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
//----------------------------------------------------------------------
/** Creates banner and initial start prompt. */
private void firstLine() {
Document doc = _textArea.getDocument();
int pos = doc.getLength() > 0 ? doc.getLength() - 1 : 0;
try {
String banner = "JPython is Copyright (c) 1997-1999 CNRI\n";
banner+="PythonPanel is Copyright (c) 1999-2000 by David M. Johnson\n";
doc.insertString(pos,banner,null);
doc.insertString(doc.getLength()-1,"\n",null);
_textArea.setCaretPosition(doc.getLength()-1);
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
//----------------------------------------------------------------------
/** Creates new prompt at end of text area and sends caret there. */
private void newPrompt() {
Document doc = _textArea.getDocument();
int pos = doc.getLength() > 0 ? doc.getLength() - 1 : 0;
String prefix = START_PROMPT+" ";
try {
doc.insertString(pos,prefix,null);
_textArea.setCaretPosition(pos+prefix.length());
}
catch (BadLocationException ble) {
ble.printStackTrace();
}
}
//----------------------------------------------------------------------
/** Determine indent level of a string. */
private int figureIndent(String inputstr) {
String line = inputstr.substring(PROMPT_LENGTH+1);
StringBuffer strbuf = new StringBuffer(line);
int indent = 0;
for (int i=0; i= 0) {
try {
String curstr = doc.getText(pos,str.length());
if (curstr.equals(str)) {
result = pos;
break;
}
}
catch (BadLocationException ble) {
// Ignored by design
}
pos--;
}
return result;
}
//----------------------------------------------------------------------
/** Seach text area forwards for str, starting at pos.
* @param str Search string
* @param pos Starting point of search
* @return Index of str, or -1 on error.
*/
private int searchForth(String str, int pos) {
int result = -1;
Document doc = _textArea.getDocument();
while (pos < doc.getLength()) {
try {
String curstr = doc.getText(pos,str.length());
if (curstr.equals(str)) {
result = pos;
break;
}
}
catch (BadLocationException ble) {
// Ignored by design
}
pos++;
}
return result;
}
//----------------------------------------------------------------------
static class debug {
private static boolean _on = false;
public static void setDebug(boolean flag) {
_on = flag;
}
public static void println(String txt) {
if (_on) System.out.println(txt);
}
}
//----------------------------------------------------------------------
/** For testing: turn on debugging and show a JFrame with PythonPanel. */
public static void main(String args[]) {
debug.setDebug(true);
JFrame frame = new JFrame("PythonPanel Test Frame");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(-1);
}
});
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(new JScrollPane(new PythonPanel()));
frame.setSize(500,300);
frame.setLocation(250,400);
frame.setVisible(true);
}
}