From a9e04080e92342e68c4421f6aa60376c4e433020 Mon Sep 17 00:00:00 2001 From: Martin Kleusberg Date: Mon, 4 Dec 2017 13:26:08 +0100 Subject: [PATCH] Clean up the code for pasting clipboard contents We have two different sources when pasting data into a table view: the system clipboard and the internal copy-paste buffer. Both work slightly different but most code is duplicated between them. This commit cleans up the pasting code by merging the two code paths into a single one. This does not only reduce the amount of duplicated code but also enables some features for both sources that were only available for one of the sources. This includes duplicating data by copying from a single cell to multiple cells and the message box asking for confirmation when the source and destination ranges do not match. These features were only available for the system clipboard before and should now work for both sources. --- src/ExtendedTableWidget.cpp | 121 ++++++++++++------------------------ src/ExtendedTableWidget.h | 1 - 2 files changed, 40 insertions(+), 82 deletions(-) diff --git a/src/ExtendedTableWidget.cpp b/src/ExtendedTableWidget.cpp index 7de8542a..71bbe69a 100644 --- a/src/ExtendedTableWidget.cpp +++ b/src/ExtendedTableWidget.cpp @@ -20,7 +20,7 @@ QList ExtendedTableWidget::m_buffer; namespace { -QList parseClipboard(QString clipboard) +QList parseClipboard(QString clipboard) { // Remove trailing line break from the clipboard text. This is necessary because some applications append an extra // line break to the clipboard contents which we would then interpret as regular data, setting the first field of the @@ -35,11 +35,11 @@ QList parseClipboard(QString clipboard) clipboard.chop(1); // Make sure there is some data in the clipboard - QList result; + QList result; if(clipboard.isEmpty()) return result; - result.push_back(QStringList()); + result.push_back(QByteArrayList()); QRegExp re("(\"(?:[^\t\"]+|\"\"[^\"]*\"\")*)\"|(\t|\r?\n)"); int offset = 0; @@ -51,7 +51,10 @@ QList parseClipboard(QString clipboard) if (pos < 0) { // insert everything that left text = clipboard.mid(whitespace_offset); - result.last().push_back(text); + if(QRegExp("\".*\"").exactMatch(text)) + text = text.mid(1, text.length() - 2); + text.replace("\"\"", "\""); + result.last().push_back(text.toUtf8()); break; } @@ -63,15 +66,18 @@ QList parseClipboard(QString clipboard) QString ws = re.cap(2); // if two whitespaces in row - that's an empty cell if (!(pos - whitespace_offset)) { - result.last().push_back(QString()); + result.last().push_back(QByteArray()); } else { text = clipboard.mid(whitespace_offset, pos - whitespace_offset); - result.last().push_back(text); + if(QRegExp("\".*\"").exactMatch(text)) + text = text.mid(1, text.length() - 2); + text.replace("\"\"", "\""); + result.last().push_back(text.toUtf8()); } if (ws.endsWith("\n")) // create new row - result.push_back(QStringList()); + result.push_back(QByteArrayList()); whitespace_offset = offset = pos + ws.length(); } @@ -365,70 +371,48 @@ void ExtendedTableWidget::paste() // Get the clipboard text QString clipboard = qApp->clipboard()->text(); - // If there is no text but the internal copy-paste buffer is filled, use the internal buffer - if (clipboard.isEmpty() && !m_buffer.isEmpty()) { - int rows = m_buffer.size(); - int columns = m_buffer.first().size(); - - int firstRow = indices.front().row(); - int firstColumn = indices.front().column(); - int lastRow = qMin(firstRow + rows - 1, m->rowCount() - 1); - int lastColumn = qMin(firstColumn + columns - 1, m->columnCount() - 1); - - int row = firstRow; - - // Copy the data as-is from the buffer to the table - for(const QByteArrayList& lst : m_buffer) { - int column = firstColumn; - for(const QByteArray& ba : lst) { - m->setData(m->index(row, column), ba); - - column++; - if (column > lastColumn) - break; - } - - row++; - if (row > lastRow) - break; - } - - return; + // If there is no text but the internal copy-paste buffer is filled, use the internal buffer; otherwise parse the system clipboard contents + QList clipboardTable; + QList* source; + if(clipboard.isEmpty() && !m_buffer.isEmpty()) + { + source = &m_buffer; + } else { + clipboardTable = parseClipboard(clipboard); + source = &clipboardTable; } - // There was some text in the system clipboard. So we need to parse that before we can paste it. - // TODO: It seems like we can tweak the parseClipboard() function to return data that's in the same format as the internal copy-paste buffer would be. - // This way we could reuse the code above and unify both code paths. On the other hand, the code for checking the selected paste range is totally - // different. So we would need to check whether there is any reason to use the code from below also for internal buffer pastes. - QList clipboardTable = parseClipboard(clipboard); - // Stop here if there's nothing to paste - if(!clipboardTable.size()) + if(!source->size()) return; // Starting from assumption that selection is rectangular, and then first index is upper-left corner and last is lower-right. - int clipboardRows = clipboardTable.size(); - int clipboardColumns = clipboardTable.front().size(); + int rows = source->size(); + int columns = source->first().size(); int firstRow = indices.front().row(); - int selectedRows = indices.back().row() - firstRow + 1; int firstColumn = indices.front().column(); + int selectedRows = indices.back().row() - firstRow + 1; int selectedColumns = indices.back().column() - firstColumn + 1; + // If last row and column are after table size, clamp it + int lastRow = qMin(firstRow + rows - 1, m->rowCount() - 1); + int lastColumn = qMin(firstColumn + columns - 1, m->columnCount() - 1); + // Special case: if there is only one cell of data to be pasted, paste it into all selected fields - if(clipboardRows == 1 && clipboardColumns == 1) + if(rows == 1 && columns == 1) { - QString data = clipboardTable.first().first(); + QByteArray data = source->first().first(); for(int row=firstRow;rowindex(row, column), data); + m->setData(m->index(row, column), data); } return; } // If more than one cell was selected, check if the selection matches the cliboard dimensions - if(selectedRows != clipboardRows || selectedColumns != clipboardColumns) + if(selectedRows != rows || selectedColumns != columns) { // Ask user if they are sure about this if(QMessageBox::question(this, QApplication::applicationName(), @@ -442,48 +426,23 @@ void ExtendedTableWidget::paste() // If we get here, we can definitely start pasting: either the ranges match in their size or the user agreed to paste anyway - // If last row and column are after table size, clamp it - int lastRow = qMin(firstRow + clipboardRows - 1, m->rowCount() - 1); - int lastColumn = qMin(firstColumn + clipboardColumns - 1, m->columnCount() - 1); - - // Paste the data cell by cell + // Copy the data cell by cell and as-is from the source buffer to the table int row = firstRow; - for(const QStringList& clipboardRow : clipboardTable) + for(const QByteArrayList& source_row : *source) { int column = firstColumn; - for(const QString& cell : clipboardRow) + for(const QByteArray& source_cell : source_row) { - setPasteData(m->index(row, column), cell); + m->setData(m->index(row, column), source_cell); column++; - if(column> lastColumn) - { + if (column > lastColumn) break; - } } row++; - if(row > lastRow) - { + if (row > lastRow) break; - } - } - -} - -void ExtendedTableWidget::setPasteData(const QModelIndex& idx, const QString& data) -{ - SqliteTableModel* m = qobject_cast(model()); - - if(data.isEmpty()) - { - m->setData(idx, QVariant()); - } else { - QString text = data; - if(QRegExp("\".*\"").exactMatch(text)) - text = text.mid(1, data.length() - 2); - text.replace("\"\"", "\""); - m->setData(idx, text); } } diff --git a/src/ExtendedTableWidget.h b/src/ExtendedTableWidget.h index f2d483da..ebd7e896 100644 --- a/src/ExtendedTableWidget.h +++ b/src/ExtendedTableWidget.h @@ -34,7 +34,6 @@ signals: private: void copy(const bool withHeaders = false); void paste(); - void setPasteData(const QModelIndex& idx, const QString& data); QString escapeCopiedData(QString data) const; void useAsFilter();