void ExtendedTableWidget::cellClicked(const QModelIndex& index) { // If Ctrl-Shift is pressed try to jump to the row referenced by the foreign key of the clicked cell if(qApp->keyboardModifiers().testFlag(Qt::ControlModifier) && qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && model()) { SqliteTableModel* m = qobject_cast<SqliteTableModel*>(model()); sqlb::ForeignKeyClause fk = m->getForeignKeyClause(index.column()-1); if(fk.isSet()) emit foreignKeyClicked(sqlb::ObjectIdentifier(m->currentTableName().schema(), fk.table()), fk.columns().size() ? fk.columns().at(0) : "", m->data(index, Qt::EditRole).toByteArray()); } }
void ExtendedTableWidget::copy() { QModelIndexList indices = selectionModel()->selectedIndexes(); // Abort if there's nothing to copy if (indices.isEmpty()) return; qSort(indices); SqliteTableModel* m = qobject_cast<SqliteTableModel*>(model()); // If a single cell is selected, copy it to clipboard if (indices.size() == 1) { QImage img; QVariant data = m->data(indices.first(), Qt::EditRole); if (img.loadFromData(data.toByteArray())) { // If it's an image qApp->clipboard()->setImage(img); return; } else { qApp->clipboard()->setText(data.toString()); return; } } m_buffer.clear(); // If any of the cells contain binary data - we use inner buffer bool containsBinary = false; foreach (const QModelIndex& index, indices) if (m->isBinary(index)) { containsBinary = true; break; } if (containsBinary) { qApp->clipboard()->clear(); // Copy selected data into inner buffer int columns = indices.last().column() - indices.first().column() + 1; while (!indices.isEmpty()) { QByteArrayList lst; for (int i = 0; i < columns; ++i) { lst << indices.first().data(Qt::EditRole).toByteArray(); indices.pop_front(); } m_buffer.push_back(lst); } return; } QModelIndex first = indices.first(); QString result; int currentRow = 0; foreach(const QModelIndex& index, indices) { if (first == index) { /* first index */ } else if (index.row() != currentRow) result.append("\r\n"); else result.append("\t"); currentRow = index.row(); QVariant data = index.data(Qt::EditRole); // non-NULL data is enquoted, whilst NULL isn't if (!data.isNull()) { QString text = data.toString(); text.replace("\"", "\"\""); result.append(QString("\"%1\"").arg(text)); } } qApp->clipboard()->setText(result); }
void ExtendedTableWidget::copy(const bool withHeaders) { QModelIndexList indices = selectionModel()->selectedIndexes(); // Remove all indices from hidden columns, because if we don't we might copy data from hidden columns as well which is very // unintuitive; especially copying the rowid column when selecting all columns of a table is a problem because pasting the data // won't work as expected. QMutableListIterator<QModelIndex> i(indices); while (i.hasNext()) { if (isColumnHidden(i.next().column())) i.remove(); } // Abort if there's nothing to copy if (indices.isEmpty()) return; SqliteTableModel* m = qobject_cast<SqliteTableModel*>(model()); // Clear internal copy-paste buffer m_buffer.clear(); // If a single cell is selected, copy it to clipboard if (!withHeaders && indices.size() == 1) { QImage img; QVariant data = m->data(indices.first(), Qt::EditRole); if (img.loadFromData(data.toByteArray())) { // If it's an image, copy the image data to the clipboard qApp->clipboard()->setImage(img); return; } else { // It it's not an image, check if it's an empty field if (data.toByteArray().isEmpty()) { // The field is either NULL or empty. Those are are handled via the internal copy-paste buffer qApp->clipboard()->setText(QString()); // Calling clear() alone doesn't seem to work on all systems qApp->clipboard()->clear(); m_buffer.push_back(QByteArrayList{data.toByteArray()}); return; } // The field isn't empty. Copy the text to the clipboard without quoting (for general plain text clipboard) qApp->clipboard()->setText(data.toByteArray()); return; } } // If we got here, there are multiple selected cells, or copy with headers was requested. // In this case, we copy selected data into internal copy-paste buffer and then // we write a table both in HTML and text formats to the system clipboard. // Copy selected data into internal copy-paste buffer int last_row = indices.first().row(); QByteArrayList lst; for(int i=0;i<indices.size();i++) { if(indices.at(i).row() != last_row) { m_buffer.push_back(lst); lst.clear(); } lst << indices.at(i).data(Qt::EditRole).toByteArray(); last_row = indices.at(i).row(); } m_buffer.push_back(lst); QString result; QString htmlResult = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"; htmlResult.append("<html><head><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">"); htmlResult.append("<title></title>"); // The generator-stamp is later used to know whether the data in the system clipboard is still ours. // In that case we will give precedence to our internal copy buffer. QString now = QDateTime::currentDateTime().toString("YYYY-MM-DDTHH:mm:ss.zzz"); m_generatorStamp = QString("<meta name=\"generator\" content=\"%1\"><meta name=\"date\" content=\"%2\">").arg(QApplication::applicationName().toHtmlEscaped(), now); htmlResult.append(m_generatorStamp); // TODO: is this really needed by Excel, since we use <pre> for multi-line cells? htmlResult.append("<style type=\"text/css\">br{mso-data-placement:same-cell;}</style></head><body><table>"); int currentRow = indices.first().row(); const QString fieldSepHtml = "</td><td>"; const QString rowSepHtml = "</td></tr><tr><td>"; const QString fieldSepText = "\t"; const QString rowSepText = "\r\n"; // Table headers if (withHeaders) { htmlResult.append("<tr><th>"); int firstColumn = indices.front().column(); for(int i = firstColumn; i <= indices.back().column(); i++) { QByteArray headerText = model()->headerData(i, Qt::Horizontal, Qt::DisplayRole).toByteArray(); if (i != firstColumn) { result.append(fieldSepText); htmlResult.append("</th><th>"); } result.append(escapeCopiedData(headerText)); htmlResult.append(headerText); } result.append(rowSepText); htmlResult.append("</th></tr>"); } // Table data rows for(const QModelIndex& index : indices) { // Separators. For first cell, only opening table row tags must be added for the HTML and nothing for the text version. if (indices.first() == index) htmlResult.append("<tr><td>"); else if (index.row() != currentRow) { result.append(rowSepText); htmlResult.append(rowSepHtml); } else { result.append(fieldSepText); htmlResult.append(fieldSepHtml); } currentRow = index.row(); QImage img; QVariant data = index.data(Qt::EditRole); // Table cell data: image? Store it as an embedded image in HTML and as base 64 in text version if (img.loadFromData(data.toByteArray())) { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); img.save(&buffer, "PNG"); buffer.close(); QString imageBase64 = ba.toBase64(); htmlResult.append("<img src=\"data:image/png;base64,"); htmlResult.append(imageBase64); result.append(QString()); htmlResult.append("\" alt=\"Image\">"); } else { QByteArray text; if (!m->isBinary(index)) text = data.toByteArray(); // Table cell data: text if (text.contains('\n') || text.contains('\t')) htmlResult.append("<pre>" + QString(text).toHtmlEscaped() + "</pre>"); else htmlResult.append(QString(text).toHtmlEscaped()); result.append(escapeCopiedData(text)); } } QMimeData *mimeData = new QMimeData; mimeData->setHtml(htmlResult + "</td></tr></table></body></html>"); mimeData->setText(result); qApp->clipboard()->setMimeData(mimeData); }