/* * 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()); } }