File indexing completed on 2024-05-12 03:57:42

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
0004     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "navigationextension.h"
0010 
0011 #include "kparts_logging.h"
0012 
0013 #include <KLocalizedString>
0014 #include <KMessageBox>
0015 #include <KUriFilter>
0016 
0017 #include <QApplication>
0018 #include <QClipboard>
0019 #include <QMap>
0020 #include <QRegularExpression>
0021 #include <QTimer>
0022 
0023 using namespace KParts;
0024 
0025 namespace KParts
0026 {
0027 // Internal class, use to store the status of the actions
0028 class KBitArray
0029 {
0030 public:
0031     int val = 0;
0032     bool operator[](int index)
0033     {
0034         return (val & (1 << index)) ? true : false;
0035     }
0036     void setBit(int index, bool value)
0037     {
0038         if (value) {
0039             val = val | (1 << index);
0040         } else {
0041             val = val & ~(1 << index);
0042         }
0043     }
0044 };
0045 
0046 class NavigationExtensionPrivate
0047 {
0048 public:
0049     NavigationExtensionPrivate(KParts::ReadOnlyPart *parent)
0050         : m_urlDropHandlingEnabled(false)
0051         , m_part(parent)
0052     {
0053     }
0054 
0055     struct DelayedRequest {
0056         QUrl m_delayedURL;
0057         KParts::OpenUrlArguments m_delayedArgs;
0058     };
0059 
0060     QList<DelayedRequest> m_requests;
0061     bool m_urlDropHandlingEnabled;
0062     KBitArray m_actionStatus;
0063     QMap<int, QString> m_actionText;
0064 
0065     static void createActionSlotMap();
0066 
0067     KParts::ReadOnlyPart *m_part;
0068 };
0069 
0070 Q_GLOBAL_STATIC(NavigationExtension::ActionSlotMap, s_actionSlotMap)
0071 Q_GLOBAL_STATIC(NavigationExtension::ActionNumberMap, s_actionNumberMap)
0072 
0073 void NavigationExtensionPrivate::createActionSlotMap()
0074 {
0075     s_actionSlotMap()->insert("cut", SLOT(cut()));
0076     s_actionSlotMap()->insert("copy", SLOT(copy()));
0077     s_actionSlotMap()->insert("paste", SLOT(paste()));
0078     s_actionSlotMap()->insert("print", SLOT(print()));
0079     // Tricky. Those aren't actions in fact, but simply methods that a browserextension
0080     // can have or not. No need to return them here.
0081     // s_actionSlotMap()->insert( "reparseConfiguration", SLOT(reparseConfiguration()) );
0082     // s_actionSlotMap()->insert( "refreshMimeTypes", SLOT(refreshMimeTypes()) );
0083 
0084     // Create the action-number map
0085     NavigationExtension::ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin();
0086     NavigationExtension::ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd();
0087     for (int i = 0; it != itEnd; ++it, ++i) {
0088         // qDebug() << " action " << it.key() << " number " << i;
0089         s_actionNumberMap()->insert(it.key(), i);
0090     }
0091 }
0092 
0093 }
0094 
0095 NavigationExtension::NavigationExtension(KParts::ReadOnlyPart *parent)
0096     : QObject(parent)
0097     , d(new NavigationExtensionPrivate(parent))
0098 {
0099     if (s_actionSlotMap()->isEmpty())
0100     // Create the action-slot map
0101     {
0102         NavigationExtensionPrivate::createActionSlotMap();
0103     }
0104 
0105     // Set the initial status of the actions depending on whether
0106     // they're supported or not
0107     const QMetaObject *metaobj = metaObject();
0108     ActionSlotMap::ConstIterator it = s_actionSlotMap()->constBegin();
0109     ActionSlotMap::ConstIterator itEnd = s_actionSlotMap()->constEnd();
0110     for (int i = 0; it != itEnd; ++it, ++i) {
0111         // Does the extension have a slot with the name of this action ?
0112         QByteArray slotSig = it.key() + "()";
0113         d->m_actionStatus.setBit(i, metaobj->indexOfMethod(slotSig.constData()) != -1);
0114     }
0115 
0116     connect(this, &NavigationExtension::openUrlRequest, this, &NavigationExtension::slotOpenUrlRequest);
0117     connect(this, &NavigationExtension::enableAction, this, &NavigationExtension::slotEnableAction);
0118     connect(this, &NavigationExtension::setActionText, this, &NavigationExtension::slotSetActionText);
0119 }
0120 
0121 NavigationExtension::~NavigationExtension()
0122 {
0123     // qDebug() << "BrowserExtension::~BrowserExtension() " << this;
0124 }
0125 
0126 int NavigationExtension::xOffset()
0127 {
0128     return 0;
0129 }
0130 
0131 int NavigationExtension::yOffset()
0132 {
0133     return 0;
0134 }
0135 
0136 void NavigationExtension::saveState(QDataStream &stream)
0137 {
0138     // TODO add d->m_part->mimeType()
0139     stream << d->m_part->url() << static_cast<qint32>(xOffset()) << static_cast<qint32>(yOffset());
0140 }
0141 
0142 void NavigationExtension::restoreState(QDataStream &stream)
0143 {
0144     QUrl u;
0145     qint32 xOfs;
0146     qint32 yOfs;
0147     stream >> u >> xOfs >> yOfs;
0148 
0149     OpenUrlArguments args;
0150     args.setXOffset(xOfs);
0151     args.setYOffset(yOfs);
0152     // TODO add args.setMimeType
0153     d->m_part->setArguments(args);
0154     d->m_part->openUrl(u);
0155 }
0156 
0157 bool NavigationExtension::isURLDropHandlingEnabled() const
0158 {
0159     return d->m_urlDropHandlingEnabled;
0160 }
0161 
0162 void NavigationExtension::setURLDropHandlingEnabled(bool enable)
0163 {
0164     d->m_urlDropHandlingEnabled = enable;
0165 }
0166 
0167 void NavigationExtension::pasteRequest()
0168 {
0169     QString plain(QStringLiteral("plain"));
0170     QString url = QApplication::clipboard()->text(plain, QClipboard::Selection).trimmed();
0171     // Remove linefeeds and any whitespace surrounding it.
0172     url.remove(QRegularExpression(QStringLiteral("[\\ ]*\\n+[\\ ]*")));
0173 
0174     // Check if it's a URL
0175     QStringList filters = KUriFilter::self()->pluginNames();
0176     filters.removeAll(QStringLiteral("kuriikwsfilter"));
0177     filters.removeAll(QStringLiteral("localdomainurifilter"));
0178     KUriFilterData filterData;
0179     filterData.setData(url);
0180     filterData.setCheckForExecutables(false);
0181     if (KUriFilter::self()->filterUri(filterData, filters)) {
0182         switch (filterData.uriType()) {
0183         case KUriFilterData::LocalFile:
0184         case KUriFilterData::LocalDir:
0185         case KUriFilterData::NetProtocol:
0186             slotOpenUrlRequest(filterData.uri());
0187             break;
0188         case KUriFilterData::Error:
0189             KMessageBox::error(d->m_part->widget(), filterData.errorMsg());
0190             break;
0191         default:
0192             break;
0193         }
0194     } else if (KUriFilter::self()->filterUri(filterData, QStringList(QStringLiteral("kuriikwsfilter"))) && url.length() < 250) {
0195         if (KMessageBox::questionTwoActions(d->m_part->widget(),
0196                                             i18n("<qt>Do you want to search the Internet for <b>%1</b>?</qt>", url.toHtmlEscaped()),
0197                                             i18n("Internet Search"),
0198                                             KGuiItem(i18n("&Search"), QStringLiteral("edit-find")),
0199                                             KStandardGuiItem::cancel(),
0200                                             QStringLiteral("MiddleClickSearch"))
0201             == KMessageBox::PrimaryAction) {
0202             slotOpenUrlRequest(filterData.uri());
0203         }
0204     }
0205 }
0206 
0207 void NavigationExtension::slotOpenUrlRequest(const QUrl &url, const KParts::OpenUrlArguments &args)
0208 {
0209     // qDebug() << this << " BrowserExtension::slotOpenURLRequest(): url=" << url.url();
0210     NavigationExtensionPrivate::DelayedRequest req;
0211     req.m_delayedURL = url;
0212     req.m_delayedArgs = args;
0213     d->m_requests.append(req);
0214     QTimer::singleShot(0, this, &NavigationExtension::slotEmitOpenUrlRequestDelayed);
0215 }
0216 
0217 void NavigationExtension::slotEmitOpenUrlRequestDelayed()
0218 {
0219     if (d->m_requests.isEmpty()) {
0220         return;
0221     }
0222     NavigationExtensionPrivate::DelayedRequest req = d->m_requests.front();
0223     d->m_requests.pop_front();
0224     Q_EMIT openUrlRequestDelayed(req.m_delayedURL, req.m_delayedArgs);
0225     // tricky: do not do anything here! (no access to member variables, etc.)
0226 }
0227 
0228 void NavigationExtension::slotEnableAction(const char *name, bool enabled)
0229 {
0230     // qDebug() << "BrowserExtension::slotEnableAction " << name << " " << enabled;
0231     ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(name);
0232     if (it != s_actionNumberMap()->constEnd()) {
0233         d->m_actionStatus.setBit(it.value(), enabled);
0234         // qDebug() << "BrowserExtension::slotEnableAction setting bit " << it.data() << " to " << enabled;
0235     } else {
0236         qCWarning(KPARTSLOG) << "BrowserExtension::slotEnableAction unknown action " << name;
0237     }
0238 }
0239 
0240 bool NavigationExtension::isActionEnabled(const char *name) const
0241 {
0242     int actionNumber = (*s_actionNumberMap())[name];
0243     return d->m_actionStatus[actionNumber];
0244 }
0245 
0246 void NavigationExtension::slotSetActionText(const char *name, const QString &text)
0247 {
0248     // qDebug() << "BrowserExtension::slotSetActionText " << name << " " << text;
0249     ActionNumberMap::ConstIterator it = s_actionNumberMap()->constFind(name);
0250     if (it != s_actionNumberMap()->constEnd()) {
0251         d->m_actionText[it.value()] = text;
0252     } else {
0253         qCWarning(KPARTSLOG) << "BrowserExtension::slotSetActionText unknown action " << name;
0254     }
0255 }
0256 
0257 QString NavigationExtension::actionText(const char *name) const
0258 {
0259     int actionNumber = (*s_actionNumberMap())[name];
0260     QMap<int, QString>::ConstIterator it = d->m_actionText.constFind(actionNumber);
0261     if (it != d->m_actionText.constEnd()) {
0262         return *it;
0263     }
0264     return QString();
0265 }
0266 
0267 NavigationExtension::ActionSlotMap *NavigationExtension::actionSlotMap()
0268 {
0269     if (s_actionSlotMap()->isEmpty()) {
0270         NavigationExtensionPrivate::createActionSlotMap();
0271     }
0272     return s_actionSlotMap();
0273 }
0274 
0275 NavigationExtension *NavigationExtension::childObject(QObject *obj)
0276 {
0277     return obj->findChild<KParts::NavigationExtension *>(QString(), Qt::FindDirectChildrenOnly);
0278 }
0279 
0280 #include "moc_navigationextension.cpp"