Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1294f86

Browse files
committed
Add clickable HTML view of Serial Monitor
The HTML view only activates if: - the output is steady - the "frame" contains a link - the length of the entire content is < 1KB No performance penalty compared to normal view (in standard conditions)
1 parent 65c250b commit 1294f86

File tree

3 files changed

+259
-1
lines changed

3 files changed

+259
-1
lines changed

‎app/src/processing/app/AbstractTextMonitor.java

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import javax.swing.SwingUtilities;
2222
import javax.swing.border.EmptyBorder;
2323
import javax.swing.text.DefaultCaret;
24+
import javax.swing.event.UndoableEditListener;
25+
import javax.swing.text.AbstractDocument;
26+
import javax.swing.text.Document;
2427

2528
import cc.arduino.packages.BoardPort;
2629

@@ -29,12 +32,18 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
2932

3033
protected JLabel noLineEndingAlert;
3134
protected TextAreaFIFO textArea;
35+
protected HTMLTextAreaFIFO htmlTextArea;
3236
protected JScrollPane scrollPane;
37+
protected JScrollPane htmlScrollPane;
3338
protected JTextField textField;
3439
protected JButton sendButton;
3540
protected JCheckBox autoscrollBox;
3641
protected JComboBox lineEndings;
3742
protected JComboBox serialRates;
43+
protected Container mainPane;
44+
private long lastMessage;
45+
private javax.swing.Timer updateTimer;
46+
private boolean htmlView = true;
3847

