[graphics/okular] /: Fix Table Selection polluting clipboard history

David Hurka null at kde.org
Fri Dec 25 13:04:46 GMT 2020


Git commit 2917e73953a5f71c8a818c552f312a73d06ac406 by David Hurka, on behalf of Steven Chua.
Committed on 25/12/2020 at 13:04.
Pushed by davidhurka into branch 'master'.

Fix Table Selection polluting clipboard history

The Table Select tool no longer automatically copies its
contents to the clipboard, but only to the selection clipboard.
(Middle-click paste clipboard.) Instead, after creating
the table, the user can right-click to bring up a popup
menu with the action "Copy Table Contents to the Clipboard".
Clicking on this will copy the table contents to the clipboard.
Ctrl+C also works.

BUG: 402110

M  +6    -2    doc/index.docbook
M  +99   -71   part/pageview.cpp
M  +2    -0    part/pageview.h

https://invent.kde.org/graphics/okular/commit/2917e73953a5f71c8a818c552f312a73d06ac406

diff --git a/doc/index.docbook b/doc/index.docbook
index dbb6078d2..5dacec5b3 100644
--- a/doc/index.docbook
+++ b/doc/index.docbook
@@ -2178,8 +2178,12 @@ Context menu actions like Rename Bookmarks etc.)
 						</menuchoice>
 					</term>
 					<listitem>
-						<para>Draw a rectangle around the text for the table, then use the click with the &LMB;
-						  to divide the text block into rows and columns. A &LMB; click on a existing line removes it and merges the adjacent rows or columns.</para>
+						<para>
+							Draw a rectangle around the text for the table,
+							then click with the &LMB; to divide the text block into rows and columns.
+							A &LMB; click on an existing line removes it and merges the adjacent rows or columns.
+							Finally, just click with the &RMB; to copy the table to the clipboard.
+						</para>
 					</listitem>
 				</varlistentry>
 				<varlistentry>
diff --git a/part/pageview.cpp b/part/pageview.cpp
index a12472dd3..5ff49b25f 100644
--- a/part/pageview.cpp
+++ b/part/pageview.cpp
@@ -972,12 +972,93 @@ QString PageViewPrivate::selectedText() const
     return text;
 }
 
