Akonadi Contacts

textbrowser.cpp
1 /*
2  SPDX-FileCopyrightText: 2012-2021 Laurent Montel <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 
6 */
7 
8 #include "textbrowser_p.h"
9 #include <KCodecs>
10 #include <KLocalizedString>
11 #include <KStandardAction>
12 #include <QAction>
13 #include <QUrl>
14 
15 #include <QApplication>
16 #include <QClipboard>
17 #include <QContextMenuEvent>
18 #include <QMenu>
19 #include <QRegularExpression>
20 #include <QTextBlock>
21 
22 using namespace Akonadi;
23 
24 TextBrowser::TextBrowser(QWidget *parent)
25  : QTextBrowser(parent)
26 {
27  setOpenLinks(false);
28 }
29 
30 void TextBrowser::slotCopyData()
31 {
32 #ifndef QT_NO_CLIPBOARD
34  // put the data into the mouse selection and the clipboard
35  if (mDataToCopy.type() == QVariant::Pixmap) {
36  clip->setPixmap(mDataToCopy.value<QPixmap>(), QClipboard::Clipboard);
37  clip->setPixmap(mDataToCopy.value<QPixmap>(), QClipboard::Selection);
38  } else {
39  clip->setText(mDataToCopy.toString(), QClipboard::Clipboard);
40  clip->setText(mDataToCopy.toString(), QClipboard::Selection);
41  }
42 #endif
43 }
44 
45 #ifndef QT_NO_CONTEXTMENU
46 void TextBrowser::contextMenuEvent(QContextMenuEvent *event)
47 {
48 #ifndef QT_NO_CLIPBOARD
49  QMenu popup;
50 
51  QAction *act = KStandardAction::copy(this, &TextBrowser::copy, this);
52  act->setEnabled(!textCursor().selectedText().isEmpty());
53  act->setShortcut(QKeySequence());
54  popup.addAction(act);
55 
56  // Create a new action to correspond with what is under the click
57  act = new QAction(i18nc("@action:inmenu Copy the text of a general item", "Copy Item"), this);
58 
59  mDataToCopy.clear(); // nothing found to copy yet
60 
61  QString link = anchorAt(event->pos());
62  if (!link.isEmpty()) {
63  if (link.startsWith(QLatin1String("mailto:"))) {
64  mDataToCopy = KCodecs::decodeRFC2047String(QUrl(link).path());
65  // Action text matches that used in KMail
66  act->setText(i18nc("@action:inmenu Copy a displayed email address", "Copy Email Address"));
67  } else {
68  // A link, but it could be one of our internal ones. There is
69  // no point in copying these. Internal links are always in the
70  // form "protocol:?argument", whereas valid external links should
71  // be in the form starting with "protocol://".
72  if (!link.contains(QRegularExpression(QStringLiteral("^\\w+:\\?")))) {
73  mDataToCopy = link;
74  // Action text matches that used in Konqueror
75  act->setText(i18nc("@action:inmenu Copy a link URL", "Copy Link URL"));
76  }
77  }
78  }
79 
80  if (!mDataToCopy.isValid()) { // no link was found above
81  QTextCursor curs = cursorForPosition(event->pos());
82  QString text = curs.block().text(); // try the text under cursor
83 
84  if (!text.isEmpty()) {
85  // curs().block().text() over an image (contact photo or QR code)
86  // returns a string starting with the character 0xFFFC (Unicode
87  // object replacement character). See the documentation for
88  // QTextImageFormat.
89  if (text.startsWith(QChar(0xFFFC))) {
90  QTextCharFormat charFormat = curs.charFormat();
91  if (charFormat.isImageFormat()) {
92  const QTextImageFormat imageFormat = charFormat.toImageFormat();
93  const QString imageName = imageFormat.name();
94  const QVariant imageResource = document()->resource(QTextDocument::ImageResource, QUrl(imageName));
95 
96  const auto pix = imageResource.value<QPixmap>();
97  if (!pix.isNull()) {
98  // There may be other images (e.g. contact type icons) that
99  // there is no point in copying.
100  if (imageName == QLatin1String("contact_photo")) {
101  mDataToCopy = pix;
102  act->setText(i18nc("@action:inmenu Copy a contact photo", "Copy Photo"));
103  } else if (imageName == QLatin1String("qrcode")) {
104  mDataToCopy = pix;
105  act->setText(i18nc("@action:inmenu Copy a QR code image", "Copy Code"));
106  }
107  }
108  }
109  } else {
110  // Added by our formatter (but not I18N'ed) for a mobile
111  // telephone number. See
112  // kdepim/kaddressbook/grantlee/grantleecontactformatter.cpp and
113  // kdepimlibs/akonadi/contact/standardcontactformatter.cpp
114  text.remove(QRegularExpression(QStringLiteral("\\s*\\(SMS\\)$")));
115 
116  // For an item which was formatted with line breaks (as <br>
117  // in HTML), the returned text contains the character 0x2028
118  // (Unicode line separator). Convert any of these back to newlines.
119  text.replace(QChar(0x2028), QLatin1Char('\n'));
120 
121  mDataToCopy = text;
122  }
123  }
124  }
125 
126  if (mDataToCopy.isValid()) {
127  connect(act, &QAction::triggered, this, &TextBrowser::slotCopyData);
128  } else {
129  act->setEnabled(false);
130  }
131 
132  popup.addAction(act);
133  popup.exec(event->globalPos());
134 #endif
135 }
136 
137 #endif
void setText(const QString &text)
void triggered(bool checked)
QTextCharFormat charFormat() const const
KCODECS_EXPORT QString decodeRFC2047String(const QString &text)
T value() const const
const QPoint & globalPos() const const
QAction * addAction(const QString &text)
void setPixmap(const QPixmap &pixmap, QClipboard::Mode mode)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
bool isEmpty() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
QAction * exec()
bool isImageFormat() const const
QTextBlock block() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
void setShortcut(const QKeySequence &shortcut)
QString text() const const
const QPoint & pos() const const
KIOCORE_EXPORT CopyJob * link(const QUrl &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
void setText(const QString &text, QClipboard::Mode mode)
QTextImageFormat toImageFormat() const const
QClipboard * clipboard()
void setEnabled(bool)
QString name() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Fri Jun 18 2021 23:08:56 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.