diff --git a/assets/qml/Windows/Console.qml b/assets/qml/Windows/Console.qml
index f4ad7970..b3ab722a 100644
--- a/assets/qml/Windows/Console.qml
+++ b/assets/qml/Windows/Console.qml
@@ -41,6 +41,13 @@ Control {
//
property alias text: textArea.text
readonly property color consoleColor: "#8ecd9d"
+
+ //
+ // Hacks to allow context menu to work
+ //
+ property int curPos
+ property int selectEnd
+ property int selectStart
//
// Function to send through serial port data
@@ -62,6 +69,42 @@ Control {
property alias displayMode: displayModeCombo.currentIndex
}
+ //
+ // Shortcut to copy selection of console
+ //
+ Shortcut {
+ sequence: StandardKey.Copy
+ onActivated: textArea.copy()
+ }
+
+ //
+ // Right-click context menu
+ //
+ Menu {
+ id: contextMenu
+
+ MenuItem {
+ text: qsTr("Copy")
+ opacity: enabled ? 1 : 0.5
+ onTriggered: textArea.copy()
+ enabled: textArea.selectedText
+ }
+
+ MenuItem {
+ text: qsTr("Clear")
+ opacity: enabled ? 1 : 0.5
+ enabled: textArea.length > 0
+ onTriggered: Cpp_IO_Console.clear()
+ }
+
+ MenuItem {
+ opacity: enabled ? 1 : 0.5
+ text: qsTr("Save as") + "..."
+ onTriggered: Cpp_IO_Console.save()
+ enabled: Cpp_IO_Console.saveAvailable
+ }
+ }
+
//
// Controls
//
@@ -111,18 +154,54 @@ Control {
TextEdit {
id: textArea
+ focus: true
readOnly: true
font.pixelSize: 12
width: flick.width
height: flick.height
+ selectByMouse: true
+ selectByKeyboard: true
color: root.consoleColor
+ persistentSelection: true
wrapMode: TextEdit.NoWrap
font.family: app.monoFont
+ selectionColor: palette.highlight
+ selectedTextColor: palette.highlightedText
onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
onTextChanged: {
if (Cpp_IO_Console.autoscroll)
textArea.cursorPosition = textArea.length
}
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ acceptedButtons: Qt.RightButton
+
+ onClicked: {
+ root.selectStart = textArea.selectionStart;
+ root.selectEnd = textArea.selectionEnd;
+ root.curPos = textArea.cursorPosition;
+ contextMenu.x = mouse.x;
+ contextMenu.y = mouse.y;
+ contextMenu.open();
+ textArea.cursorPosition = root.curPos;
+ textArea.select(root.selectStart, root.selectEnd);
+ }
+
+ onPressAndHold: {
+ if (mouse.source === Qt.MouseEventNotSynthesized) {
+ root.selectStart = textArea.selectionStart;
+ root.selectEnd = textArea.selectionEnd;
+ root.curPos = textArea.cursorPosition;
+ contextMenu.x = mouse.x;
+ contextMenu.y = mouse.y;
+ contextMenu.open();
+ textArea.cursorPosition = root.curPos;
+ textArea.select(root.selectStart, root.selectEnd);
+ }
+ }
+ }
}
}
}
@@ -246,6 +325,18 @@ Control {
Button {
height: 24
+ Layout.maximumWidth: 32
+ icon.color: palette.text
+ opacity: enabled ? 1 : 0.5
+ onClicked: Cpp_IO_Console.save()
+ icon.source: "qrc:/icons/save.svg"
+ enabled: Cpp_IO_Console.saveAvailable
+ Behavior on opacity {NumberAnimation{}}
+ }
+
+ Button {
+ height: 24
+ Layout.maximumWidth: 32
icon.color: palette.text
opacity: enabled ? 1 : 0.5
enabled: textArea.length > 0
diff --git a/assets/qml/Windows/Setup.qml b/assets/qml/Windows/Setup.qml
index 33f911f6..215ee03d 100644
--- a/assets/qml/Windows/Setup.qml
+++ b/assets/qml/Windows/Setup.qml
@@ -186,7 +186,7 @@ Control {
//
Label {
opacity: enabled ? 1 : 0.5
- enabled: !Cpp_IO_Serial.connected
+ enabled: !Cpp_IO_Manager.connected
Behavior on opacity {NumberAnimation{}}
text: qsTr("COM Port") + ":"
} ComboBox {
@@ -200,7 +200,7 @@ Control {
}
opacity: enabled ? 1 : 0.5
- enabled: !Cpp_IO_Serial.connected
+ enabled: !Cpp_IO_Manager.connected
Behavior on opacity {NumberAnimation{}}
}
@@ -209,7 +209,6 @@ Control {
//
Label {
opacity: enabled ? 1 : 0.5
- enabled: !Cpp_IO_Serial.connected
Behavior on opacity {NumberAnimation{}}
text: qsTr("Baud Rate") + ":"
} ComboBox {
diff --git a/assets/translations/de.qm b/assets/translations/de.qm
index e8c0a270..54bd6dfe 100644
Binary files a/assets/translations/de.qm and b/assets/translations/de.qm differ
diff --git a/assets/translations/de.ts b/assets/translations/de.ts
index 18aff3e0..10c2c8a1 100644
--- a/assets/translations/de.ts
+++ b/assets/translations/de.ts
@@ -155,6 +155,18 @@
Show timestamp
Zeitstempel anzeigen
+
+ Copy
+ Kopieren
+
+
+ Clear
+ Löschen
+
+
+ Save as
+ Speichern als
+
CsvPlayer
@@ -459,6 +471,18 @@
Hexadecimal
Hexadezimal
+
+ Export console data
+ Konsolendaten exportieren
+
+
+ Text files
+ Textdateien
+
+
+ File save error
+ Fehler beim Speichern der Datei
+
IO::DataSources::Serial
diff --git a/assets/translations/en.qm b/assets/translations/en.qm
index 8f6f9ea0..555f764d 100644
Binary files a/assets/translations/en.qm and b/assets/translations/en.qm differ
diff --git a/assets/translations/en.ts b/assets/translations/en.ts
index 33eee682..40a8fa40 100644
--- a/assets/translations/en.ts
+++ b/assets/translations/en.ts
@@ -151,6 +151,18 @@
Show timestamp
+
+ Copy
+
+
+
+ Clear
+
+
+
+ Save as
+
+
CsvPlayer
@@ -396,6 +408,18 @@
Hexadecimal
+
+ Export console data
+
+
+
+ Text files
+
+
+
+ File save error
+
+
IO::DataSources::Serial
diff --git a/assets/translations/es.qm b/assets/translations/es.qm
index d1077f49..8049ddd1 100644
Binary files a/assets/translations/es.qm and b/assets/translations/es.qm differ
diff --git a/assets/translations/es.ts b/assets/translations/es.ts
index 763bf6c2..fec08ed3 100644
--- a/assets/translations/es.ts
+++ b/assets/translations/es.ts
@@ -159,6 +159,18 @@
Show timestamp
Marca de tiempo
+
+ Copy
+ Copiar
+
+
+ Clear
+ Limpiar
+
+
+ Save as
+ Guardar como
+
CsvPlayer
@@ -491,6 +503,18 @@
Hexadecimal
Hexadecimal
+
+ Export console data
+ Exportar datos de la consola
+
+
+ Text files
+ Archivos de texto
+
+
+ File save error
+ Error al intentar guardar el archivo
+
IO::DataSources::Serial
diff --git a/assets/translations/zh.qm b/assets/translations/zh.qm
index 519c3b89..819d1378 100644
Binary files a/assets/translations/zh.qm and b/assets/translations/zh.qm differ
diff --git a/assets/translations/zh.ts b/assets/translations/zh.ts
index a8f0bc76..0e5bbbb4 100644
--- a/assets/translations/zh.ts
+++ b/assets/translations/zh.ts
@@ -155,6 +155,18 @@
Show timestamp
显示时间戳
+
+ Copy
+ 复制
+
+
+ Clear
+ 删除
+
+
+ Save as
+ 另存为
+
CsvPlayer
@@ -487,6 +499,18 @@
Hexadecimal
十六进制
+
+ Export console data
+ 导出控制台数据
+
+
+ Text files
+ 文字档
+
+
+ File save error
+ 文件保存错误
+
IO::DataSources::Serial
diff --git a/src/IO/Console.cpp b/src/IO/Console.cpp
index 1b8e4c4e..c1a6477c 100644
--- a/src/IO/Console.cpp
+++ b/src/IO/Console.cpp
@@ -23,13 +23,21 @@
#include "Console.h"
#include "Manager.h"
+#include
#include
#include
+#include
+#include
#include
using namespace IO;
static Console *INSTANCE = nullptr;
+/**
+ * Set buffer size to 15 MB
+ */
+static const int MAX_BUFFER_SIZE = 1024 * 1024 * 15;
+
/**
* Constructor function
*/
@@ -45,6 +53,7 @@ Console::Console()
, m_cursor(nullptr)
, m_document(nullptr)
{
+ clear();
auto m = Manager::getInstance();
connect(m, &Manager::dataReceived, this, &Console::onDataReceived);
LOG_INFO() << "Class initialized";
@@ -79,6 +88,14 @@ bool Console::autoscroll() const
return m_autoscroll;
}
+/**
+ * Returns @c true if data buffer contains information that the user can export.
+ */
+bool Console::saveAvailable() const
+{
+ return m_dataBuffer.size() > 0;
+}
+
/**
* Returns @c true if a timestamp should be shown before each displayed data block.
*/
@@ -190,15 +207,49 @@ QStringList Console::displayModes() const
void Console::copy() { }
-void Console::save() { }
+/**
+ * Allows the user to export the information displayed on the console
+ */
+void Console::save()
+{
+ // No data buffer received, abort
+ if (!saveAvailable())
+ return;
+
+ // Get file name
+ auto path
+ = QFileDialog::getSaveFileName(Q_NULLPTR, tr("Export console data"),
+ QDir::homePath(), tr("Text files") + " (*.txt)");
+
+ // Create file
+ if (!path.isEmpty())
+ {
+ QFile file(path);
+ if (file.open(QFile::WriteOnly))
+ {
+ file.write(m_dataBuffer);
+ file.close();
+
+ Misc::Utilities::revealFile(path);
+ }
+
+ else
+ Misc::Utilities::showMessageBox(tr("File save error"), file.errorString());
+ }
+}
/**
* Deletes all the text displayed by the current QML text document
*/
void Console::clear()
{
+ // Clear console display
if (document())
document()->clear();
+
+ // Reserve 15 MB for data buffer
+ m_dataBuffer.clear();
+ m_dataBuffer.reserve(MAX_BUFFER_SIZE);
}
/**
@@ -383,10 +434,25 @@ void Console::setTextDocument(QQuickTextDocument *document)
* Displays the given @a data in the console. @c QByteArray to ~@c QString conversion is
* done by the @c dataToString() function, which displays incoming data either in UTF-8
* or in hexadecimal mode.
+ *
+ * The incoming data is also added to a buffer so that the user can save the file if
+ * needed.
*/
void Console::onDataReceived(const QByteArray &data)
{
+ // Display data
append(dataToString(data));
+
+ // Append data to buffer
+ m_dataBuffer.append(data);
+ if (m_dataBuffer.size() > MAX_BUFFER_SIZE)
+ {
+ m_dataBuffer.clear();
+ m_dataBuffer.reserve(MAX_BUFFER_SIZE);
+ }
+
+ // Notify UI
+ emit dataReceived();
}
/**
diff --git a/src/IO/Console.h b/src/IO/Console.h
index 8e690ef6..7802d8f4 100644
--- a/src/IO/Console.h
+++ b/src/IO/Console.h
@@ -48,6 +48,9 @@ class Console : public QObject
READ showTimestamp
WRITE setShowTimestamp
NOTIFY showTimestampChanged)
+ Q_PROPERTY(bool saveAvailable
+ READ saveAvailable
+ NOTIFY dataReceived)
Q_PROPERTY(IO::Console::DataMode dataMode
READ dataMode
WRITE setDataMode
@@ -67,6 +70,7 @@ class Console : public QObject
signals:
void echoChanged();
+ void dataReceived();
void dataModeChanged();
void autoscrollChanged();
void lineEndingChanged();
@@ -103,6 +107,7 @@ public:
bool echo() const;
bool autoscroll() const;
+ bool saveAvailable() const;
bool showTimestamp() const;
DataMode dataMode() const;
@@ -145,6 +150,7 @@ private:
private:
DataMode m_dataMode;
+ QByteArray m_dataBuffer;
LineEnding m_lineEnding;
DisplayMode m_displayMode;
diff --git a/src/IO/Manager.cpp b/src/IO/Manager.cpp
index ee483b19..958bd809 100644
--- a/src/IO/Manager.cpp
+++ b/src/IO/Manager.cpp
@@ -46,6 +46,7 @@ Manager::Manager()
, m_finishSequence("*/")
{
setWatchdogInterval(15);
+ setMaxBufferSize(1024 * 1024);
LOG_INFO() << "Class initialized";
}
@@ -310,6 +311,7 @@ void Manager::disconnectDevice()
m_device = nullptr;
m_receivedBytes = 0;
m_dataBuffer.clear();
+ m_dataBuffer.reserve(maxBufferSize());
// Update UI
emit deviceChanged();
@@ -351,6 +353,8 @@ void Manager::setMaxBufferSize(const int maxBufferSize)
{
m_maxBuzzerSize = maxBufferSize;
emit maxBufferSizeChanged();
+
+ m_dataBuffer.reserve(maxBufferSize);
}
/**