From c4b77468fdb7b49657deaa6cc6af7d3293bc812f Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Sat, 2 Nov 2024 12:43:28 +0100 Subject: [PATCH] =?UTF-8?q?Nefunk=C4=8Dn=C3=AD=20copy&paste=20v=20z=C3=A1l?= =?UTF-8?q?o=C5=BEce=20=C3=9Aseky/Etapy=20#969?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../src/framework/logwidget.cpp | 2 +- .../src/internal/dlgtableviewcopyspecial.cpp | 9 +- .../src/internal/dlgtableviewcopyspecial.h | 4 +- .../src/internal/dlgtableviewcopyspecial.ui | 30 ++-- libqf/libqfqmlwidgets/src/tableview.cpp | 137 +++++++++--------- libqf/libqfqmlwidgets/src/tableview.h | 6 +- 7 files changed, 98 insertions(+), 92 deletions(-) diff --git a/.gitignore b/.gitignore index 950a76eb7..3a95cae8b 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ Makefile* *.qmlproject.user.* _work/* +build +doc/book diff --git a/libqf/libqfqmlwidgets/src/framework/logwidget.cpp b/libqf/libqfqmlwidgets/src/framework/logwidget.cpp index bd4ab41ff..bea4c92ad 100644 --- a/libqf/libqfqmlwidgets/src/framework/logwidget.cpp +++ b/libqf/libqfqmlwidgets/src/framework/logwidget.cpp @@ -43,7 +43,7 @@ LogWidgetTableView::LogWidgetTableView(QWidget *parent) void LogWidgetTableView::copy() { qfLogFuncFrame(); - qf::qmlwidgets::TableView::copySelectionToClipboard(this); + TableView::copySelectionToClipboard(this); } class LogFilterProxyModel : public QSortFilterProxyModel diff --git a/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.cpp b/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.cpp index f71bc9821..b75d0204d 100644 --- a/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.cpp +++ b/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.cpp @@ -8,6 +8,11 @@ DlgTableViewCopySpecial::DlgTableViewCopySpecial(QWidget *parent) { ui = new Ui::DlgTableViewCopySpecial; ui->setupUi(this); + + ui->lstReplaceEscapes->addItem(tr("Quote field if needed"), static_cast(TableView::ReplaceEscapes::QuoteIfNeeded)); + ui->lstReplaceEscapes->addItem(tr("Never"), static_cast(TableView::ReplaceEscapes::Never)); + ui->lstReplaceEscapes->addItem(tr("Always"), static_cast(TableView::ReplaceEscapes::Always)); + ui->lstReplaceEscapes->setCurrentIndex(0); } DlgTableViewCopySpecial::~DlgTableViewCopySpecial() @@ -30,8 +35,8 @@ QString DlgTableViewCopySpecial::rowsSeparator() return ui->edRowsSeparator->text(); } -bool DlgTableViewCopySpecial::replaceEscapes() +qf::qmlwidgets::TableView::ReplaceEscapes DlgTableViewCopySpecial::replaceEscapes() { - return ui->chkReplaceEscapes->isChecked(); + return static_cast(ui->lstReplaceEscapes->currentData().toInt()); } diff --git a/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.h b/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.h index a3bc13d05..753d1cab8 100644 --- a/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.h +++ b/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.h @@ -3,6 +3,8 @@ #include +#include "../tableview.h" + namespace qf { namespace qmlwidgets { namespace internal { @@ -21,7 +23,7 @@ class DlgTableViewCopySpecial : public QDialog QString fieldsSeparator(); QString rowsSeparator(); QString fieldsQuotes(); - bool replaceEscapes(); + qf::qmlwidgets::TableView::ReplaceEscapes replaceEscapes(); private: Ui::DlgTableViewCopySpecial *ui; }; diff --git a/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.ui b/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.ui index 203c7d650..bf623471b 100644 --- a/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.ui +++ b/libqf/libqfqmlwidgets/src/internal/dlgtableviewcopyspecial.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 140 + 404 + 181 @@ -37,17 +37,17 @@ - - + + - fields quotes + \n - - + + - \n + fields quotes @@ -58,25 +58,25 @@ - - - - Replace nonprintable characters with escape sequencies - + + replace escapes + + + - Qt::Vertical + Qt::Orientation::Vertical - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok diff --git a/libqf/libqfqmlwidgets/src/tableview.cpp b/libqf/libqfqmlwidgets/src/tableview.cpp index 8bb76fb9e..a0e9f5326 100644 --- a/libqf/libqfqmlwidgets/src/tableview.cpp +++ b/libqf/libqfqmlwidgets/src/tableview.cpp @@ -144,44 +144,9 @@ QAbstractProxyModel* TableView::lastProxyModel() const return ret; } -void TableView::copySelectionToClipboard(QTableView *table_view) +void TableView::copySelectionToClipboard(const QTableView *table_view) { - qfLogFuncFrame(); - auto *m = table_view->model(); - if(!m) - return; - int n = 0; - QString rows; - QItemSelection sel = table_view->selectionModel()->selection(); - foreach(const QItemSelectionRange &sel1, sel) { - if(sel1.isValid()) { - for(int row=sel1.top(); row<=sel1.bottom(); row++) { - QString cells; - for(int col=sel1.left(); col<=sel1.right(); col++) { - QModelIndex ix = m->index(row, col); - QString s; - s = ix.data(Qt::DisplayRole).toString(); - static constexpr bool replace_escapes = true; - if(replace_escapes) { - s.replace('\r', QStringLiteral("\\r")); - s.replace('\n', QStringLiteral("\\n")); - s.replace('\t', QStringLiteral("\\t")); - } - if(col > sel1.left()) - cells += '\t'; - cells += s; - } - if(n++ > 0) - rows += '\n'; - rows += cells; - } - } - } - if(!rows.isEmpty()) { - //qfInfo() << "\tSetting clipboard:" << rows; - QClipboard *clipboard = QApplication::clipboard(); - clipboard->setText(rows); - } + copySpecial_helper(table_view, "\t", "\n", "", ReplaceEscapes::QuoteIfNeeded); } void TableView::setModel(QAbstractItemModel *model) @@ -537,14 +502,14 @@ int TableView::reloadRow(int row_no) void TableView::copy() { qfLogFuncFrame(); - copySpecial_helper("\t", "\n", QString(), true); + copySelectionToClipboard(); } void TableView::copySpecial() { internal::DlgTableViewCopySpecial dlg(this); if(dlg.exec()) { - copySpecial_helper(dlg.fieldsSeparator(), dlg.rowsSeparator(), dlg.fieldsQuotes(), dlg.replaceEscapes()); + copySpecial_helper(this, dlg.fieldsSeparator(), dlg.rowsSeparator(), dlg.fieldsQuotes(), dlg.replaceEscapes()); } } @@ -1974,48 +1939,78 @@ void TableView::createActions() } } -void TableView::copySpecial_helper(const QString &fields_separator, const QString &rows_separator, const QString &field_quotes, bool replace_escapes) +void TableView::copySpecial_helper(const QTableView *table_view, const QString &fields_separator, const QString &rows_separator, const QString &field_quotes, ReplaceEscapes replace_escapes) { qfLogFuncFrame(); - auto *m = model(); + auto *m = table_view->model(); if(!m) return; - int n = 0; - QString rows; - QItemSelection sel = selectionModel()->selection(); - foreach(QItemSelectionRange sel1, sel) { - //QItemSelectionRange sel1 = sel.value(0); - if(sel1.isValid()) { - //qfInfo() << "sel count:" << sel.count() << "sel range left:" << sel1.left() << "right:" << sel1.right() << "top:" << sel1.top() << "bottom:" << sel1.bottom(); - for(int row=sel1.top(); row<=sel1.bottom(); row++) { - QString cells; - for(int col=sel1.left(); col<=sel1.right(); col++) { - QModelIndex ix = m->index(row, col); - QString s; - if(!ix.data(qf::core::model::TableModel::ValueIsNullRole).toBool()) { - s = ix.data(Qt::DisplayRole).toString(); - if(s.isEmpty()) { - QVariant v = ix.data(Qt::CheckStateRole); - if(v.isValid()) { - s = (v.toInt() == Qt::Checked)? QStringLiteral("True"): QStringLiteral("False"); - } + auto selection_union = [](const QItemSelection &selection) { + QMap> res; + foreach(QItemSelectionRange sel, selection) { + for(int row = sel.top(); row <= sel.bottom(); row++) { + for(int col = sel.left(); col <= sel.right(); col++) { + auto &cols = res[row]; + if (auto lower = std::lower_bound(cols.begin(), cols.end(), col); lower == cols.end()) { + cols << col; + } + else { + if (*lower != col) { + cols.insert((lower - cols.begin()), col); } } - if(replace_escapes) { - s.replace('\r', QStringLiteral("\\r")); - s.replace('\n', QStringLiteral("\\n")); - s.replace('\t', QStringLiteral("\\t")); + } + } + } + return res; + }; + QString rows; + int row_cnt = 0; + auto sel = selection_union(table_view->selectionModel()->selection()); + QMapIterator> it(sel); + while (it.hasNext()) { + it.next(); + auto row = it.key(); + auto cols = it.value(); + int col_cnt = 0; + QString cells; + for (auto col : cols) { + QModelIndex ix = m->index(row, col); + QString s; + if(!ix.data(qf::core::model::TableModel::ValueIsNullRole).toBool()) { + s = ix.data(Qt::DisplayRole).toString(); + if(s.isEmpty()) { + QVariant v = ix.data(Qt::CheckStateRole); + if(v.isValid()) { + s = (v.toInt() == Qt::Checked)? QStringLiteral("True"): QStringLiteral("False"); } - //qfInfo() << ix.row() << '\t' << ix.column() << '\t' << s; - if(col > sel1.left()) - cells += fields_separator; - cells += (field_quotes + s + field_quotes); } - if(n++ > 0) - rows += rows_separator; - rows += cells; } + if(replace_escapes == ReplaceEscapes::Always) { + s.replace('\r', QStringLiteral("\\r")); + s.replace('\n', QStringLiteral("\\n")); + s.replace('\t', QStringLiteral("\\t")); + } + if(replace_escapes == ReplaceEscapes::QuoteIfNeeded) { + if (s.indexOf('\n') >= 0 + || s.indexOf('\r') >= 0 + || s.indexOf('\t') >= 0 + ) { + s = (field_quotes + s + field_quotes); + } + } + else { + s = (field_quotes + s + field_quotes); + } + //qfInfo() << "row:" << row << "col:" << col << "index:" << ix.row() << ',' << ix.column() << '\t' << s; + if(col_cnt++ > 0) { + cells += fields_separator; + } + cells += s; } + if(row_cnt++ > 0) + rows += rows_separator; + rows += cells; } if(!rows.isEmpty()) { qfDebug() << "\tSetting clipboard:" << rows; diff --git a/libqf/libqfqmlwidgets/src/tableview.h b/libqf/libqfqmlwidgets/src/tableview.h index 835d0918f..44955d0d2 100644 --- a/libqf/libqfqmlwidgets/src/tableview.h +++ b/libqf/libqfqmlwidgets/src/tableview.h @@ -158,7 +158,9 @@ class QFQMLWIDGETS_DECL_EXPORT TableView : public QTableView, public framework:: Q_SIGNAL void sqlException(const QString &what, const QString &where, const QString &stack_trace); - static void copySelectionToClipboard(QTableView *table_view); + enum class ReplaceEscapes {Never, Always, QuoteIfNeeded}; + static void copySelectionToClipboard(const QTableView *table_view); + void copySelectionToClipboard() const { copySelectionToClipboard(this); } Q_SLOT void loadPersistentSettings(); Q_SLOT void savePersistentSettings(); @@ -193,7 +195,7 @@ class QFQMLWIDGETS_DECL_EXPORT TableView : public QTableView, public framework:: virtual void createActions(); void setValueInSelection_helper(const QVariant &new_val); - void copySpecial_helper(const QString &fields_separator, const QString &rows_separator, const QString &field_quotes, bool replace_escapes); + static void copySpecial_helper(const QTableView *table_view, const QString &fields_separator, const QString &rows_separator, const QString &field_quotes, ReplaceEscapes replace_escapes); Q_SLOT void generateSequenceInSelection(); //static const int StandardContextMenuActionsGroups = AllActions & ~(SetValueActions | BlobActions | PasteActions);