/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* LogWindow.java
* Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui;
import weka.core.Tee;
import weka.core.Utils;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.PrintStream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
/**
* Frame that shows the output from stdout and stderr.
*
* @author FracPete (fracpete at waikato dot ac dot nz)
* @version $Revision: 4973 $
*/
public class LogWindow
extends JFrame
implements CaretListener, ChangeListener {
/** for serialization */
private static final long serialVersionUID = 5650947361381061112L;
/** the name of the style for stdout */
public final static String STYLE_STDOUT = "stdout";
/** the name of the style for stderr */
public final static String STYLE_STDERR = "stderr";
/** the color of the style for stdout */
public final static Color COLOR_STDOUT = Color.BLACK;
/** the Color of the style for stderr */
public final static Color COLOR_STDERR = Color.RED;
/** whether we're debugging - enables output on stdout */
public final static boolean DEBUG = false;
/** whether the JTextPane has wordwrap or not */
public boolean m_UseWordwrap = true;
/** the output */
protected JTextPane m_Output = new JTextPane();
/** the clear button */
protected JButton m_ButtonClear = new JButton("Clear");
/** the close button */
protected JButton m_ButtonClose = new JButton("Close");
/** the current size */
protected JLabel m_LabelCurrentSize = new JLabel("currently: 0");
/** the spinner for the max number of chars */
protected JSpinner m_SpinnerMaxSize = new JSpinner();
/** whether to allow wordwrap or not */
protected JCheckBox m_CheckBoxWordwrap = new JCheckBox("Use wordwrap");
/** for redirecting stdout */
protected static Tee m_TeeOut = null;
/** for redirecting stderr */
protected static Tee m_TeeErr = null;
/** inner class for printing to the window, is used instead of standard
* System.out and System.err */
protected class LogWindowPrintStream extends PrintStream {
/** the parent */
protected LogWindow m_Parent = null;
/** the style of the printstream */
protected String m_Style = null;
/**
* the constructor
* @param parent the parent frame
* @param stream the stream (used for constructor of superclass)
* @param style the style name associated with this output
*/
public LogWindowPrintStream( LogWindow parent,
PrintStream stream,
String style ) {
super(stream);
m_Parent = parent;
m_Style = style;
}
/**
* flushes the printstream
*/
public synchronized void flush() {
// ignored
}
/**
* prints the given int
*/
public synchronized void print(int x) {
print(new Integer(x).toString());
}
/**
* prints the given boolean
*/
public synchronized void print(boolean x) {
print(new Boolean(x).toString());
}
/**
* prints the given string
*/
public synchronized void print(String x) {
StyledDocument doc;
int size;
int maxSize;
int pos;
doc = m_Parent.m_Output.getStyledDocument();
try {
// insert text
doc.insertString(doc.getLength(), x, doc.getStyle(m_Style));
// move cursor to end
m_Parent.m_Output.setCaretPosition(doc.getLength());
// trim size if necessary
m_Parent.trim();
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* prints the given object
*/
public synchronized void print(Object x) {
String line;
Throwable t;
StackTraceElement[] trace;
int i;
if (x instanceof Throwable) {
t = (Throwable) x;
trace = t.getStackTrace();
line = t.getMessage() + "\n";
for (i = 0; i < trace.length; i++)
line += "\t" + trace[i].toString() + "\n";
x = line;
}
if (x == null)
print("null");
else
print(x.toString());
}
/**
* prints a new line
*/
public synchronized void println() {
print("\n");
}
/**
* prints the given int
*/
public synchronized void println(int x) {
print(x);
println();
}
/**
* prints the given boolean
*/
public synchronized void println(boolean x) {
print(x);
println();
}
/**
* prints the given string
*/
public synchronized void println(String x) {
print(x);
println();
}
/**
* prints the given object (for Throwables we print the stack trace)
*/
public synchronized void println(Object x) {
print(x);
println();
}
}
/**
* creates the frame
*/
public LogWindow() {
super("Weka - Log");
createFrame();
// styles
StyledDocument doc;
Style style;
boolean teeDone;
doc = m_Output.getStyledDocument();
style = StyleContext.getDefaultStyleContext()
.getStyle(StyleContext.DEFAULT_STYLE);
style = doc.addStyle(STYLE_STDOUT, style);
StyleConstants.setFontFamily(style, "monospaced");
StyleConstants.setForeground(style, COLOR_STDOUT);
style = StyleContext.getDefaultStyleContext()
.getStyle(StyleContext.DEFAULT_STYLE);
style = doc.addStyle(STYLE_STDERR, style);
StyleConstants.setFontFamily(style, "monospaced");
StyleConstants.setForeground(style, COLOR_STDERR);
// print streams (instantiate only once!)
teeDone = !((m_TeeOut == null) && (m_TeeErr == null));
if (!DEBUG) {
if (!teeDone) {
m_TeeOut = new Tee(System.out);
System.setOut(m_TeeOut);
}
m_TeeOut.add(
new LogWindowPrintStream(this, m_TeeOut.getDefault(), STYLE_STDOUT));
}
if (!teeDone) {
m_TeeErr = new Tee(System.err);
System.setErr(m_TeeErr);
}
m_TeeErr.add(
new LogWindowPrintStream(this, m_TeeErr.getDefault(), STYLE_STDERR));
}
/**
* creates the frame and all its components
*/
protected void createFrame() {
JPanel panel;
JPanel panel2;
JPanel panel3;
JPanel panel4;
SpinnerNumberModel model;
int width;
JLabel label;
// set layout
setSize(600, 400);
width = getBounds().width;
setLocation(
getGraphicsConfiguration().getBounds().width - width, getLocation().y);
getContentPane().setLayout(new BorderLayout());
// output
getContentPane().add(new JScrollPane(m_Output), BorderLayout.CENTER);
setWordwrap(m_UseWordwrap);
// button(s)
panel = new JPanel(new BorderLayout());
getContentPane().add(panel, BorderLayout.SOUTH);
panel3 = new JPanel(new BorderLayout());
panel.add(panel3, BorderLayout.SOUTH);
panel2 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
panel3.add(panel2, BorderLayout.EAST);
m_ButtonClear.setMnemonic('C');
m_ButtonClear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
clear();
}
});
panel2.add(m_ButtonClear);
m_ButtonClose.setMnemonic('l');
m_ButtonClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
close();
}
});
panel2.add(m_ButtonClose);
// size + current size + wordwrap
panel2 = new JPanel(new GridLayout(1, 3));
panel3.add(panel2, BorderLayout.WEST);
// size
panel4 = new JPanel(new FlowLayout());
panel2.add(panel4);
model = (SpinnerNumberModel) m_SpinnerMaxSize.getModel();
model.setMinimum(new Integer(1));
model.setStepSize(new Integer(1000));
model.setValue(new Integer(100000));
model.addChangeListener(this);
label = new JLabel("max. Size");
label.setDisplayedMnemonic('m');
label.setLabelFor(m_SpinnerMaxSize);
panel4.add(label);
panel4.add(m_SpinnerMaxSize);
// current size
panel4 = new JPanel(new FlowLayout());
panel2.add(panel4);
panel4.add(m_LabelCurrentSize);
// wordwrap
panel4 = new JPanel(new FlowLayout());
panel2.add(panel4);
m_CheckBoxWordwrap.setSelected(m_UseWordwrap);
m_CheckBoxWordwrap.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
setWordwrap(m_CheckBoxWordwrap.isSelected());
}
});
panel4.add(m_CheckBoxWordwrap);
}
/**
* clears the output
*/
public void clear() {
m_Output.setText("");
}
/**
* closes the frame
*/
public void close() {
setVisible(false);
}
/**
* trims the JTextPane, if too big
*/
public void trim() {
StyledDocument doc;
int size;
int maxSize;
int pos;
doc = m_Output.getStyledDocument();
// too large?
size = doc.getLength();
maxSize = ((Integer) m_SpinnerMaxSize.getValue()).intValue();
if (size > maxSize) {
try {
// determine EOL after which to cut
pos = size - maxSize;
while (!doc.getText(pos, 1).equals("\n"))
pos++;
while (doc.getText(pos, 1).equals("\n"))
pos++;
// delete text
doc.remove(0, pos);
}
catch (Exception ex) {
// don't print it, otherwise we get an endless loop!
if (DEBUG)
System.out.println(ex);
}
}
// move cursor to end
m_Output.setCaretPosition(doc.getLength());
}
/**
* returns a string representation (#RGB) of the given color
*/
protected String colorToString(Color c) {
String result;
result = "#" + Utils.padLeft(Integer.toHexString(c.getRed()), 2)
+ Utils.padLeft(Integer.toHexString(c.getGreen()), 2)
+ Utils.padLeft(Integer.toHexString(c.getBlue()), 2);
result = result.replaceAll("\\ ", "0").toUpperCase();
return result;
}
/**
* toggles the wordwrap<br/>
* override wordwrap from:
* http://forum.java.sun.com/thread.jspa?threadID=498535&messageID=2356174
*/
public void setWordwrap(boolean wrap) {
Container parent;
JTextPane outputOld;
m_UseWordwrap = wrap;
if (m_CheckBoxWordwrap.isSelected() != m_UseWordwrap)
m_CheckBoxWordwrap.setSelected(m_UseWordwrap);
// create new JTextPane
parent = m_Output.getParent();
outputOld = m_Output;
if (m_UseWordwrap)
m_Output = new JTextPane();
else
m_Output = new JTextPane(){
private static final long serialVersionUID = -8275856175921425981L;
public void setSize(Dimension d) {
if (d.width < getGraphicsConfiguration().getBounds().width)
d.width = getGraphicsConfiguration().getBounds().width;
super.setSize(d);
}
public boolean getScrollableTracksViewportWidth() {
return false;
}
};
m_Output.setEditable(false);
m_Output.addCaretListener(this);
m_Output.setDocument(outputOld.getDocument());
m_Output.setCaretPosition(m_Output.getDocument().getLength());
//m_Output.setToolTipText(
// "stdout = " + colorToString(COLOR_STDOUT) + ", "
// + "stderr = " + colorToString(COLOR_STDERR));
parent.add(m_Output);
parent.remove(outputOld);
}
/**
* Called when the caret position is updated.
*/
public void caretUpdate(CaretEvent e) {
m_LabelCurrentSize.setText(
"currently: " + m_Output.getStyledDocument().getLength());
if (DEBUG)
System.out.println(e);
}
/**
* Invoked when the target of the listener has changed its state.
*/
public void stateChanged(ChangeEvent e) {
// check max size if Spinner is changed
if (e.getSource() == m_SpinnerMaxSize.getModel()) {
trim();
validate();
caretUpdate(null);
}
}
/**
* for testing only
*/
public static void main(String[] args) {
LogWindow log;
LookAndFeel.setLookAndFeel();
log = new LogWindow();
log.setVisible(true);
log.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// test output
System.out.print("a");
System.err.print("a");
System.out.print("a");
System.out.println();
System.err.println(new java.util.Date());
}
}