1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
4 SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "navigationextension.h"
10
11#include "kparts_logging.h"
12
13#include <KLocalizedString>
14#include <KMessageBox>
15#include <KUriFilter>
16
17#include <QApplication>
18#include <QClipboard>
19#include <QMap>
20#include <QRegularExpression>
21#include <QTimer>
22
23using namespace KParts;
24
25namespace KParts
26{
27// Internal class, use to store the status of the actions
28class KBitArray
29{
30public:
31 int val = 0;
32 bool operator[](int index)
33 {
34 return (val & (1 << index)) ? true : false;
35 }
36 void setBit(int index, bool value)
37 {
38 if (value) {
39 val = val | (1 << index);
40 } else {
41 val = val & ~(1 << index);
42 }
43 }
44};
45
46class NavigationExtensionPrivate
47{
48public:
49 NavigationExtensionPrivate(KParts::ReadOnlyPart *parent)
50 : m_urlDropHandlingEnabled(false)
51 , m_part(parent)
52 {
53 }
54
55 struct DelayedRequest {
56 QUrl m_delayedURL;
57 KParts::OpenUrlArguments m_delayedArgs;
58 };
59
60 QList<DelayedRequest> m_requests;
61 bool m_urlDropHandlingEnabled;
62 KBitArray m_actionStatus;
63 QMap<int, QString> m_actionText;
64
65 static void createActionSlotMap();
66
67 KParts::ReadOnlyPart *m_part;
68};
69
70Q_GLOBAL_STATIC(NavigationExtension::ActionSlotMap, s_actionSlotMap)
71Q_GLOBAL_STATIC(NavigationExtension::ActionNumberMap, s_actionNumberMap)
72
73void NavigationExtensionPrivate::createActionSlotMap()
74{
75 s_actionSlotMap()->insert(key: "cut", SLOT(cut()));
76 s_actionSlotMap()->insert(key: "copy", SLOT(copy()));
77 s_actionSlotMap()->insert(key: "paste", SLOT(paste()));
78 s_actionSlotMap()->insert(key: "print", SLOT(print()));
79 // Tricky. Those aren't actions in fact, but simply methods that a browserextension
80 // can have or not. No need to return them here.
81 // s_actionSlotMap()->insert( "reparseConfiguration", SLOT(reparseConfiguration()) );
82 // s_actionSlotMap()->insert( "refreshMimeTypes", SLOT(refreshMimeTypes()) );
83
84 // Create the action-number map
85 NavigationExtension::ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin();
86 NavigationExtension::ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd();
87 for (int i = 0; it != itEnd; ++it, ++i) {
88 // qDebug() << " action " << it.key() << " number " << i;
89 s_actionNumberMap()->insert(key: it.key(), value: i);
90 }
91}
92
93}
94
95NavigationExtension::NavigationExtension(KParts::ReadOnlyPart *parent)
96 : QObject(parent)
97 , d(new NavigationExtensionPrivate(parent))
98{
99 if (s_actionSlotMap()->isEmpty())
100 // Create the action-slot map
101 {
102 NavigationExtensionPrivate::createActionSlotMap();
103 }
104
105 // Set the initial status of the actions depending on whether
106 // they're supported or not
107 const QMetaObject *metaobj = metaObject();
108 ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin();
109 ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd();
110 for (int i = 0; it != itEnd; ++it, ++i) {
111 // Does the extension have a slot with the name of this action ?
112 QByteArray slotSig = it.key() + "()";
113 d->m_actionStatus.setBit(index: i, value: metaobj->indexOfMethod(method: slotSig.constData()) != -1);
114 }
115
116 connect(sender: this, signal: &NavigationExtension::openUrlRequest, context: this, slot: &NavigationExtension::slotOpenUrlRequest);
117 connect(sender: this, signal: &NavigationExtension::enableAction, context: this, slot: &NavigationExtension::slotEnableAction);
118 connect(sender: this, signal: &NavigationExtension::setActionText, context: this, slot: &NavigationExtension::slotSetActionText);
119}
120
121NavigationExtension::~NavigationExtension()
122{
123 // qDebug() << "BrowserExtension::~BrowserExtension() " << this;
124}
125
126int NavigationExtension::xOffset()
127{
128 return 0;
129}
130
131int NavigationExtension::yOffset()
132{
133 return 0;
134}
135
136void NavigationExtension::saveState(QDataStream &stream)
137{
138 // TODO add d->m_part->mimeType()
139 stream << d->m_part->url() << static_cast<qint32>(xOffset()) << static_cast<qint32>(yOffset());
140}
141
142void NavigationExtension::restoreState(QDataStream &stream)
143{
144 QUrl u;
145 qint32 xOfs;
146 qint32 yOfs;
147 stream >> u >> xOfs >> yOfs;
148
149 OpenUrlArguments args;
150 args.setXOffset(xOfs);
151 args.setYOffset(yOfs);
152 // TODO add args.setMimeType
153 d->m_part->setArguments(args);
154 d->m_part->openUrl(url: u);
155}
156
157bool NavigationExtension::isURLDropHandlingEnabled() const
158{
159 return d->m_urlDropHandlingEnabled;
160}
161
162void NavigationExtension::setURLDropHandlingEnabled(bool enable)
163{
164 d->m_urlDropHandlingEnabled = enable;
165}
166
167void NavigationExtension::pasteRequest()
168{
169 QString plain(QStringLiteral("plain"));
170 QString url = QApplication::clipboard()->text(subtype&: plain, mode: QClipboard::Selection).trimmed();
171 // Remove linefeeds and any whitespace surrounding it.
172 url.remove(re: QRegularExpression(QStringLiteral("[\\ ]*\\n+[\\ ]*")));
173
174 // Check if it's a URL
175 QStringList filters = KUriFilter::self()->pluginNames();
176 filters.removeAll(QStringLiteral("kuriikwsfilter"));
177 filters.removeAll(QStringLiteral("localdomainurifilter"));
178 KUriFilterData filterData;
179 filterData.setData(url);
180 filterData.setCheckForExecutables(false);
181 if (KUriFilter::self()->filterUri(data&: filterData, filters)) {
182 switch (filterData.uriType()) {
183 case KUriFilterData::LocalFile:
184 case KUriFilterData::LocalDir:
185 case KUriFilterData::NetProtocol:
186 slotOpenUrlRequest(url: filterData.uri());
187 break;
188 case KUriFilterData::Error:
189 KMessageBox::error(parent: d->m_part->widget(), text: filterData.errorMsg());
190 break;
191 default:
192 break;
193 }
194 } else if (KUriFilter::self()->filterUri(data&: filterData, filters: QStringList(QStringLiteral("kuriikwsfilter"))) && url.length() < 250) {
195 if (KMessageBox::questionTwoActions(parent: d->m_part->widget(),
196 i18n("<qt>Do you want to search the Internet for <b>%1</b>?</qt>", url.toHtmlEscaped()),
197 i18n("Internet Search"),
198 primaryAction: KGuiItem(i18n("&Search"), QStringLiteral("edit-find")),
199 secondaryAction: KStandardGuiItem::cancel(),
200 QStringLiteral("MiddleClickSearch"))
201 == KMessageBox::PrimaryAction) {
202 slotOpenUrlRequest(url: filterData.uri());
203 }
204 }
205}
206
207void NavigationExtension::slotOpenUrlRequest(const QUrl &url, const KParts::OpenUrlArguments &args)
208{
209 // qDebug() << this << " BrowserExtension::slotOpenURLRequest(): url=" << url.url();
210 NavigationExtensionPrivate::DelayedRequest req;
211 req.m_delayedURL = url;
212 req.m_delayedArgs = args;
213 d->m_requests.append(t: req);
214 QTimer::singleShot(interval: 0, receiver: this, slot: &NavigationExtension::slotEmitOpenUrlRequestDelayed);
215}
216
217void NavigationExtension::slotEmitOpenUrlRequestDelayed()
218{
219 if (d->m_requests.isEmpty()) {
220 return;
221 }
222 NavigationExtensionPrivate::DelayedRequest req = d->m_requests.front();
223 d->m_requests.pop_front();
224 Q_EMIT openUrlRequestDelayed(url: req.m_delayedURL, arguments: req.m_delayedArgs);
225 // tricky: do not do anything here! (no access to member variables, etc.)
226}
227
228void NavigationExtension::slotEnableAction(const char *name, bool enabled)
229{
230 // qDebug() << "BrowserExtension::slotEnableAction " << name << " " << enabled;
231 ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(key: name);
232 if (it != s_actionNumberMap()->constEnd()) {
233 d->m_actionStatus.setBit(index: it.value(), value: enabled);
234 // qDebug() << "BrowserExtension::slotEnableAction setting bit " << it.data() << " to " << enabled;
235 } else {
236 qCWarning(KPARTSLOG) << "BrowserExtension::slotEnableAction unknown action " << name;
237 }
238}
239
240bool NavigationExtension::isActionEnabled(const char *name) const
241{
242 int actionNumber = (*s_actionNumberMap())[name];
243 return d->m_actionStatus[actionNumber];
244}
245
246void NavigationExtension::slotSetActionText(const char *name, const QString &text)
247{
248 // qDebug() << "BrowserExtension::slotSetActionText " << name << " " << text;
249 ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(key: name);
250 if (it != s_actionNumberMap()->constEnd()) {
251 d->m_actionText[it.value()] = text;
252 } else {
253 qCWarning(KPARTSLOG) << "BrowserExtension::slotSetActionText unknown action " << name;
254 }
255}
256
257QString NavigationExtension::actionText(const char *name) const
258{
259 int actionNumber = (*s_actionNumberMap())[name];
260 QMap<int, QString>::ConstIterator it = d->m_actionText.constFind(key: actionNumber);
261 if (it != d->m_actionText.constEnd()) {
262 return *it;
263 }
264 return QString();
265}
266
267NavigationExtension::ActionSlotMap *NavigationExtension::actionSlotMap()
268{
269 if (s_actionSlotMap()->isEmpty()) {
270 NavigationExtensionPrivate::createActionSlotMap();
271 }
272 return s_actionSlotMap();
273}
274
275NavigationExtension *NavigationExtension::childObject(QObject *obj)
276{
277 return obj->findChild<KParts::NavigationExtension *>(aName: QString(), options: Qt::FindDirectChildrenOnly);
278}
279
280#include "moc_navigationextension.cpp"
281

source code of kparts/src/navigationextension.cpp