+QMimeData *PageView::getTableContents() const
+{
+    QString selText;
+    QString selHtml;
+    QList<double> xs = d->tableSelectionCols;
+    QList<double> ys = d->tableSelectionRows;
+    xs.prepend(0.0);
+    xs.append(1.0);
+    ys.prepend(0.0);
+    ys.append(1.0);
+    selHtml = QString::fromLatin1(
+        "<html><head>"
+        "<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">"
+        "</head><body><table>");
+    for (int r = 0; r + 1 < ys.length(); r++) {
+        selHtml += QLatin1String("<tr>");
+        for (int c = 0; c + 1 < xs.length(); c++) {
+            Okular::NormalizedRect cell(xs[c], ys[r], xs[c + 1], ys[r + 1]);
+            if (c)
+                selText += QLatin1Char('\t');
+            QString txt;
+            for (const TableSelectionPart &tsp : qAsConst(d->tableSelectionParts)) {
+                // first, crop the cell to this part
+                if (!tsp.rectInSelection.intersects(cell))
+                    continue;
+                Okular::NormalizedRect cellPart = tsp.rectInSelection & cell; // intersection
+
+                // second, convert it from table coordinates to part coordinates
+                cellPart.left -= tsp.rectInSelection.left;
+                cellPart.left /= (tsp.rectInSelection.right - tsp.rectInSelection.left);
+                cellPart.right -= tsp.rectInSelection.left;
+                cellPart.right /= (tsp.rectInSelection.right - tsp.rectInSelection.left);
+                cellPart.top -= tsp.rectInSelection.top;
+                cellPart.top /= (tsp.rectInSelection.bottom - tsp.rectInSelection.top);
+                cellPart.bottom -= tsp.rectInSelection.top;
+                cellPart.bottom /= (tsp.rectInSelection.bottom - tsp.rectInSelection.top);
+
+                // third, convert from part coordinates to item coordinates
+                cellPart.left *= (tsp.rectInItem.right - tsp.rectInItem.left);
+                cellPart.left += tsp.rectInItem.left;
+                cellPart.right *= (tsp.rectInItem.right - tsp.rectInItem.left);
+                cellPart.right += tsp.rectInItem.left;
+                cellPart.top *= (tsp.rectInItem.bottom - tsp.rectInItem.top);
+                cellPart.top += tsp.rectInItem.top;
+                cellPart.bottom *= (tsp.rectInItem.bottom - tsp.rectInItem.top);
+                cellPart.bottom += tsp.rectInItem.top;
+
+                // now get the text
+                Okular::RegularAreaRect rects;
+                rects.append(cellPart);
+                txt += tsp.item->page()->text(&rects, Okular::TextPage::CentralPixelTextAreaInclusionBehaviour);
+            }
+            QString html = txt;
+            selText += txt.replace(QLatin1Char('\n'), QLatin1Char(' '));
+            html.replace(QLatin1Char('&'), QLatin1String("&")).replace(QLatin1Char('<'), QLatin1String("<")).replace(QLatin1Char('>'), QLatin1String(">"));
+            // Remove newlines, do not turn them into <br>, because
+            // Excel interprets <br> within cell as new cell...
+            html.replace(QLatin1Char('\n'), QLatin1String(" "));
+            selHtml += QStringLiteral("<td>") + html + QStringLiteral("</td>");
+        }
+        selText += QLatin1Char('\n');
+        selHtml += QLatin1String("</tr>\n");
+    }
+    selHtml += QLatin1String("</table></body></html>\n");
+
+    QMimeData *md = new QMimeData();
+    md->setText(selText);
+    md->setHtml(selHtml);
+
+    return md;
+}
+
 void PageView::copyTextSelection() const
 {
-    const QString text = d->selectedText();
-    if (!text.isEmpty()) {
+    switch (d->mouseMode) {
+    case Okular::Settings::EnumMouseMode::TableSelect: {
         QClipboard *cb = QApplication::clipboard();
-        cb->setText(text, QClipboard::Clipboard);
+        cb->setMimeData(getTableContents(), QClipboard::Clipboard);
+    } break;
+
+    case Okular::Settings::EnumMouseMode::TextSelect: {
+        const QString text = d->selectedText();
+        if (!text.isEmpty()) {
+            QClipboard *cb = QApplication::clipboard();
+            cb->setText(text, QClipboard::Clipboard);
+        }
+    } break;
     }
 }
 
@@ -2344,6 +2425,19 @@ void PageView::mousePressEvent(QMouseEvent *e)
                 updatedRect.translate(-contentAreaPosition());
                 viewport()->update(updatedRect);
             }
+        } else if (rightButton && !d->tableSelectionParts.isEmpty()) {
+            QMenu menu(this);
+            QAction *copyToClipboard = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy Table Contents to Clipboard"));
+            const bool copyAllowed = d->document->isAllowed(Okular::AllowCopy);
+
+            if (!copyAllowed) {
+                copyToClipboard->setEnabled(false);
+                copyToClipboard->setText(i18n("Copy forbidden by DRM"));
+            }
+
+            QAction *choice = menu.exec(e->globalPos());
+            if (choice == copyToClipboard)
+                copyTextSelection();
         }
         break;
     case Okular::Settings::EnumMouseMode::TextSelect:
@@ -2792,78 +2886,12 @@ void PageView::mouseReleaseEvent(QMouseEvent *e)
             break;
         }
 