3948
public AbstractTextMonitor(BoardPort boardPort) {
4049
super(boardPort);
@@ -45,6 +54,7 @@ protected void onCreateWindow(Container mainPane) {
4554
Font editorFont = PreferencesData.getFont("editor.font");
4655
Font font = Theme.scale(new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize()));
4756

57+
this.mainPane = mainPane;
4858
mainPane.setLayout(new BorderLayout());
4959

5060
textArea = new TextAreaFIFO(8000000);
@@ -53,13 +63,89 @@ protected void onCreateWindow(Container mainPane) {
5363
textArea.setEditable(false);
5464
textArea.setFont(font);
5565

66+
htmlTextArea = new HTMLTextAreaFIFO(8000000);
67+
htmlTextArea.setEditable(false);
68+
htmlTextArea.setFont(font);
69+
htmlTextArea.setOpaque(false);
70+
5671
// don't automatically update the caret. that way we can manually decide
5772
// whether or not to do so based on the autoscroll checkbox.
5873
((DefaultCaret) textArea.getCaret()).setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
74+
((DefaultCaret) htmlTextArea.getCaret()).setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
75+
76+
Document doc = textArea.getDocument();
77+
if (doc instanceof AbstractDocument)
78+
{
79+
UndoableEditListener[] undoListeners =
80+
( (AbstractDocument) doc).getUndoableEditListeners();
81+
if (undoListeners.length > 0)
82+
{
83+
for (UndoableEditListener undoListener : undoListeners)
84+
{
85+
doc.removeUndoableEditListener(undoListener);
86+
}
87+
}
88+
}
89+
90+
doc = htmlTextArea.getDocument();
91+
if (doc instanceof AbstractDocument)
92+
{
93+
UndoableEditListener[] undoListeners =
94+
( (AbstractDocument) doc).getUndoableEditListeners();
95+
if (undoListeners.length > 0)
96+
{
97+
for (UndoableEditListener undoListener : undoListeners)
98+
{
99+
doc.removeUndoableEditListener(undoListener);
100+
}
101+
}
102+
}
59103

60104
scrollPane = new JScrollPane(textArea);
105+
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
106+
htmlScrollPane = new JScrollPane(htmlTextArea);
107+
htmlScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
108+
109+
ActionListener checkIfSteady = new ActionListener() {
110+
public void actionPerformed(ActionEvent evt) {
111+
if (System.currentTimeMillis() - lastMessage > 200) {
112+
if (htmlView == false && textArea.getLength() < 1000) {
113+
114+
htmlTextArea.setText("");
115+
boolean res = htmlTextArea.append(textArea.getText());
116+
if (res) {
117+
htmlView = true;
118+
mainPane.remove(scrollPane);
119+
if (textArea.getCaretPosition() > htmlTextArea.getDocument().getLength()) {
120+
htmlTextArea.setCaretPosition(htmlTextArea.getDocument().getLength());
121+
} else {
122+
htmlTextArea.setCaretPosition(textArea.getCaretPosition());
123+
}
124+
mainPane.add(htmlScrollPane, BorderLayout.CENTER);
125+
scrollPane.setVisible(false);
126+
mainPane.validate();
127+
mainPane.repaint();
128+
}
129+
}
130+
} else {
131+
if (htmlView == true) {
132+
htmlView = false;
133+
mainPane.remove(htmlScrollPane);
134+
mainPane.add(scrollPane, BorderLayout.CENTER);
135+
scrollPane.setVisible(true);
136+
mainPane.validate();
137+
mainPane.repaint();
138+
}
139+
}
140+
}
141+
};
142+
143+
updateTimer = new javax.swing.Timer(33, checkIfSteady);
61144

62145
mainPane.add(scrollPane, BorderLayout.CENTER);
146+
147+
htmlTextArea.setVisible(true);
148+
htmlScrollPane.setVisible(true);
63149

64150
JPanel upperPane = new JPanel();
65151
upperPane.setLayout(new BoxLayout(upperPane, BoxLayout.X_AXIS));
@@ -115,17 +201,24 @@ public void actionPerformed(ActionEvent event) {
115201
pane.add(serialRates);
116202

117203
mainPane.add(pane, BorderLayout.SOUTH);
204+
205+
updateTimer.start();
118206
}
119207

120208
protected void onEnableWindow(boolean enable)
121209
{
122210
textArea.setEnabled(enable);
211+
htmlTextArea.setEnabled(enable);
123212
scrollPane.setEnabled(enable);
213+
htmlScrollPane.setEnabled(enable);
124214
textField.setEnabled(enable);
125215
sendButton.setEnabled(enable);
126216
autoscrollBox.setEnabled(enable);
127217
lineEndings.setEnabled(enable);
128218
serialRates.setEnabled(enable);
219+
if (enable == false) {
220+
htmlTextArea.setText("");
221+
}
129222
}
130223

131224
public void onSendCommand(ActionListener listener) {
@@ -136,8 +229,9 @@ public void onSendCommand(ActionListener listener) {
136229
public void onSerialRateChange(ActionListener listener) {
137230
serialRates.addActionListener(listener);
138231
}
139-
232+
140233
public void message(final String s) {
234+
lastMessage = System.currentTimeMillis();
141235
SwingUtilities.invokeLater(new Runnable() {
142236
public void run() {
143237
textArea.append(s);
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
Copyright (c) 2014 Paul Stoffregen <paul@pjrc.com>
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program; if not, write to the Free Software Foundation,
16+
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+
*/
18+
19+
// adapted from https://community.oracle.com/thread/1479784
20+
21+
package processing.app;
22+
23+
import java.io.IOException;
24+
import java.net.URL;
25+
import java.awt.Desktop;
26+
import java.net.URLEncoder;
27+
28+
import java.util.*;
29+
import java.util.regex.*;
30+
31+
import javax.swing.text.html.HTMLDocument;
32+
import javax.swing.JEditorPane;
33+
import javax.swing.JTextPane;
34+
import javax.swing.SwingUtilities;
35+
import javax.swing.event.HyperlinkEvent;
36+
import javax.swing.event.HyperlinkListener;
37+
import javax.swing.event.DocumentEvent;
38+
import javax.swing.event.DocumentListener;
39+
import javax.swing.text.BadLocationException;
40+
import javax.swing.text.html.HTMLEditorKit;
41+
42+
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
43+
44+
public class HTMLTextAreaFIFO extends JTextPane implements DocumentListener {
45+
private int maxChars;
46+
private int trimMaxChars;
47+
48+
private int updateCount; // limit how often we trim the document
49+
50+
private boolean doTrim;
51+
private final HTMLEditorKit kit;
52+
53+
public HTMLTextAreaFIFO(int max) {
54+
maxChars = max;
55+
trimMaxChars = max / 2;
56+
updateCount = 0;
57+
doTrim = true;
58+
setContentType("text/html");
59+
getDocument().addDocumentListener(this);
60+
setText("");
61+
kit = new HTMLEditorKit();
62+
this.addHyperlinkListener(new UpdatableBoardsLibsFakeURLsHandler(Base.INSTANCE));
63+
}
64+
65+
public void insertUpdate(DocumentEvent e) {
66+
}
67+
68+
public void removeUpdate(DocumentEvent e) {
69+
}
70+
71+
public void changedUpdate(DocumentEvent e) {
72+
}
73+
74+
public void trimDocument() {
75+
int len = 0;
76+
len = getDocument().getLength();
77+
if (len > trimMaxChars) {
78+
int n = len - trimMaxChars;
79+
//System.out.println("trimDocument: remove " + n + " chars");
80+
try {
81+
getDocument().remove(0, n);
82+
} catch (BadLocationException ble) {
83+
}
84+
}
85+
}
86+
87+
private static List<String> extractUrls(String input) {
88+
List<String> result = new ArrayList<String>();
89+
90+
Pattern pattern = Pattern.compile(
91+
"(http|ftp|https)://([^\\s]+)");
92+
93+
Matcher matcher = pattern.matcher(input);
94+
while (matcher.find()) {
95+
result.add(matcher.group());
96+
}
97+
98+
return result;
99+
}
100+
101+
static public final String WITH_DELIMITER = "((?<=%1$s)|(?=%1$s))";
102+
103+
public boolean append(String s) {
104+
boolean htmlFound = false;
105+
try {
106+
HTMLDocument doc = (HTMLDocument) getDocument();
107+
108+
String strings[] = s.split(String.format(WITH_DELIMITER, "\\r?\\n"));
109+
110+
for (int l = 0; l < strings.length; l++) {
111+
String str = strings[l];
112+
List<String> urls = extractUrls(str);
113+
114+
if (urls.size() > 0) {
115+
116+
for (int i = 0; i < urls.size(); i++) {
117+
if (!((urls.get(i)).contains("</a>"))) {
118+
str = str.replace(urls.get(i), "<a href='" + urls.get(i) + "'>" + urls.get(i) + "</a>");
119+
}
120+
}
121+
122+
kit.insertHTML(doc, doc.getLength(), str, 0, 0, null);
123+
htmlFound = true;
124+
} else {
125+
doc.insertString(doc.getLength(), str, null);
126+
}
127+
}
128+
} catch(BadLocationException exc) {
129+
exc.printStackTrace();
130+
} catch(IOException exc) {
131+
exc.printStackTrace();
132+
}
133+
134+
if (++updateCount > 150 && doTrim) {
135+
updateCount = 0;
136+
SwingUtilities.invokeLater(new Runnable() {
137+
public void run() {
138+
trimDocument();
139+
}
140+
});
141+
}
142+
return htmlFound;
143+
}
144+
145+
public void appendNoTrim(String s) {
146+
int free = maxChars - getDocument().getLength();
147+
if (free <= 0)
148+
return;
149+
if (s.length() > free)
150+
append(s.substring(0, free));
151+
else
152+
append(s);
153+
doTrim = false;
154+
}
155+
156+
public void appendTrim(String str) {
157+
append(str);
158+
doTrim = true;
159+
}
160+
}

‎app/src/processing/app/TextAreaFIFO.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ public void trimDocument() {
7272
}
7373
}
7474

75+
public int getLength() {
76+
return getDocument().getLength();
77+
}
78+
7579
public void appendNoTrim(String s) {
7680
int free = maxChars - getDocument().getLength();
7781
if (free <= 0)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /