/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app;
import processing.app.debug.MessageConsumer;
import processing.core.*;
import processing.app.tools.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class SerialMonitor extends JFrame implements MessageConsumer {
private Serial serial;
private String port;
private JTextArea textArea;
private JScrollPane scrollPane;
private JTextField textField;
private JButton sendButton;
private JCheckBox autoscrollBox;
private JComboBox lineEndings;
private JComboBox serialRates;
private int serialRate;
private boolean speedDetection = false;
private String lineEnding;
private boolean hasPrintedTime = false;
private int insertAt = 0;
Color foreground;
Color background;
Color messageForeground;
public SerialMonitor(String port) {
super(port);
this.port = port;
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
closeSerialPort();
}
});
// obvious, no?
KeyStroke wc = Base.WINDOW_CLOSE_KEYSTROKE;
getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(wc, "close");
getRootPane().getActionMap().put("close", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
closeSerialPort();
setVisible(false);
}});
getContentPane().setLayout(new BorderLayout());
Font font = Theme.getFont("console.font");
foreground = Theme.getColor("serialmonitor.color");
background = Theme.getColor("serialmonitor.bgcolor");
messageForeground = Theme.getColor("serialmonitor.message.color");
textArea = new JTextArea(16, 40);
MouseListener popupListener = new PopupListener();
textArea.addMouseListener(popupListener);
textArea.setEditable(false);
textArea.setFont(font);
textArea.setForeground(foreground);
textArea.setBackground(background);
// don't automatically update the caret. that way we can manually decide
// whether or not to do so based on the autoscroll checkbox.
((DefaultCaret)textArea.getCaret()).setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
scrollPane = new JScrollPane(textArea);
getContentPane().add(scrollPane, BorderLayout.CENTER);
JPanel pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
pane.setBorder(new EmptyBorder(4, 4, 4, 4));
textField = new JTextField(40);
textField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send(textField.getText());
textField.setText("");
}});
sendButton = new JButton("Send");
sendButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send(textField.getText());
textField.setText("");
}});
pane.add(textField);
pane.add(Box.createRigidArea(new Dimension(4, 0)));
pane.add(sendButton);
getContentPane().add(pane, BorderLayout.NORTH);
pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
pane.setBorder(new EmptyBorder(4, 4, 4, 4));
autoscrollBox = new JCheckBox("Autoscroll", true);
lineEndings = new JComboBox(new String[] { "No line ending", "Newline", "Carriage return", "Both NL & CR" });
lineEnding = Preferences.get("serial.debug_line_end");
lineEndings.setSelectedItem(lineEnding);
lineEndings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
String wholeString = (String) lineEndings.getSelectedItem();
Preferences.set("serial.debug_line_end", wholeString);
}});
lineEndings.setMaximumSize(lineEndings.getMinimumSize());
String[] serialRateStrings = {
"300","1200","2400","4800","9600","14400",
"19200","28800","38400","57600","115200"
};
serialRates = new JComboBox();
for (int i = 0; i < serialRateStrings.length; i++)
serialRates.addItem(serialRateStrings[i] + " baud");
serialRate = Preferences.getInteger("serial.debug_rate");
serialRates.setSelectedItem(serialRate + " baud");
serialRates.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
if(speedDetection == true)
return;
String wholeString = (String) serialRates.getSelectedItem();
String rateString = wholeString.substring(0, wholeString.indexOf(' '));
serialRate = Integer.parseInt(rateString);
Preferences.set("serial.debug_rate", rateString);
closeSerialPort();
try {
openSerialPort();
} catch (SerialException e) {
//status.error("Error opening serial port '" + this.port + "'.");
System.err.println(e.getMessage());
//System.err.println(e);
}
}});
serialRates.setMaximumSize(serialRates.getMinimumSize());
JButton reset = new JButton("Reset Board");
reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
serial.setDTR(false);
serial.setRTS(false);
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
serial.setDTR(true);
serial.setRTS(true);
}
});
pane.add(reset);
pane.add(Box.createRigidArea(new Dimension(4, 0)));
pane.add(autoscrollBox);
pane.add(Box.createHorizontalGlue());
pane.add(lineEndings);
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(serialRates);
getContentPane().add(pane, BorderLayout.SOUTH);
Image image = Toolkit.getDefaultToolkit().createImage(PApplet.ICON_IMAGE);
setIconImage(image);
pack();
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
if (Preferences.get("last.screen.height") != null) {
// if screen size has changed, the window coordinates no longer
// make sense, so don't use them unless they're identical
int screenW = Preferences.getInteger("last.screen.width");
int screenH = Preferences.getInteger("last.screen.height");
if ((screen.width == screenW) && (screen.height == screenH)) {
String locationStr = Preferences.get("last.serial.location");
if (locationStr != null) {
int[] location = PApplet.parseInt(PApplet.split(locationStr, ','));
setPlacement(location);
}
}
}
}
protected void setPlacement(int[] location) {
setBounds(location[0], location[1], location[2], location[3]);
}
protected int[] getPlacement() {
int[] location = new int[4];
// Get the dimensions of the Frame
Rectangle bounds = getBounds();
location[0] = bounds.x;
location[1] = bounds.y;
location[2] = bounds.width;
location[3] = bounds.height;
return location;
}
private void send(String s) {
if (serial != null) {
switch (lineEndings.getSelectedIndex()) {
case 1: s += "\n"; break;
case 2: s += "\r"; break;
case 3: s += "\r\n"; break;
}
serial.write(s);
echo(s);
}
}
public void openSerialPort(int detectedRate) throws SerialException {
if (serial != null) return;
serialRate = detectedRate;
speedDetection = true;
serialRates.setSelectedItem(serialRate + " baud");
speedDetection = false;
serial = new Serial(port, serialRate);
serial.addListener(this);
}
public void openSerialPort() throws SerialException {
if (serial != null) return;
serial = new Serial(port, serialRate);
serial.addListener(this);
}
public void closeSerialPort() {
if (serial != null) {
int[] location = getPlacement();
String locationStr = PApplet.join(PApplet.str(location), ",");
Preferences.set("last.serial.location", locationStr);
textArea.setText("");
insertAt = 0;
serial.dispose();
serial = null;
}
}
public void message(final String s) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.insert(s,insertAt);
insertAt += s.length();
if (autoscrollBox.isSelected()) {
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}});
}
public void echo(final String s) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
String prnt = "\r\nsent: "+s+"\r\n";
textArea.insert(prnt,insertAt);
insertAt += prnt.length();
if (autoscrollBox.isSelected()) {
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}});
}
private String getDateTime() {
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
Date date = new Date();
return dateFormat.format(date);
}
class PopupListener extends MouseAdapter {
JPopupMenu popup;
PopupListener() {
popup = new JPopupMenu();
JMenuItem item;
item = new JMenuItem("Clear");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.setText("");
insertAt = 0;
}
});
popup.add(item);
popup.addSeparator();
item = new JMenuItem("Copy");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.selectAll();
textArea.copy();
}
});
popup.add(item);
}
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());
}
}
}
}