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 | |
23 | using namespace KParts; |
24 | |
25 | namespace KParts |
26 | { |
27 | // Internal class, use to store the status of the actions |
28 | class KBitArray |
29 | { |
30 | public: |
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 | |
46 | class NavigationExtensionPrivate |
47 | { |
48 | public: |
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 | |
70 | Q_GLOBAL_STATIC(NavigationExtension::ActionSlotMap, s_actionSlotMap) |
71 | Q_GLOBAL_STATIC(NavigationExtension::ActionNumberMap, s_actionNumberMap) |
72 | |
73 | void 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 | |
95 | NavigationExtension::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 | |
121 | NavigationExtension::~NavigationExtension() |
122 | { |
123 | // qDebug() << "BrowserExtension::~BrowserExtension() " << this; |
124 | } |
125 | |
126 | int NavigationExtension::xOffset() |
127 | { |
128 | return 0; |
129 | } |
130 | |
131 | int NavigationExtension::yOffset() |
132 | { |
133 | return 0; |
134 | } |
135 | |
136 | void 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 | |
142 | void 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 | |
157 | bool NavigationExtension::isURLDropHandlingEnabled() const |
158 | { |
159 | return d->m_urlDropHandlingEnabled; |
160 | } |
161 | |
162 | void NavigationExtension::setURLDropHandlingEnabled(bool enable) |
163 | { |
164 | d->m_urlDropHandlingEnabled = enable; |
165 | } |
166 | |
167 | void 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 | |
207 | void 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 | |
217 | void 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 | |
228 | void 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 | |
240 | bool NavigationExtension::isActionEnabled(const char *name) const |
241 | { |
242 | int actionNumber = (*s_actionNumberMap())[name]; |
243 | return d->m_actionStatus[actionNumber]; |
244 | } |
245 | |
246 | void 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 | |
257 | QString 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 | |
267 | NavigationExtension::ActionSlotMap *NavigationExtension::actionSlotMap() |
268 | { |
269 | if (s_actionSlotMap()->isEmpty()) { |
270 | NavigationExtensionPrivate::createActionSlotMap(); |
271 | } |
272 | return s_actionSlotMap(); |
273 | } |
274 | |
275 | NavigationExtension *NavigationExtension::childObject(QObject *obj) |
276 | { |
277 | return obj->findChild<KParts::NavigationExtension *>(aName: QString(), options: Qt::FindDirectChildrenOnly); |
278 | } |
279 | |
280 | #include "moc_navigationextension.cpp" |
281 | |