From 8057d581baef7f90c943dfc3c0aa708b680fece7 Mon Sep 17 00:00:00 2001 From: Simon B Date: Tue, 21 Dec 2021 22:40:41 +0000 Subject: [PATCH] #31 Added functionality to select and copy text in the serial monitor --- ProcessingGrapher/ColorSchemes.pde | 3 + ProcessingGrapher/ProcessingGrapher.pde | 7 +- ProcessingGrapher/SerialMonitor.pde | 169 +++++++++++++++++++++++- README.md | 2 + 4 files changed, 172 insertions(+), 9 deletions(-) diff --git a/ProcessingGrapher/ColorSchemes.pde b/ProcessingGrapher/ColorSchemes.pde index f1d7e86..189f3d7 100644 --- a/ProcessingGrapher/ColorSchemes.pde +++ b/ProcessingGrapher/ColorSchemes.pde @@ -30,6 +30,7 @@ void loadColorScheme(int mode) { c_alert_message_box = c_tabbar; c_info_message_box = color(229, 229, 229); c_status_bar = c_message_text; + c_highlight_background = c_tabbar; break; // Dark mode - One Dark Gravity @@ -57,6 +58,7 @@ void loadColorScheme(int mode) { c_alert_message_box = c_tabbar; c_info_message_box = c_tabbar; c_status_bar = c_terminal_text; + c_highlight_background = c_tabbar; break; // Dark mode - Monokai (default) @@ -85,6 +87,7 @@ void loadColorScheme(int mode) { c_alert_message_box = c_tabbar; c_info_message_box = c_darkgrey; c_status_bar = c_lightgrey; + c_highlight_background = c_tabbar; break; } diff --git a/ProcessingGrapher/ProcessingGrapher.pde b/ProcessingGrapher/ProcessingGrapher.pde index 3a7fc18..1bb7ab2 100644 --- a/ProcessingGrapher/ProcessingGrapher.pde +++ b/ProcessingGrapher/ProcessingGrapher.pde @@ -7,8 +7,8 @@ * @website https://wired.chillibasket.com/processing-grapher/ * * @copyright GNU General Public License v3 - * @date 5th December 2021 - * @version 1.3.4 + * @date 21st December 2021 + * @version 1.3.5 * * * * * * * * * * * * * * * * * * * * * * */ /* @@ -31,7 +31,7 @@ * along with this program. If not, see . */ -final String versionNumber = "1.3.4"; +final String versionNumber = "1.3.5"; // Swing for input popups import static javax.swing.JOptionPane.*; @@ -133,6 +133,7 @@ color c_message_box_outline; color c_alert_message_box; color c_info_message_box; color c_status_bar; +color c_highlight_background; // Serial Port Variables Serial myPort; diff --git a/ProcessingGrapher/SerialMonitor.pde b/ProcessingGrapher/SerialMonitor.pde index 1ec17a1..00ffa07 100644 --- a/ProcessingGrapher/SerialMonitor.pde +++ b/ProcessingGrapher/SerialMonitor.pde @@ -76,6 +76,8 @@ class SerialMonitor implements TabAPI { SerialMessages serialBuffer; //PGraphics serialGraphics; + SerialTextSelection textSelection = new SerialTextSelection(); + /** * Constructor @@ -326,6 +328,27 @@ class SerialMonitor implements TabAPI { text(">>", cR - 2*border, msgB + totalHeight); } + // If text is highlighted, draw the background + if (textSelection.valid) { + if (textSelection.startLine < textIndex && textSelection.endLine > textIndex) { + fill(c_highlight_background); + rect(cL + 2*border, msgB + totalHeight, textRow.length() * charWidth, yTextHeight); + } else if (textSelection.startLine == textIndex && textSelection.endLine == textIndex && textSelection.startChar < textRow.length()) { + fill(c_highlight_background); + int endChar = textSelection.endChar; + if (endChar > textRow.length()) endChar = textRow.length(); + rect(cL + 2*border + charWidth * textSelection.startChar, msgB + totalHeight, (endChar - textSelection.startChar) * charWidth, yTextHeight); + } else if (textSelection.startLine == textIndex && textSelection.endLine > textIndex && textSelection.startChar < textRow.length()) { + fill(c_highlight_background); + rect(cL + 2*border + charWidth * textSelection.startChar, msgB + totalHeight, (textRow.length() - textSelection.startChar) * charWidth, yTextHeight); + } else if (textSelection.startLine < textIndex && textSelection.endLine == textIndex) { + fill(c_highlight_background); + int endChar = textSelection.endChar; + if (endChar > textRow.length()) endChar = textRow.length(); + rect(cL + 2*border, msgB + totalHeight, endChar * charWidth, yTextHeight); + } + } + // Print the text fill(textColor); text(textRow, cL + 2*border, msgB + totalHeight);//, cR - cL - 3*border, yTextHeight); @@ -778,6 +801,10 @@ class SerialMonitor implements TabAPI { menuLevel = 0; menuScroll = 0; redrawUI = true; + } else if (textSelection.valid) { + textSelection.valid = false; + textSelection.active = false; + redrawContent = true; } break; case ENTER: @@ -949,10 +976,33 @@ class SerialMonitor implements TabAPI { break; case KeyEvent.VK_COPY: { - println("Copying: " + serialBuffer.get(serialBuffer.size() - 1)); - StringSelection selection = new StringSelection(serialBuffer.get(serialBuffer.size() - 1)); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(selection, selection); + + if (textSelection.valid) { + String copyText = ""; + + for (int i = textSelection.startLine; i <= textSelection.endLine; i++) { + if (textSelection.startLine == textSelection.endLine) { + copyText = serialBuffer.get(i); + copyText = copyText.substring(textSelection.startChar, textSelection.endChar); + } else if (i == textSelection.startLine) { + String tempString = serialBuffer.get(i); + copyText += tempString.substring(textSelection.startChar); + copyText += '\n'; + } else if (i == textSelection.endLine) { + String tempString = serialBuffer.get(i); + copyText += tempString.substring(0, textSelection.endChar); + } else { + copyText += serialBuffer.get(i); + copyText += '\n'; + } + } + + println("Copying: " + copyText); + StringSelection selection = new StringSelection(copyText); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(selection, selection); + } + break; } @@ -1009,13 +1059,49 @@ class SerialMonitor implements TabAPI { autoScroll = true; redrawUI = true; redrawContent = true; + return; } } // Click on serial monitor scrollbar if ((scrollUp != -1) && serialScroll.click(xcoord, ycoord)) { sidebarScroll.active(false); + textSelection.active = false; + startScrolling(true); + return; + } + + // Text selection in message area + if ((ycoord > msgB) && (ycoord < cB - border*2)) { + sidebarScroll.active(false); + serialScroll.active(false); + // Figure out where in the serial messages was clicked!!! + int selectedLine = displayRows - ((ycoord - msgB) / yTextHeight); + if (selectedLine > displayRows) selectedLine = displayRows; + + int textIndex = (serialBuffer.size() - selectedLine - scrollUp); + if (textIndex < 0) textIndex = 0; + String textRow = serialBuffer.get(textIndex); + + textFont(mono_font); + final float charWidth = textWidth("a"); + textFont(base_font); + + final int maxChars = floor((cR - 2*cL - 5*border) / charWidth); + int selectedChar = int((xcoord - (cL + 2*border)) / charWidth); + if (selectedChar < 0) selectedChar = 0; + else if (selectedChar > maxChars) selectedChar = maxChars; + + println(textIndex); + println(selectedChar); + textSelection.startLine = textIndex; + textSelection.startChar = selectedChar; + textSelection.endLine = textIndex; + textSelection.endChar = selectedChar; + textSelection.active = true; + textSelection.valid = false; startScrolling(true); + redrawContent = true; } } @@ -1047,6 +1133,7 @@ class SerialMonitor implements TabAPI { // Click on sidebar menu scroll bar if ((menuScroll != -1) && sidebarScroll.click(xcoord, ycoord)) { serialScroll.active(false); + textSelection.active = false; startScrolling(false); } @@ -1310,12 +1397,68 @@ class SerialMonitor implements TabAPI { redrawUI = true; } } - if (sidebarScroll.active()) { + + else if (sidebarScroll.active()) { int previousScroll = menuScroll; menuScroll = sidebarScroll.move(xcoord, ycoord, menuScroll, 0, menuHeight - (height - cT)); if (previousScroll != menuScroll) redrawUI = true; } - } + + else if (textSelection.active) { + // Figure out where in the serial messages was clicked!!! + int selectedLine = displayRows - ((ycoord - msgB) / yTextHeight); + if (selectedLine > displayRows) { + if (scrollUp < serialBuffer.size() - displayRows) { + scrollUp++; + selectedLine = displayRows + 1; + } else { + selectedLine = displayRows; + } + } else if (selectedLine < 0) { + if (scrollUp > 0) { + scrollUp--; + selectedLine = 0; + } else { + selectedLine = 0; + } + } + + int textIndex = (serialBuffer.size() - selectedLine - scrollUp); + if (textIndex < 0) textIndex = 0; + String textRow = serialBuffer.get(textIndex); + + textFont(mono_font); + final float charWidth = textWidth("a"); + textFont(base_font); + + final int maxChars = floor((cR - 2*cL - 5*border) / charWidth); + int selectedChar = int((xcoord - (cL + 2*border)) / charWidth) + 1; + if (selectedChar < 0) selectedChar = 0; + else if (selectedChar > maxChars) selectedChar = maxChars; + + if (!textSelection.inverted && (textIndex < textSelection.startLine || (textIndex == textSelection.startLine && selectedChar < textSelection.startChar))) { + textSelection.inverted = true; + textSelection.endLine = textSelection.startLine; + textSelection.endChar = textSelection.startChar; + } else if (textSelection.inverted && (textIndex > textSelection.endLine || (textIndex == textSelection.endLine && selectedChar > textSelection.endChar))) { + textSelection.inverted = false; + textSelection.startLine = textSelection.endLine; + textSelection.startChar = textSelection.endChar; + } + + if (textSelection.inverted) { + textSelection.startLine = textIndex; + textSelection.startChar = selectedChar; + } else { + textSelection.endLine = textIndex; + textSelection.endChar = selectedChar; + } + + textSelection.active = true; + textSelection.valid = true; + redrawContent = true; + } + } /** @@ -1358,6 +1501,20 @@ class SerialMonitor implements TabAPI { } + /** + * Data structure to store info related to selected/highlighted text + */ + class SerialTextSelection { + public int startLine = 0; + public int startChar = 0; + public int endLine = 0; + public int endChar = 0; + public boolean active = false; + public boolean valid = false; + public boolean inverted = false; + } + + /** * Data structure to store serial messages and other related info */ diff --git a/README.md b/README.md index abb9761..c9337f8 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ A full set of instructions and documentation can be found on my website at: [htt
## Changelog +1. (21st December 2021) Version 1.3.5 + 1. ([#31](https://github.com/chillibasket/processing-grapher/issues/31)) Implemented serial monitor text selection/highlighting. The selected text can also be copied using the `CTRL-C` keyboard combination. 1. (15th December 2021) Version 1.3.4 1. ([#28](https://github.com/chillibasket/processing-grapher/issues/28)) Added option to automatically scale the live graph both in and out depending on visible data. 2. ([#30](https://github.com/chillibasket/processing-grapher/issues/30)) Rearranged Pause/Play button on "Live Graph" tab and added button to clear all graphs.