-        QString selText;
-        QString selHtml;
-        QList<double> xs = d->tableSelectionCols;
-        QList<double> ys = d->tableSelectionRows;
-        xs.prepend(0.0);
-        xs.append(1.0);
-        ys.prepend(0.0);
-        ys.append(1.0);
-        selHtml = QString::fromLatin1(
-            "<html><head>"
-            "<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">"
-            "</head><body><table>");
-        for (int r = 0; r + 1 < ys.length(); r++) {
-            selHtml += QLatin1String("<tr>");
-            for (int c = 0; c + 1 < xs.length(); c++) {
-                Okular::NormalizedRect cell(xs[c], ys[r], xs[c + 1], ys[r + 1]);
-                if (c)
-                    selText += QLatin1Char('\t');
-                QString txt;
-                for (const TableSelectionPart &tsp : qAsConst(d->tableSelectionParts)) {
-                    // first, crop the cell to this part
-                    if (!tsp.rectInSelection.intersects(cell))
-                        continue;
-                    Okular::NormalizedRect cellPart = tsp.rectInSelection & cell; // intersection
-
-                    // second, convert it from table coordinates to part coordinates
-                    cellPart.left -= tsp.rectInSelection.left;
-                    cellPart.left /= (tsp.rectInSelection.right - tsp.rectInSelection.left);
-                    cellPart.right -= tsp.rectInSelection.left;
-                    cellPart.right /= (tsp.rectInSelection.right - tsp.rectInSelection.left);
-                    cellPart.top -= tsp.rectInSelection.top;
-                    cellPart.top /= (tsp.rectInSelection.bottom - tsp.rectInSelection.top);
-                    cellPart.bottom -= tsp.rectInSelection.top;
-                    cellPart.bottom /= (tsp.rectInSelection.bottom - tsp.rectInSelection.top);
-
-                    // third, convert from part coordinates to item coordinates
-                    cellPart.left *= (tsp.rectInItem.right - tsp.rectInItem.left);
-                    cellPart.left += tsp.rectInItem.left;
-                    cellPart.right *= (tsp.rectInItem.right - tsp.rectInItem.left);
-                    cellPart.right += tsp.rectInItem.left;
-                    cellPart.top *= (tsp.rectInItem.bottom - tsp.rectInItem.top);
-                    cellPart.top += tsp.rectInItem.top;
-                    cellPart.bottom *= (tsp.rectInItem.bottom - tsp.rectInItem.top);
-                    cellPart.bottom += tsp.rectInItem.top;
-
-                    // now get the text
-                    Okular::RegularAreaRect rects;
-                    rects.append(cellPart);
-                    txt += tsp.item->page()->text(&rects, Okular::TextPage::CentralPixelTextAreaInclusionBehaviour);
-                }
-                QString html = txt;
-                selText += txt.replace(QLatin1Char('\n'), QLatin1Char(' '));
-                html.replace(QLatin1Char('&'), QLatin1String("&")).replace(QLatin1Char('<'), QLatin1String("<")).replace(QLatin1Char('>'), QLatin1String(">"));
-                // Remove newlines, do not turn them into <br>, because
-                // Excel interprets <br> within cell as new cell...
-                html.replace(QLatin1Char('\n'), QLatin1String(" "));
-                selHtml += QStringLiteral("<td>") + html + QStringLiteral("</td>");
-            }
-            selText += QLatin1Char('\n');
-            selHtml += QLatin1String("</tr>\n");
-        }
-        selHtml += QLatin1String("</table></body></html>\n");
-
         QClipboard *cb = QApplication::clipboard();
-        QMimeData *md = new QMimeData();
-        md->setText(selText);
-        md->setHtml(selHtml);
-        cb->setMimeData(md, QClipboard::Clipboard);
         if (cb->supportsSelection())
-            cb->setMimeData(md, QClipboard::Selection);
+            cb->setMimeData(getTableContents(), QClipboard::Selection);
 
     } break;
+
     case Okular::Settings::EnumMouseMode::TextSelect:
         // if it is a left release checks if is over a previous link press
         if (leftButton && mouseReleaseOverLink(d->mouseOverLinkObject)) {
diff --git a/part/pageview.h b/part/pageview.h
index eee98959a..29faa26a1 100644
--- a/part/pageview.h
+++ b/part/pageview.h
@@ -31,6 +31,7 @@
 #include <QVector>
 
 class QMenu;
+class QMimeData;
 class KActionCollection;
 
 namespace Okular
@@ -177,6 +178,7 @@ private:
     // start / modify / clear selection rectangle
     void selectionStart(const QPoint pos, const QColor &color, bool aboveAll = false);
     void selectionClear(const ClearMode mode = ClearAllSelection);
+    QMimeData *getTableContents() const;
     void drawTableDividers(QPainter *screenPainter);
     void guessTableDividers();
     // update either text or rectangle selection


More information about the kde-doc-english mailing list