File indexing completed on 2024-04-28 04:44:39

0001 // Copyright 2018-2020 Camilo Higuita <milo.h@aol.com>
0002 // Copyright 2018-2020 Nitrux Latinoamericana S.C.
0003 //
0004 // SPDX-License-Identifier: GPL-3.0-or-later
0005 
0006 #include "index.h"
0007 
0008 #if (defined Q_OS_LINUX || defined Q_OS_FREEBSD) && !defined Q_OS_ANDROID
0009 #include <KTerminalLauncherJob>
0010 #include <KWindowConfig>
0011 #endif
0012 
0013 #include <QGuiApplication>
0014 #include <QQuickWindow>
0015 #include <QQmlApplicationEngine>
0016 
0017 #include <QDebug>
0018 #include <QFileInfo>
0019 
0020 #include <QProcess>
0021 
0022 #include <MauiKit3/Core/fmh.h>
0023 #include <MauiKit3/FileBrowsing/fmstatic.h>
0024 
0025 #if (defined Q_OS_LINUX || defined Q_OS_FREEBSD) && !defined Q_OS_ANDROID
0026 #include "indexinterface.h"
0027 #include "indexadaptor.h"
0028 
0029 QVector<QPair<QSharedPointer<OrgKdeIndexActionsInterface>, QStringList>> IndexInstance::appInstances(const QString& preferredService)
0030 {
0031     QVector<QPair<QSharedPointer<OrgKdeIndexActionsInterface>, QStringList>> dolphinInterfaces;
0032 
0033     if (!preferredService.isEmpty())
0034     {
0035         QSharedPointer<OrgKdeIndexActionsInterface> preferredInterface(
0036                     new OrgKdeIndexActionsInterface(preferredService,
0037                                                     QStringLiteral("/Actions"),
0038                                                     QDBusConnection::sessionBus()));
0039 
0040         qDebug() << "IS PREFRFRED INTERFACE VALID?" << preferredInterface->isValid() << preferredInterface->lastError().message();
0041         if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) {
0042             dolphinInterfaces.append(qMakePair(preferredInterface, QStringList()));
0043         }
0044     }
0045 
0046     // Look for dolphin instances among all available dbus services.
0047     QDBusConnectionInterface *sessionInterface = QDBusConnection::sessionBus().interface();
0048     const QStringList dbusServices = sessionInterface ? sessionInterface->registeredServiceNames().value() : QStringList();
0049     // Don't match the service without trailing "-" (unique instance)
0050     const QString pattern = QStringLiteral("org.kde.index-");
0051 
0052     // Don't match the pid without leading "-"
0053     const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid());
0054 
0055     for (const QString& service : dbusServices)
0056     {
0057         if (service.startsWith(pattern) && !service.endsWith(myPid))
0058         {
0059             qDebug() << "EXISTING INTANCES" << service;
0060 
0061             // Check if instance can handle our URLs
0062             QSharedPointer<OrgKdeIndexActionsInterface> interface(
0063                         new OrgKdeIndexActionsInterface(service,
0064                                                         QStringLiteral("/Actions"),
0065                                                         QDBusConnection::sessionBus()));
0066             if (interface->isValid() && !interface->lastError().isValid())
0067             {
0068                 dolphinInterfaces.append(qMakePair(interface, QStringList()));
0069             }
0070         }
0071     }
0072 
0073     return dolphinInterfaces;
0074 }
0075 
0076 bool IndexInstance::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFiles, bool splitView, const QString& preferredService)
0077 {
0078     bool attached = false;
0079 
0080     if (inputUrls.isEmpty())
0081     {
0082         return false;
0083     }
0084 
0085     auto dolphinInterfaces = appInstances(preferredService);
0086     if (dolphinInterfaces.isEmpty())
0087     {
0088         return false;
0089     }
0090 
0091     QStringList newUrls;
0092 
0093     // check to see if any instances already have any of the given URLs open
0094     const auto urls = QUrl::toStringList(inputUrls);
0095     for (const QString& url : urls)
0096     {
0097         bool urlFound = false;
0098 
0099         for (auto& interface: dolphinInterfaces)
0100         {
0101             auto isUrlOpenReply = interface.first->isUrlOpen(url);
0102             isUrlOpenReply.waitForFinished();
0103 
0104             if (!isUrlOpenReply.isError() && isUrlOpenReply.value())
0105             {
0106                 interface.second.append(url);
0107                 urlFound = true;
0108                 break;
0109             }
0110         }
0111 
0112         if (!urlFound)
0113         {
0114             newUrls.append(url);
0115         }
0116     }
0117 
0118     for (const auto& interface: qAsConst(dolphinInterfaces))
0119     {
0120         auto reply = openFiles ? interface.first->openFiles(newUrls, splitView) : interface.first->openDirectories(newUrls, splitView);
0121         reply.waitForFinished();
0122 
0123         if (!reply.isError())
0124         {
0125             interface.first->activateWindow();
0126             attached = true;
0127             break;
0128         }
0129     }
0130 
0131     return attached;
0132 }
0133 
0134 
0135 bool IndexInstance::registerService()
0136 {
0137     QDBusConnectionInterface *iface = QDBusConnection::sessionBus().interface();
0138 
0139     auto registration = iface->registerService(QStringLiteral("org.kde.index-%1").arg(QCoreApplication::applicationPid()),
0140                                                QDBusConnectionInterface::ReplaceExistingService,
0141                                                QDBusConnectionInterface::DontAllowReplacement);
0142 
0143     if (!registration.isValid())
0144     {
0145         qWarning("2 Failed to register D-Bus service \"%s\" on session bus: \"%s\"",
0146                  qPrintable("org.kde.index"),
0147                  qPrintable(registration.error().message()));
0148         return false;
0149     }
0150 
0151     return true;
0152 }
0153 
0154 #endif
0155 
0156 Index::Index(QObject *parent)
0157     : QObject(parent)
0158 {
0159 #if (defined Q_OS_LINUX || defined Q_OS_FREEBSD) && !defined Q_OS_ANDROID
0160     new ActionsAdaptor(this);
0161     if(!QDBusConnection::sessionBus().registerObject(QStringLiteral("/Actions"), this))
0162     {
0163         qDebug() << "FAILED TO REGISTER BACKGROUND DBUS OBJECT";
0164         return;
0165     }
0166 #endif
0167 }
0168 
0169 void Index::openDirectories(const QStringList &dirs, bool splitView)
0170 {
0171     openPaths(dirs);
0172 }
0173 
0174 void Index::openFiles(const QStringList &files, bool splitView)
0175 {
0176     openPaths(files);
0177 }
0178 
0179 void Index::activateWindow()
0180 {
0181     if(m_qmlObject)
0182     {
0183         auto window = qobject_cast<QQuickWindow *>(m_qmlObject);
0184         if (window)
0185         {
0186             qDebug() << "Trying to raise wndow";
0187             window->raise();
0188             window->requestActivate();
0189         }
0190     }
0191 }
0192 
0193 bool Index::isUrlOpen(const QString &url)
0194 {
0195     bool value = false;
0196 
0197     QMetaObject::invokeMethod(m_qmlObject, "isUrlOpen",
0198                               Q_RETURN_ARG(bool, value),
0199                               Q_ARG(QString, url));
0200 
0201     return value;
0202 }
0203 
0204 void Index::pasteIntoFolder()
0205 {
0206 
0207 }
0208 
0209 void Index::changeUrl(const QUrl &url)
0210 {
0211 
0212 }
0213 
0214 void Index::slotTerminalDirectoryChanged(const QUrl &url)
0215 {
0216 
0217 }
0218 
0219 void Index::quit()
0220 {
0221     QCoreApplication::quit();
0222 }
0223 
0224 void Index::openNewTab(const QUrl &url)
0225 {
0226 
0227 }
0228 
0229 void Index::openNewTabAndActivate(const QUrl &url)
0230 {
0231 
0232 }
0233 
0234 void Index::openNewWindow(const QUrl &url)
0235 {
0236     QProcess process;
0237     process.setProgram("index");
0238     process.setArguments({"-n", url.toString()});
0239     process.startDetached();
0240 }
0241 
0242 /* to be called to launch index with opening different paths */
0243 void Index::openPaths(const QStringList &paths)
0244 {
0245     QStringList urls = std::accumulate(paths.constBegin(), paths.constEnd(), QStringList(), [](QStringList &list, const QString &path) -> QStringList {
0246             const auto url = QUrl::fromUserInput(path);
0247             if (url.isLocalFile())
0248     {
0249             if (FMStatic::isDir(url))
0250     {
0251             list << url.toString();
0252 }
0253             else
0254     {
0255             list <<  FMStatic::fileDir(url).toString();
0256 }
0257 }
0258 
0259             return list;
0260 });
0261 
0262     if(m_qmlObject)
0263         QMetaObject::invokeMethod(m_qmlObject, "openDirs",
0264                                   Q_ARG(QVariant, urls));
0265 }
0266 
0267 void Index::setQmlObject(QObject *object)
0268 {
0269     m_qmlObject = object;
0270 }
0271 
0272 void Index::openTerminal(const QUrl &url)
0273 {
0274 #if (defined Q_OS_LINUX || defined Q_OS_FREEBSD) && !defined Q_OS_ANDROID
0275 
0276     auto job = new KTerminalLauncherJob(QString());
0277     job->setWorkingDirectory(url.toLocalFile());
0278     job->start();
0279 
0280 #else
0281     Q_UNUSED(url)
0282 #endif
0283 }
0284 
0285 QVariantList Index::quickPaths()
0286 {
0287     FMH::MODEL_LIST paths;
0288 
0289     paths << FMH::MODEL {{FMH::MODEL_KEY::PATH, "overview:///"}, {FMH::MODEL_KEY::ICON, "folder-recent"}, {FMH::MODEL_KEY::LABEL, "Overview"}, {FMH::MODEL_KEY::TYPE, "Quick"}};
0290 
0291     paths << FMH::MODEL {{FMH::MODEL_KEY::PATH, FMStatic::PATHTYPE_URI[FMStatic::PATHTYPE_KEY::TAGS_PATH] + "fav"}, {FMH::MODEL_KEY::ICON, "love"}, {FMH::MODEL_KEY::LABEL, "Favorite"}, {FMH::MODEL_KEY::TYPE, "Quick"}};
0292 
0293 paths << FMH::MODEL {{FMH::MODEL_KEY::PATH, "tags:///"}, {FMH::MODEL_KEY::ICON, "tag"}, {FMH::MODEL_KEY::LABEL, "Tags"}, {FMH::MODEL_KEY::TYPE, "Quick"}};
0294 
0295 paths << FMStatic::getDefaultPaths();
0296 
0297 
0298 return FMH::toMapList(paths);
0299 }
0300 
0301 QUrl Index::cameraPath()
0302 {
0303     const static auto paths = QStringList{FMStatic::HomePath + "/DCIM/Camera", FMStatic::HomePath + "/Camera"};
0304 
0305     for (const auto &path : paths) {
0306         if (FMH::fileExists(path))
0307             return QUrl(path);
0308     }
0309 
0310     return QUrl();
0311 }
0312 
0313 QUrl Index::screenshotsPath()
0314 {
0315     const static auto paths = QStringList{FMStatic::HomePath + "/DCIM/Screenshots", FMStatic::HomePath + "/Screenshots"};
0316 
0317     for (const auto &path : paths) {
0318         if (FMH::fileExists(path))
0319             return QUrl(path);
0320     }
0321 
0322     return QUrl();
0323 }